Skip to content

Commit 4b853af

Browse files
authoredJul 13, 2018
Allowing 101 listeners (#256)
1 parent 9552c32 commit 4b853af

File tree

5 files changed

+448
-74
lines changed

5 files changed

+448
-74
lines changed
 

‎src/index.js

+89-42
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
'use strict';
1818

19+
const assert = require('assert');
1920
const bun = require('bun');
2021
const extend = require('extend');
2122
const is = require('is');
@@ -64,6 +65,11 @@ const FieldValue = require('./field-value').FieldValue;
6465
*/
6566
const Timestamp = require('./timestamp');
6667

68+
/*!
69+
* @see ClientPool
70+
*/
71+
const ClientPool = require('./pool').ClientPool;
72+
6773
/*!
6874
* @see CollectionReference
6975
*/
@@ -100,12 +106,12 @@ let Transaction;
100106
/*!
101107
* @see v1beta1
102108
*/
103-
let v1beta1; // Lazy-loaded in `_ensureClient()`
109+
let v1beta1; // Lazy-loaded in `_runRequest()`
104110

105111
/*!
106112
* @see @google-cloud/common
107113
*/
108-
let common; // Lazy-loaded in `_ensureClient()`
114+
let common; // Lazy-loaded in `_runRequest()`
109115

110116
/*!
111117
* HTTP header for the resource prefix to improve routing and project isolation
@@ -120,6 +126,16 @@ const CLOUD_RESOURCE_HEADER = 'google-cloud-resource-prefix';
120126
*/
121127
const MAX_REQUEST_RETRIES = 5;
122128

129+
/*!
130+
* The maximum number of concurrent requests supported by a single GRPC channel,
131+
* as enforced by Google's Frontend. If the SDK issues more than 100 concurrent
132+
* operations, we need to use more than one GAPIC client since these clients
133+
* multiplex all requests over a single channel.
134+
*
135+
* @type {number}
136+
*/
137+
const MAX_CONCURRENT_REQUESTS_PER_CLIENT = 100;
138+
123139
/*!
124140
* GRPC Error code for 'UNAVAILABLE'.
125141
* @type {number}
@@ -233,11 +249,12 @@ class Firestore {
233249
});
234250

235251
/**
252+
* A client pool to distribute requests over multiple GAPIC clients in order
253+
* to work around a connection limit of 100 concurrent requests per client.
236254
* @private
237-
* @type {object|null}
238-
* @property {FirestoreClient} Firestore The Firestore GAPIC client.
255+
* @type {ClientPool|null}
239256
*/
240-
this._firestoreClient = null;
257+
this._clientPool = null;
241258

242259
/**
243260
* The configuration options for the GAPIC client.
@@ -725,47 +742,78 @@ follow these steps, YOUR APP MAY BREAK.`);
725742
}
726743

727744
/**
728-
* Initializes the client and detects the Firestore Project ID. Returns a
729-
* Promise on completion. If the client is already initialized, the returned
730-
* Promise resolves immediately.
745+
* Executes a new request using the first available GAPIC client.
731746
*
732747
* @private
733748
*/
734-
_ensureClient() {
749+
_runRequest(op) {
750+
// Initialize the client pool if this is the first request.
735751
if (!this._clientInitialized) {
736752
common = require('@google-cloud/common');
753+
this._clientInitialized = this._initClientPool().then(clientPool => {
754+
this._clientPool = clientPool;
755+
})
756+
}
737757

738-
this._clientInitialized = new Promise((resolve, reject) => {
739-
this._firestoreClient =
740-
module.exports.v1beta1(this._initalizationOptions);
758+
return this._clientInitialized.then(() => this._clientPool.run(op));
759+
}
741760

742-
Firestore.log('Firestore', null, 'Initialized Firestore GAPIC Client');
761+
/**
762+
* Initializes the client pool and invokes Project ID detection. Returns a
763+
* Promise on completion.
764+
*
765+
* @private
766+
* @return {Promise<ClientPool>}
767+
*/
768+
_initClientPool() {
769+
assert(!this._clientInitialized, 'Client pool already initialized');
743770

744-
// We schedule Project ID detection using `setImmediate` to allow the
745-
// testing framework to provide its own implementation of
746-
// `getProjectId`.
747-
setImmediate(() => {
748-
this._firestoreClient.getProjectId((err, projectId) => {
749-
if (err) {
750-
Firestore.log(
751-
'Firestore._ensureClient', null,
752-
'Failed to detect project ID: %s', err);
753-
reject(err);
754-
} else {
755-
Firestore.log(
756-
'Firestore._ensureClient', null, 'Detected project ID: %s',
757-
projectId);
758-
this._referencePath =
759-
new ResourcePath(projectId, this._referencePath.databaseId);
760-
resolve();
761-
}
762-
});
771+
const clientPool =
772+
new ClientPool(MAX_CONCURRENT_REQUESTS_PER_CLIENT, () => {
773+
const gapicClient =
774+
module.exports.v1beta1(this._initalizationOptions);
775+
Firestore.log(
776+
'Firestore', null, 'Initialized Firestore GAPIC Client');
777+
return gapicClient;
763778
});
764-
});
779+
780+
const projectIdProvided = this._referencePath.projectId !== '{{projectId}}';
781+
782+
if (projectIdProvided) {
783+
return Promise.resolve(clientPool);
784+
} else {
785+
return clientPool.run(client => this._detectProjectId(client))
786+
.then(projectId => {
787+
this._referencePath =
788+
new ResourcePath(projectId, this._referencePath.databaseId);
789+
return clientPool;
790+
});
765791
}
766-
return this._clientInitialized;
767792
}
768793

794+
/**
795+
* Auto-detects the Firestore Project ID.
796+
*
797+
* @private
798+
* @param {object} gapicClient - The Firestore GAPIC client.
799+
* @return {Promise<string>} A Promise that resolves with the Project ID.
800+
*/
801+
_detectProjectId(gapicClient) {
802+
return new Promise(
803+
(resolve, reject) => {gapicClient.getProjectId((err, projectId) => {
804+
if (err) {
805+
Firestore.log(
806+
'Firestore._detectProjectId', null,
807+
'Failed to detect project ID: %s', err);
808+
reject(err);
809+
} else {
810+
Firestore.log(
811+
'Firestore._detectProjectId', null, 'Detected project ID: %s',
812+
projectId);
813+
resolve(projectId);
814+
}
815+
})});
816+
}
769817
/**
770818
* Decorate the request options of an API request. This is used to replace
771819
* any `{{projectId}}` placeholders with the value detected from the user's
@@ -801,7 +849,7 @@ follow these steps, YOUR APP MAY BREAK.`);
801849
*
802850
* @private
803851
* @param {number} attemptsRemaining - The number of available attempts.
804-
* @param {string} requestTag A unique client-assigned identifier for this
852+
* @param {string} requestTag - A unique client-assigned identifier for this
805853
* request.
806854
* @param {retryFunction} func - Method returning a Promise than can be
807855
* retried.
@@ -972,14 +1020,14 @@ follow these steps, YOUR APP MAY BREAK.`);
9721020
request(methodName, request, requestTag, allowRetries) {
9731021
let attempts = allowRetries ? MAX_REQUEST_RETRIES : 1;
9741022

975-
return this._ensureClient().then(() => {
1023+
return this._runRequest(gapicClient => {
9761024
const decorated = this._decorateRequest(request);
9771025
return this._retry(attempts, requestTag, () => {
9781026
return new Promise((resolve, reject) => {
9791027
Firestore.log(
9801028
'Firestore.request', requestTag, 'Sending request: %j',
9811029
decorated.request);
982-
this._firestoreClient[methodName](
1030+
gapicClient[methodName](
9831031
decorated.request, decorated.gax, (err, result) => {
9841032
if (err) {
9851033
Firestore.log(
@@ -1017,15 +1065,15 @@ follow these steps, YOUR APP MAY BREAK.`);
10171065
readStream(methodName, request, requestTag, allowRetries) {
10181066
let attempts = allowRetries ? MAX_REQUEST_RETRIES : 1;
10191067

1020-
return this._ensureClient().then(() => {
1068+
return this._runRequest(gapicClient => {
10211069
const decorated = this._decorateRequest(request);
10221070
return this._retry(attempts, requestTag, () => {
10231071
return new Promise((resolve, reject) => {
10241072
try {
10251073
Firestore.log(
10261074
'Firestore.readStream', requestTag,
10271075
'Sending request: %j', decorated.request);
1028-
let stream = this._firestoreClient[methodName](
1076+
let stream = gapicClient[methodName](
10291077
decorated.request, decorated.gax);
10301078
let logger = through.obj(function(chunk, enc, callback) {
10311079
Firestore.log(
@@ -1069,16 +1117,15 @@ follow these steps, YOUR APP MAY BREAK.`);
10691117
let self = this;
10701118
let attempts = allowRetries ? MAX_REQUEST_RETRIES : 1;
10711119

1072-
return this._ensureClient().then(() => {
1120+
return this._runRequest(gapicClient => {
10731121
const decorated = this._decorateRequest(request);
10741122
return this._retry(attempts, requestTag, () => {
10751123
return Promise.resolve().then(() => {
10761124
Firestore.log(
10771125
'Firestore.readWriteStream', requestTag, 'Opening stream');
10781126
// The generated bi-directional streaming API takes the list of GAX
10791127
// headers as its second argument.
1080-
let requestStream =
1081-
this._firestoreClient[methodName]({}, decorated.gax);
1128+
let requestStream = gapicClient[methodName]({}, decorated.gax);
10821129

10831130
// The transform stream to assign the project ID.
10841131
let transform = through.obj(function(chunk, encoding, callback) {

‎src/pool.ts

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*!
2+
* Copyright 2018 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
'use strict';
18+
19+
import assert from 'assert';
20+
21+
/**
22+
* An auto-resizing pool that distributes concurrent operations over multiple
23+
* clients of type `T`.
24+
*
25+
* ClientPool is used within Firestore to manage a pool of GAPIC clients and
26+
* automatically initializes multiple clients if we issue more than 100
27+
* concurrent operations.
28+
*/
29+
export class ClientPool<T> {
30+
/** Stores each active clients and how many operations it has outstanding. */
31+
private activeClients: Map<T, number> = new Map();
32+
33+
/**
34+
* @param {number} concurrentOperationLimit - The number of operations that
35+
* each client can handle.
36+
* @param {() => T} clientFactory - A factory function called as needed when
37+
* new clients are required.
38+
*/
39+
constructor(
40+
private readonly concurrentOperationLimit: number,
41+
private readonly clientFactory: () => T) {}
42+
43+
/**
44+
* Returns an already existing client if it has less than the maximum number
45+
* of concurrent operations or initializes and returns a new client.
46+
*/
47+
private acquire(): T {
48+
let selectedClient: T|null = null;
49+
let selectedRequestCount = 0;
50+
51+
this.activeClients.forEach((requestCount, client) => {
52+
if (!selectedClient && requestCount < this.concurrentOperationLimit) {
53+
selectedClient = client;
54+
selectedRequestCount = requestCount;
55+
}
56+
});
57+
58+
if (!selectedClient) {
59+
selectedClient = this.clientFactory();
60+
assert(
61+
!this.activeClients.has(selectedClient),
62+
'The provided client factory returned an existing instance');
63+
}
64+
65+
this.activeClients.set(selectedClient, selectedRequestCount + 1);
66+
67+
return selectedClient!;
68+
}
69+
70+
/**
71+
* Reduces the number of operations for the provided client, potentially
72+
* removing it from the pool of active clients.
73+
*/
74+
private release(client: T): void {
75+
let requestCount = this.activeClients.get(client) || 0;
76+
assert(requestCount > 0, 'No active request');
77+
78+
requestCount = requestCount! - 1;
79+
this.activeClients.set(client, requestCount);
80+
81+
if (requestCount === 0) {
82+
this.garbageCollect();
83+
}
84+
}
85+
86+
/**
87+
* The number of currently registered clients.
88+
*
89+
* @return {number} Number of currently registered clients.
90+
*/
91+
// Visible for testing.
92+
get size(): number {
93+
return this.activeClients.size;
94+
}
95+
96+
/**
97+
* Runs the provided operation in this pool. This function may create an
98+
* additional client if all existing clients already operate at the concurrent
99+
* operation limit.
100+
*
101+
* @param {(client: T) => Promise<V>} op - A callback function that returns a
102+
* Promise. The client T will be returned to the pool when callback finishes.
103+
* @return {Promise<V>} A Promise that resolves with the result of `op`.
104+
*/
105+
run<V>(op: (client: T) => Promise<V>): Promise<V> {
106+
const client = this.acquire();
107+
108+
return op(client)
109+
.catch(err => {
110+
this.release(client);
111+
return Promise.reject(err);
112+
})
113+
.then(res => {
114+
this.release(client);
115+
return res;
116+
});
117+
}
118+
119+
/**
120+
* Deletes clients that are no longer executing operations. Keeps up to one
121+
* idle client to reduce future initialization costs.
122+
*/
123+
private garbageCollect(): void {
124+
let idleClients = 0;
125+
this.activeClients.forEach((requestCount, client) => {
126+
if (requestCount === 0) {
127+
++idleClients;
128+
129+
if (idleClients > 1) {
130+
this.activeClients.delete(client);
131+
}
132+
}
133+
});
134+
}
135+
}

‎test/index.js

+42-27
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,22 @@ describe('instantiation', function() {
314314
assert(firestore instanceof Firestore);
315315
});
316316

317+
it('uses project id from constructor', () => {
318+
let firestore = new Firestore({
319+
projectId: PROJECT_ID,
320+
sslCreds: grpc.credentials.createInsecure(),
321+
timestampsInSnapshots: true,
322+
keyFilename: './test/fake-certificate.json',
323+
});
324+
325+
return firestore._runRequest(() => {
326+
assert.equal(
327+
firestore.formattedName,
328+
`projects/${PROJECT_ID}/databases/(default)`);
329+
return Promise.resolve();
330+
});
331+
});
332+
317333
it('detects project id', function() {
318334
let firestore = new Firestore({
319335
sslCreds: grpc.credentials.createInsecure(),
@@ -324,48 +340,47 @@ describe('instantiation', function() {
324340
assert.equal(
325341
firestore.formattedName, 'projects/{{projectId}}/databases/(default)');
326342

327-
let initialized = firestore._ensureClient();
343+
firestore._detectProjectId = () => Promise.resolve(PROJECT_ID);
328344

329-
let projectIdDetected = false;
345+
return firestore._runRequest(() => {
346+
assert.equal(
347+
firestore.formattedName,
348+
`projects/${PROJECT_ID}/databases/(default)`);
349+
return Promise.resolve();
350+
});
351+
});
330352

331-
firestore._firestoreClient.getProjectId = function(callback) {
332-
projectIdDetected = true;
333-
callback(null, PROJECT_ID);
334-
};
353+
it('uses project id from gapic client', function() {
354+
let firestore = new Firestore({
355+
sslCreds: grpc.credentials.createInsecure(),
356+
timestampsInSnapshots: true,
357+
keyFilename: './test/fake-certificate.json',
358+
});
335359

336-
firestore._firestoreClient._innerApiCalls.batchGetDocuments = function(
337-
request) {
338-
let expectedRequest = {
339-
database: DATABASE_ROOT,
340-
documents: [`${DATABASE_ROOT}/documents/collectionId/documentId`],
341-
};
342-
assert.deepEqual(request, expectedRequest);
343-
return stream(found('documentId'));
344-
};
360+
assert.equal(
361+
firestore.formattedName, 'projects/{{projectId}}/databases/(default)');
345362

346-
return initialized.then(() => {
347-
assert.equal(projectIdDetected, true);
348-
return firestore.doc('collectionId/documentId').get();
363+
let gapicClient = {getProjectId: callback => callback(null, PROJECT_ID)};
364+
365+
return firestore._detectProjectId(gapicClient).then(projectId => {
366+
assert.equal(projectId, PROJECT_ID);
349367
});
350368
});
351369

352-
it('handles error from project ID detection', function() {
370+
it('handles error from project ID detection', () => {
353371
let firestore = new Firestore({
354372
sslCreds: grpc.credentials.createInsecure(),
355373
timestampsInSnapshots: true,
356374
keyFilename: './test/fake-certificate.json',
357375
});
358376

359-
let initialized = firestore._ensureClient();
360-
361-
firestore._firestoreClient.getProjectId = function(callback) {
362-
callback(new Error('Project ID error'));
377+
let gapicClient = {
378+
getProjectId: callback => callback(new Error('Injected Error'))
363379
};
364380

365-
return initialized.then(
366-
() => assert.fail('Expected error missing'), err => {
367-
assert.equal(err.message, 'Project ID error');
368-
});
381+
return firestore._detectProjectId(gapicClient)
382+
.then(() => assert.fail('Error ignored'))
383+
.catch(err => assert.equal('Injected Error', err.message))
369384
});
370385

371386
it('exports all types', function() {

‎test/pool.ts

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/**
2+
* Copyright 2018 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
'use strict';
18+
19+
import {use, expect} from 'chai';
20+
import * as chaiAsPromised from 'chai-as-promised';
21+
22+
import {ClientPool} from '../src/pool';
23+
import {Deferred} from './util/helpers';
24+
25+
use(chaiAsPromised.default);
26+
27+
function deferredPromises(count: number): Array<Deferred<void>> {
28+
const deferred: Array<Deferred<void>> = [];
29+
for (let i = 0; i < count; ++i) {
30+
deferred.push(new Deferred<void>());
31+
}
32+
return deferred;
33+
}
34+
35+
describe('Client pool', () => {
36+
it('creates new instances as needed', () => {
37+
const clientPool = new ClientPool<{}>(3, () => {
38+
return {};
39+
});
40+
41+
expect(clientPool.size).to.eq(0);
42+
43+
const operationPromises = deferredPromises(4);
44+
45+
clientPool.run(() => operationPromises[0].promise);
46+
expect(clientPool.size).to.eq(1);
47+
clientPool.run(() => operationPromises[1].promise);
48+
expect(clientPool.size).to.eq(1);
49+
clientPool.run(() => operationPromises[2].promise);
50+
expect(clientPool.size).to.eq(1);
51+
52+
clientPool.run(() => operationPromises[3].promise);
53+
expect(clientPool.size).to.eq(2);
54+
});
55+
56+
it('re-uses idle instances', () => {
57+
const clientPool = new ClientPool<{}>(2, () => {
58+
return {};
59+
});
60+
61+
expect(clientPool.size).to.eq(0);
62+
63+
const operationPromises = deferredPromises(5);
64+
65+
const completionPromise =
66+
clientPool.run(() => operationPromises[0].promise);
67+
expect(clientPool.size).to.eq(1);
68+
clientPool.run(() => operationPromises[1].promise);
69+
expect(clientPool.size).to.eq(1);
70+
clientPool.run(() => operationPromises[2].promise);
71+
expect(clientPool.size).to.eq(2);
72+
clientPool.run(() => operationPromises[3].promise);
73+
expect(clientPool.size).to.eq(2);
74+
75+
operationPromises[0].resolve();
76+
77+
return completionPromise.then(() => {
78+
clientPool.run(() => operationPromises[4].promise);
79+
expect(clientPool.size).to.eq(2);
80+
});
81+
});
82+
83+
it('garbage collects after success', () => {
84+
const clientPool = new ClientPool<{}>(2, () => {
85+
return {};
86+
});
87+
88+
expect(clientPool.size).to.eq(0);
89+
90+
const operationPromises = deferredPromises(4);
91+
const completionPromises: Array<Promise<void>> = [];
92+
93+
completionPromises.push(clientPool.run(() => operationPromises[0].promise));
94+
expect(clientPool.size).to.eq(1);
95+
completionPromises.push(clientPool.run(() => operationPromises[1].promise));
96+
expect(clientPool.size).to.eq(1);
97+
completionPromises.push(clientPool.run(() => operationPromises[2].promise));
98+
expect(clientPool.size).to.eq(2);
99+
completionPromises.push(clientPool.run(() => operationPromises[3].promise));
100+
expect(clientPool.size).to.eq(2);
101+
102+
operationPromises.forEach(deferred => deferred.resolve());
103+
104+
return Promise.all(completionPromises).then(() => {
105+
expect(clientPool.size).to.eq(1);
106+
});
107+
});
108+
109+
it('garbage collects after error', () => {
110+
const clientPool = new ClientPool<{}>(2, () => {
111+
return {};
112+
});
113+
114+
expect(clientPool.size).to.eq(0);
115+
116+
const operationPromises = deferredPromises(4);
117+
const completionPromises: Array<Promise<void>> = [];
118+
119+
completionPromises.push(clientPool.run(() => operationPromises[0].promise));
120+
expect(clientPool.size).to.eq(1);
121+
completionPromises.push(clientPool.run(() => operationPromises[1].promise));
122+
expect(clientPool.size).to.eq(1);
123+
completionPromises.push(clientPool.run(() => operationPromises[2].promise));
124+
expect(clientPool.size).to.eq(2);
125+
completionPromises.push(clientPool.run(() => operationPromises[3].promise));
126+
expect(clientPool.size).to.eq(2);
127+
128+
operationPromises.forEach(deferred => deferred.reject());
129+
130+
return Promise.all(completionPromises.map(p => p.catch(() => {})))
131+
.then(() => {
132+
expect(clientPool.size).to.eq(1);
133+
});
134+
});
135+
136+
it('forwards success', () => {
137+
const clientPool = new ClientPool<{}>(1, () => {
138+
return {};
139+
});
140+
141+
const op = clientPool.run(() => Promise.resolve('Success'));
142+
return expect(op).to.become('Success');
143+
});
144+
145+
it('forwards failure', () => {
146+
const clientPool = new ClientPool<{}>(1, () => {
147+
return {};
148+
});
149+
150+
const op = clientPool.run(() => Promise.reject('Generated error'));
151+
return expect(op).to.eventually.be.rejectedWith('Generated error');
152+
});
153+
});

‎test/util/helpers.ts

+29-5
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,33 @@
1919
import {GrpcClient} from 'google-gax';
2020

2121
import Firestore from '../../src';
22+
import {ClientPool} from '../../src/pool';
23+
import v1beta1 from '../../src/v1beta1';
2224

2325
/* tslint:disable:no-any */
26+
type GapicClient = any;
2427
const grpc = new GrpcClient({} as any).grpc;
2528
const SSL_CREDENTIALS = (grpc.credentials as any).createInsecure();
2629
/* tslint:enable:no-any */
2730

2831
const PROJECT_ID = 'test-project';
2932

33+
/** A Promise implementation that supports deferred resolution. */
34+
export class Deferred<R> {
35+
promise: Promise<R>;
36+
resolve: (value?: R|Promise<R>) => void = () => {};
37+
reject: (reason?: Error) => void = () => {};
38+
39+
constructor() {
40+
this.promise = new Promise(
41+
(resolve: (value?: R|Promise<R>) => void,
42+
reject: (reason?: Error) => void) => {
43+
this.resolve = resolve;
44+
this.reject = reject;
45+
});
46+
}
47+
}
48+
3049
/**
3150
* Interface that defines the request handlers used by Firestore.
3251
*/
@@ -47,7 +66,8 @@ export interface ApiOverride {
4766
* @param {ApiOverride} apiOverrides An object with the request handlers to
4867
* override.
4968
* @param {Object} firestoreSettings Firestore Settings to configure the client.
50-
* @return {Firestore} A new Firestore client.
69+
* @return {Promise<Firestore>} A Promise that resolves with the new Firestore
70+
* client.
5171
*/
5272
export function createInstance(
5373
apiOverrides?: ApiOverride, firestoreSettings?: {}): Promise<Firestore> {
@@ -62,13 +82,17 @@ export function createInstance(
6282

6383
const firestore = new Firestore(initializationOptions);
6484

65-
return firestore._ensureClient().then(() => {
85+
const clientPool = new ClientPool(/* concurrentRequestLimit= */ 1, () => {
86+
const gapicClient: GapicClient = v1beta1(initializationOptions);
6687
if (apiOverrides) {
6788
Object.keys(apiOverrides).forEach(override => {
68-
firestore._firestoreClient._innerApiCalls[override] =
69-
apiOverrides[override];
89+
gapicClient._innerApiCalls[override] = apiOverrides[override];
7090
});
7191
}
72-
return firestore;
92+
return gapicClient;
7393
});
94+
95+
firestore._initClientPool = () => Promise.resolve(clientPool);
96+
97+
return Promise.resolve(firestore);
7498
}

0 commit comments

Comments
 (0)
Please sign in to comment.