Skip to content

Commit a5e59b9

Browse files
WIP Tree-Shakeable Datastore
1 parent 9c409ea commit a5e59b9

File tree

14 files changed

+2509
-11440
lines changed

14 files changed

+2509
-11440
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { Firestore } from './database';
19+
import { DatabaseInfo } from '../../../src/core/database_info';
20+
import {
21+
FirestoreClient,
22+
PersistenceSettings
23+
} from '../../../src/core/firestore_client';
24+
import { Code, FirestoreError } from '../../../src/util/error';
25+
import {
26+
ComponentProvider,
27+
MemoryComponentProvider
28+
} from '../../../src/core/component_provider';
29+
30+
const firestoreClientInstances = new Map<Firestore, Promise<FirestoreClient>>();
31+
32+
// settings() defaults:
33+
export const DEFAULT_HOST = 'firestore.googleapis.com';
34+
export const DEFAULT_SSL = true;
35+
36+
export function getFirestoreClient(
37+
firestore: Firestore
38+
): Promise<FirestoreClient> {
39+
if (firestore._terminated) {
40+
throw new FirestoreError(
41+
Code.FAILED_PRECONDITION,
42+
'The client has already been terminated.'
43+
);
44+
}
45+
46+
if (!firestoreClientInstances.has(firestore)) {
47+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
48+
initializeFirestoreClient(firestore, new MemoryComponentProvider(), {
49+
durable: false
50+
});
51+
}
52+
return firestoreClientInstances.get(firestore)!;
53+
}
54+
55+
export function initializeFirestoreClient(
56+
firestore: Firestore,
57+
componentProvider: ComponentProvider,
58+
persistenceSettings: PersistenceSettings
59+
): Promise<void> {
60+
if (firestore._terminated || hasFirestoreClient(firestore)) {
61+
throw new FirestoreError(
62+
Code.FAILED_PRECONDITION,
63+
'Firestore has already been started and persistence can no longer ' +
64+
'be enabled. You can only enable persistence before calling ' +
65+
'any other methods on a Firestore object.'
66+
);
67+
}
68+
69+
const settings = firestore._getSettings();
70+
const databaseInfo = new DatabaseInfo(
71+
firestore._databaseId,
72+
/* persistenceKey= */ firestore._persistenceKey,
73+
settings.host ?? DEFAULT_HOST,
74+
settings.ssl ?? DEFAULT_SSL,
75+
/** forceLongPolling= */ false
76+
);
77+
const firestoreClient = new FirestoreClient(
78+
firestore._credentials,
79+
firestore._queue
80+
);
81+
const initializationPromise = firestoreClient.start(
82+
databaseInfo,
83+
componentProvider,
84+
persistenceSettings
85+
);
86+
firestoreClientInstances.set(
87+
firestore,
88+
initializationPromise.then(() => firestoreClient)
89+
);
90+
return initializationPromise;
91+
}
92+
93+
export function hasFirestoreClient(firestore: Firestore): boolean {
94+
return firestoreClientInstances.has(firestore);
95+
}
96+
97+
export async function removeFirestoreClient(
98+
firestore: Firestore
99+
): Promise<void> {
100+
const firestoreClient = await firestoreClientInstances.get(firestore);
101+
if (firestoreClient) {
102+
firestoreClientInstances.delete(firestore);
103+
return firestoreClient.terminate();
104+
}
105+
}

packages/firestore/exp/src/api/database.ts

Lines changed: 45 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -18,67 +18,53 @@
1818
import * as firestore from '../../index';
1919

2020
import { _getProvider, _removeServiceInstance } from '@firebase/app-exp';
21-
import { FirebaseApp, _FirebaseService } from '@firebase/app-types-exp';
21+
import { _FirebaseService, FirebaseApp } from '@firebase/app-types-exp';
2222
import { Provider } from '@firebase/component';
2323

2424
import { FirebaseAuthInternalName } from '@firebase/auth-interop-types';
25-
import {
26-
FirestoreClient,
27-
PersistenceSettings
28-
} from '../../../src/core/firestore_client';
2925
import { AsyncQueue } from '../../../src/util/async_queue';
3026
import {
31-
ComponentProvider,
3227
IndexedDbComponentProvider,
33-
MemoryComponentProvider,
3428
MultiTabIndexedDbComponentProvider
3529
} from '../../../src/core/component_provider';
3630

