Skip to content

Commit 8a8e60a

Browse files
LocalStore-only queries (#3508)
1 parent ef348fe commit 8a8e60a

File tree

11 files changed

+918
-603
lines changed

11 files changed

+918
-603
lines changed

packages/firestore/exp/dependencies.json

+315-417
Large diffs are not rendered by default.

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

+137-95
Original file line numberDiff line numberDiff line change
@@ -16,138 +16,180 @@
1616
*/
1717

1818
import { Firestore } from './database';
19-
import { DatabaseInfo } from '../../../src/core/database_info';
20-
import {
21-
FirestoreClient,
22-
PersistenceSettings
23-
} from '../../../src/core/firestore_client';
19+
import { PersistenceSettings } from '../../../src/core/firestore_client';
2420
import { Code, FirestoreError } from '../../../src/util/error';
2521
import {
2622
MemoryOfflineComponentProvider,
2723
OfflineComponentProvider,
2824
OnlineComponentProvider
2925
} from '../../../src/core/component_provider';
30-
import { DEFAULT_HOST, DEFAULT_SSL } from '../../../lite/src/api/components';
26+
import { LocalStore } from '../../../src/local/local_store';
27+
import { Deferred } from '../../../src/util/promise';
3128
import { logDebug } from '../../../src/util/log';
32-
29+
import { SyncEngine } from '../../../src/core/sync_engine';
30+
import { RemoteStore } from '../../../src/remote/remote_store';
31+
import { Persistence } from '../../../src/local/persistence';
32+
import { EventManager } from '../../../src/core/event_manager';
3333
export const LOG_TAG = 'ComponentProvider';
3434

3535
// The components module manages the lifetime of dependencies of the Firestore
3636
// client. Dependencies can be lazily constructed and only one exists per
3737
// Firestore instance.
3838

39-
/**
40-
* An instance map that ensures only one FirestoreClient exists per Firestore
41-
* instance.
42-
*/
43-
const firestoreClientInstances = new Map<Firestore, Promise<FirestoreClient>>();
39+
// Instance maps that ensure that only one component provider exists per
40+
// Firestore instance.
41+
const offlineComponentProviders = new Map<
42+
Firestore,
43+
Promise<OfflineComponentProvider>
44+
>();
45+
const onlineComponentProviders = new Map<
46+
Firestore,
47+
Promise<OnlineComponentProvider>
48+
>();
4449

45-
/**
46-
* Returns the initialized and started FirestoreClient for the given Firestore
47-
* instance. If none exists, creates a new FirestoreClient with memory
48-
* persistence. Callers must invoke removeFirestoreClient() when the Firestore
49-
* instance is terminated.
50-
*/
51-
export function getFirestoreClient(
50+
export async function setOfflineComponentProvider(
51+
firestore: Firestore,
52+
persistenceSettings: PersistenceSettings,
53+
offlineComponentProvider: OfflineComponentProvider
54+
): Promise<void> {
55+
const offlineDeferred = new Deferred<OfflineComponentProvider>();
56+
offlineComponentProviders.set(firestore, offlineDeferred.promise);
57+
58+
const configuration = await firestore._getConfiguration();
59+
configuration.persistenceSettings = persistenceSettings;
60+
61+
logDebug(LOG_TAG, 'Initializing OfflineComponentProvider');
62+
await offlineComponentProvider.initialize(configuration);
63+
firestore._setCredentialChangeListener(user =>
64+
// TODO(firestorexp): This should be a retryable IndexedDB operation
65+
firestore._queue.enqueueAndForget(() =>
66+
// TODO(firestorexp): Make sure handleUserChange is a no-op if user
67+
// didn't change
68+
offlineComponentProvider.localStore.handleUserChange(user)
69+
)
70+
);
71+
// When a user calls clearPersistence() in one client, all other clients
72+
// need to be terminated to allow the delete to succeed.
73+
offlineComponentProvider.persistence.setDatabaseDeletedListener(() =>
74+
firestore.delete()
75+
);
76+
offlineDeferred.resolve(offlineComponentProvider);
77+
}
78+
79+
export async function setOnlineComponentProvider(
80+
firestore: Firestore,
81+
onlineComponentProvider: OnlineComponentProvider
82+
): Promise<void> {
83+
const onlineDeferred = new Deferred<OnlineComponentProvider>();
84+
onlineComponentProviders.set(firestore, onlineDeferred.promise);
85+
86+
const configuration = await firestore._getConfiguration();
87+
const offlineComponentProvider = await getOfflineComponentProvider(firestore);
88+
89+
logDebug(LOG_TAG, 'Initializing OnlineComponentProvider');
90+
await onlineComponentProvider.initialize(
91+
offlineComponentProvider,
92+
configuration
93+
);
94+
// The CredentialChangeListener of the online component provider takes
95+
// precedence over the offline component provider.
96+
firestore._setCredentialChangeListener(user =>
97+
firestore._queue.enqueueAndForget(() =>
98+
onlineComponentProvider.remoteStore.handleCredentialChange(user)
99+
)
100+
);
101+
onlineDeferred.resolve(onlineComponentProvider);
102+
}
103+
104+
function getOfflineComponentProvider(
52105
firestore: Firestore
53-
): Promise<FirestoreClient> {
54-
if (firestore._terminated) {
55-
throw new FirestoreError(
56-
Code.FAILED_PRECONDITION,
57-
'The client has already been terminated.'
58-
);
59-
}
60-
if (!firestoreClientInstances.has(firestore)) {
106+
): Promise<OfflineComponentProvider> {
107+
verifyNotTerminated(firestore);
108+
109+
if (!offlineComponentProviders.has(firestore)) {
110+
logDebug(LOG_TAG, 'Using default OfflineComponentProvider');
61111
// eslint-disable-next-line @typescript-eslint/no-floating-promises
62-
initializeFirestoreClient(
112+
setOfflineComponentProvider(
63113
firestore,
64-
new MemoryOfflineComponentProvider(),
65-
new OnlineComponentProvider(),
66-
{
67-
durable: false
68-
}
114+
{ durable: false },
115+
new MemoryOfflineComponentProvider()
69116
);
70117
}
71-
return firestoreClientInstances.get(firestore)!;
118+
return offlineComponentProviders.get(firestore)!;
72119
}
73120

74-
/**
75-
* Creates a new FirestoreClient for the given Firestore instance. Throws if the
76-
* instance exists.
77-
*
78-
* @param firestore The Firestore instance for which to create the
79-
* FirestoreClient.
80-
* @param offlineComponentProvider Provider that returns all components required
81-
* for memory-only or IndexedDB persistence.
82-
* @param onlineComponentProvider Provider that returns all components required
83-
* for online support.
84-
* @param persistenceSettings Settings for the component provider.
85-
*/
86-
export function initializeFirestoreClient(
87-
firestore: Firestore,
88-
offlineComponentProvider: OfflineComponentProvider,
89-
onlineComponentProvider: OnlineComponentProvider,
90-
persistenceSettings: PersistenceSettings
91-
): Promise<void> {
92-
if (firestoreClientInstances.has(firestore)) {
121+
function getOnlineComponentProvider(
122+
firestore: Firestore
123+
): Promise<OnlineComponentProvider> {
124+
verifyNotTerminated(firestore);
125+
126+
if (!onlineComponentProviders.has(firestore)) {
127+
logDebug(LOG_TAG, 'Using default OnlineComponentProvider');
128+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
129+
setOnlineComponentProvider(firestore, new OnlineComponentProvider());
130+
}
131+
return onlineComponentProviders.get(firestore)!;
132+
}
133+
134+
function verifyNotTerminated(firestore: Firestore): void {
135+
if (firestore._terminated) {
93136
throw new FirestoreError(
94137
Code.FAILED_PRECONDITION,
95-
'Firestore has already been started and persistence can no longer ' +
96-
'be enabled. You can only enable persistence before calling ' +
97-
'any other methods on a Firestore object.'
138+
'The client has already been terminated.'
98139
);
99140
}
100-
logDebug(LOG_TAG, 'Initializing FirestoreClient');
101-
const settings = firestore._getSettings();
102-
const databaseInfo = new DatabaseInfo(
103-
firestore._databaseId,
104-
firestore._persistenceKey,
105-
settings.host ?? DEFAULT_HOST,
106-
settings.ssl ?? DEFAULT_SSL,
107-
/** forceLongPolling= */ false
141+
}
142+
143+
export function getSyncEngine(firestore: Firestore): Promise<SyncEngine> {
144+
return getOnlineComponentProvider(firestore).then(
145+
components => components.syncEngine
108146
);
109-
const firestoreClient = new FirestoreClient(
110-
firestore._credentials,
111-
firestore._queue
147+
}
148+
149+
export function getRemoteStore(firestore: Firestore): Promise<RemoteStore> {
150+
return getOnlineComponentProvider(firestore).then(
151+
components => components.remoteStore
112152
);
113-
const initializationPromise = firestoreClient.start(
114-
databaseInfo,
115-
offlineComponentProvider,
116-
onlineComponentProvider,
117-
persistenceSettings
153+
}
154+
155+
export function getEventManager(firestore: Firestore): Promise<EventManager> {
156+
return getOnlineComponentProvider(firestore).then(
157+
components => components.eventManager
118158
);
119-
firestoreClientInstances.set(
120-
firestore,
121-
initializationPromise.then(() => firestoreClient)
159+
}
160+
161+
export function getPersistence(firestore: Firestore): Promise<Persistence> {
162+
return getOfflineComponentProvider(firestore).then(
163+
components => components.persistence
122164
);
123-
return initializationPromise;
124165
}
125166

126-
/**
127-
* Removes and terminates the FirestoreClient for the given instance if it has
128-
* been started.
129-
*/
130-
export async function removeFirestoreClient(
131-
firestore: Firestore
132-
): Promise<void> {
133-
const firestoreClientPromise = firestoreClientInstances.get(firestore);
134-
if (firestoreClientPromise) {
135-
firestoreClientInstances.delete(firestore);
136-
return firestoreClientPromise.then(firestoreClient =>
137-
firestoreClient.terminate()
138-
);
139-
}
167+
export function getLocalStore(firestore: Firestore): Promise<LocalStore> {
168+
return getOfflineComponentProvider(firestore).then(
169+
provider => provider.localStore
170+
);
140171
}
141172

142173
/**
143174
* Removes all components associated with the provided instance. Must be called
144175
* when the Firestore instance is terminated.
145176
*/
146177
export async function removeComponents(firestore: Firestore): Promise<void> {
147-
const firestoreClientPromise = firestoreClientInstances.get(firestore);
148-
if (firestoreClientPromise) {
149-
logDebug(LOG_TAG, 'Removing FirestoreClient');
150-
firestoreClientInstances.delete(firestore);
151-
return (await firestoreClientPromise).terminate();
178+
const onlineComponentProviderPromise = onlineComponentProviders.get(
179+
firestore
180+
);
181+
if (onlineComponentProviderPromise) {
182+
logDebug(LOG_TAG, 'Removing OnlineComponentProvider');
183+
onlineComponentProviders.delete(firestore);
184+
await (await onlineComponentProviderPromise).terminate();
185+
}
186+
187+
const offlineComponentProviderPromise = offlineComponentProviders.get(
188+
firestore
189+
);
190+
if (offlineComponentProviderPromise) {
191+
logDebug(LOG_TAG, 'Removing OfflineComponentProvider');
192+
offlineComponentProviders.delete(firestore);
193+
await (await offlineComponentProviderPromise).terminate();
152194
}
153195
}

0 commit comments

Comments
 (0)