37-
import {
38-
DEFAULT_FORCE_LONG_POLLING,
39-
DEFAULT_HOST,
40-
DEFAULT_SSL,
41-
Firestore as LiteFirestore
42-
} from '../../../lite/src/api/database';
31+
import { Firestore as LiteFirestore } from '../../../lite/src/api/database';
4332
import { cast } from '../../../lite/src/api/util';
4433
import { Code, FirestoreError } from '../../../src/util/error';
4534
import { Deferred } from '../../../src/util/promise';
4635
import { LruParams } from '../../../src/local/lru_garbage_collector';
4736
import { CACHE_SIZE_UNLIMITED } from '../../../src/api/database';
48-
import { DatabaseId, DatabaseInfo } from '../../../src/core/database_info';
37+
import { DatabaseId } from '../../../src/core/database_info';
4938
import {
50-
indexedDbStoragePrefix,
51-
indexedDbClearPersistence
39+
indexedDbClearPersistence,
40+
indexedDbStoragePrefix
5241
} from '../../../src/local/indexeddb_persistence';
42+
import {
43+
getFirestoreClient,
44+
hasFirestoreClient,
45+
initializeFirestoreClient,
46+
removeFirestoreClient
47+
} from './components';
5348

5449
/**
5550
* The root reference to the Firestore database and the entry point for the
5651
* tree-shakeable SDK.
5752
*/
5853
export class Firestore extends LiteFirestore
5954
implements firestore.FirebaseFirestore, _FirebaseService {
60-
private readonly _queue = new AsyncQueue();
61-
private readonly _firestoreClient: FirestoreClient;
62-
private readonly _persistenceKey: string;
63-
private _componentProvider: ComponentProvider = new MemoryComponentProvider();
55+
readonly _queue = new AsyncQueue();
56+
readonly _persistenceKey: string;
6457

65-
// Assigned via _getFirestoreClient()
66-
private _deferredInitialization?: Promise<void>;
67-
68-
protected _persistenceSettings: PersistenceSettings = { durable: false };
6958
// We override the Settings property of the Lite SDK since the full Firestore
7059
// SDK supports more settings.
7160
protected _settings?: firestore.Settings;
7261

73-
private _terminated: boolean = false;
74-
7562
constructor(
7663
app: FirebaseApp,
7764
authProvider: Provider<FirebaseAuthInternalName>
7865
) {
7966
super(app, authProvider);
8067
this._persistenceKey = app.name;
81-
this._firestoreClient = new FirestoreClient(this._credentials, this._queue);
8268
}
8369

8470
_getSettings(): firestore.Settings {
@@ -88,65 +74,6 @@ export class Firestore extends LiteFirestore
8874
return this._settings;
8975
}
9076

91-
_getFirestoreClient(): Promise<FirestoreClient> {
92-
if (this._terminated) {
93-
throw new FirestoreError(
94-
Code.FAILED_PRECONDITION,
95-
'The client has already been terminated.'
96-
);
97-
}
98-
99-
if (!this._deferredInitialization) {
100-
const settings = this._getSettings();
101-
const databaseInfo = this._makeDatabaseInfo(
102-
settings.host,
103-
settings.ssl,
104-
settings.experimentalForceLongPolling
105-
);
106-
107-
this._deferredInitialization = this._firestoreClient.start(
108-
databaseInfo,
109-
this._componentProvider,
110-
this._persistenceSettings
111-
);
112-
}
113-
114-
return this._deferredInitialization.then(() => this._firestoreClient);
115-
}
116-
117-
// TODO(firestorexp): Factor out MultiTabComponentProvider and remove
118-
// `synchronizeTabs` argument
119-
_enablePersistence(
120-
persistenceProvider: ComponentProvider,
121-
synchronizeTabs: boolean
122-
): Promise<void> {
123-
if (this._deferredInitialization) {
124-
throw new FirestoreError(
125-
Code.FAILED_PRECONDITION,
126-
'Firestore has already been started and persistence can no longer ' +
127-
'be enabled. You can only enable persistence before calling ' +
128-
'any other methods on a Firestore object.'
129-
);
130-
}
131-
132-
const settings = this._getSettings();
133-
this._persistenceSettings = {
134-
durable: true,
135-
synchronizeTabs,
136-
forceOwningTab: false,
137-
cacheSizeBytes:
138-
settings.cacheSizeBytes ?? LruParams.DEFAULT_CACHE_SIZE_BYTES
139-
};
140-
this._componentProvider = persistenceProvider;
141-
142-
// TODO(firestorexp): Add support for Persistence fallback
143-
return this._getFirestoreClient().then(() => {});
144-
}
145-
146-
delete(): Promise<void> {
147-
return terminate(this);
148-
}
149-
15077
/**
15178
* Verifies that the client is not running and clears persistence by invoking
15279
* `delegate` on the async queue.
@@ -157,7 +84,7 @@ export class Firestore extends LiteFirestore
15784
_clearPersistence(
15885
delegate: (databaseId: DatabaseId, persistenceKey: string) => Promise<void>
15986
): Promise<void> {
160-
if (this._deferredInitialization !== undefined && !this._terminated) {
87+
if (hasFirestoreClient(this)) {
16188
throw new FirestoreError(
16289
Code.FAILED_PRECONDITION,
16390
'Persistence can only be cleared before a Firestore instance is ' +
@@ -177,28 +104,9 @@ export class Firestore extends LiteFirestore
177104
return deferred.promise;
178105
}
179106

180-
protected _makeDatabaseInfo(
181-
host?: string,
182-
ssl?: boolean,
183-
forceLongPolling?: boolean
184-
): DatabaseInfo {
185-
return new DatabaseInfo(
186-
this._databaseId,
187-
this._persistenceKey,
188-
host ?? DEFAULT_HOST,
189-
ssl ?? DEFAULT_SSL,
190-
forceLongPolling ?? DEFAULT_FORCE_LONG_POLLING
191-
);
192-
}
193-
194-
_terminate(): Promise<void> {
195-
this._terminated = true;
196-
if (this._deferredInitialization) {
197-
return this._deferredInitialization.then(() =>
198-
this._firestoreClient.terminate()
199-
);
200-
}
201-
return Promise.resolve();
107+
async _terminate(): Promise<void> {
108+
await super._terminate();
109+
await removeFirestoreClient(this);
202110
}
203111
}
204112

@@ -234,20 +142,33 @@ export function enableIndexedDbPersistence(
234142
firestore: firestore.FirebaseFirestore
235143
): Promise<void> {
236144
const firestoreImpl = cast(firestore, Firestore);
237-
return firestoreImpl._enablePersistence(
145+
const settings = firestoreImpl._getSettings();
146+
return initializeFirestoreClient(
147+
firestoreImpl,
238148
new IndexedDbComponentProvider(),
239-
/*synchronizeTabs=*/ false
149+
{
150+
durable: true,
151+
cacheSizeBytes:
152+
settings.cacheSizeBytes || LruParams.DEFAULT_CACHE_SIZE_BYTES
153+
}
240154
);
241155
}
242156

243157
export function enableMultiTabIndexedDbPersistence(
244158
firestore: firestore.FirebaseFirestore
245159
): Promise<void> {
246160
const firestoreImpl = cast(firestore, Firestore);
247-
return firestoreImpl._enablePersistence(
161+
const settings = firestoreImpl._getSettings();
162+
return initializeFirestoreClient(
163+
firestoreImpl,
248164
new MultiTabIndexedDbComponentProvider(),
249-
/*synchronizeTabs=*/ false
250-
);
165+
{
166+
durable: true,
167+
synchronizeTabs: true,
168+
cacheSizeBytes:
169+
settings.cacheSizeBytes || LruParams.DEFAULT_CACHE_SIZE_BYTES
170+
}
171+
).then(() => {});
251172
}
252173

253174
export function clearIndexedDbPersistence(
@@ -265,33 +186,33 @@ export function waitForPendingWrites(
265186
firestore: firestore.FirebaseFirestore
266187
): Promise<void> {
267188
const firestoreImpl = cast(firestore, Firestore);
268-
return firestoreImpl
269-
._getFirestoreClient()
270-
.then(firestoreClient => firestoreClient.waitForPendingWrites());
189+
return getFirestoreClient(firestoreImpl).then(firestoreClient =>
190+
firestoreClient.waitForPendingWrites()
191+
);
271192
}
272193

273194
export function enableNetwork(
274195
firestore: firestore.FirebaseFirestore
275196
): Promise<void> {
276197
const firestoreImpl = cast(firestore, Firestore);
277-
return firestoreImpl
278-
._getFirestoreClient()
279-
.then(firestoreClient => firestoreClient.enableNetwork());
198+
return getFirestoreClient(firestoreImpl).then(firestoreClient =>
199+
firestoreClient.enableNetwork()
200+
);
280201
}
281202

282203
export function disableNetwork(
283204
firestore: firestore.FirebaseFirestore
284205
): Promise<void> {
285206
const firestoreImpl = cast(firestore, Firestore);
286-
return firestoreImpl
287-
._getFirestoreClient()
288-
.then(firestoreClient => firestoreClient.disableNetwork());
207+
return getFirestoreClient(firestoreImpl).then(firestoreClient =>
208+
firestoreClient.disableNetwork()
209+
);
289210
}
290211

291212
export function terminate(
292213
firestore: firestore.FirebaseFirestore
293214
): Promise<void> {
294215
_removeServiceInstance(firestore.app, 'firestore/lite');
295216
const firestoreImpl = cast(firestore, Firestore);
296-
return firestoreImpl._terminate();
217+
return firestoreImpl.delete();
297218
}

0 commit comments

Comments
 (0)