Skip to content

Commit 80d2230

Browse files
ComponentProvider v2
1 parent 9bc4167 commit 80d2230

9 files changed

+201
-162
lines changed

packages/firestore/src/core/component_provider.ts

Lines changed: 185 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -48,34 +48,17 @@ import {
4848
* Initializes and wires up all core components for Firestore. Implementations
4949
* override `initialize()` to provide all components.
5050
*/
51-
export interface ComponentProvider {
52-
persistence: Persistence;
53-
sharedClientState: SharedClientState;
54-
localStore: LocalStore;
55-
syncEngine: SyncEngine;
56-
gcScheduler: GarbageCollectionScheduler | null;
57-
remoteStore: RemoteStore;
58-
eventManager: EventManager;
51+
export abstract class ComponentProvider {
52+
protected asyncQueue!: AsyncQueue;
53+
protected databaseInfo!: DatabaseInfo;
54+
protected platform!: Platform;
55+
protected datastore!: Datastore;
56+
protected clientId!: ClientId;
57+
protected initialUser!: User;
58+
protected maxConcurrentLimboResolutions!: number;
59+
protected persistenceSettings!: PersistenceSettings;
5960

60-
initialize(
61-
asyncQueue: AsyncQueue,
62-
databaseInfo: DatabaseInfo,
63-
platform: Platform,
64-
datastore: Datastore,
65-
clientId: ClientId,
66-
initialUser: User,
67-
maxConcurrentLimboResolutions: number,
68-
persistenceSettings: PersistenceSettings
69-
): Promise<void>;
70-
71-
clearPersistence(databaseId: DatabaseInfo): Promise<void>;
72-
}
73-
74-
/**
75-
* Provides all components needed for Firestore with IndexedDB persistence.
76-
*/
77-
export class IndexedDbComponentProvider implements ComponentProvider {
78-
persistence!: IndexedDbPersistence;
61+
persistence!: Persistence;
7962
sharedClientState!: SharedClientState;
8063
localStore!: LocalStore;
8164
syncEngine!: SyncEngine;
@@ -93,96 +76,162 @@ export class IndexedDbComponentProvider implements ComponentProvider {
9376
maxConcurrentLimboResolutions: number,
9477
persistenceSettings: PersistenceSettings
9578
): Promise<void> {
96-
debugAssert(
97-
persistenceSettings.durable,
98-
'IndexedDbComponentProvider can only provide durable persistence'
99-
);
100-
debugAssert(!this.sharedClientState, 'initialize() already called');
101-
102-
const persistenceKey = IndexedDbPersistence.buildStoragePrefix(
103-
databaseInfo
104-
);
79+
this.asyncQueue = asyncQueue;
80+
this.databaseInfo = databaseInfo;
81+
this.platform = platform;
82+
this.datastore = datastore;
83+
this.clientId = clientId;
84+
this.initialUser = initialUser;
85+
this.maxConcurrentLimboResolutions = maxConcurrentLimboResolutions;
86+
this.persistenceSettings = persistenceSettings;
10587

106-
const serializer = platform.newSerializer(databaseInfo.databaseId);
107-
if (!WebStorageSharedClientState.isAvailable(platform)) {
108-
throw new FirestoreError(
109-
Code.UNIMPLEMENTED,
110-
'IndexedDB persistence is only available on platforms that support LocalStorage.'
111-
);
112-
}
88+
this.sharedClientState = this.createSharedClientState();
89+
this.persistence = this.createPersistence();
90+
await this.persistence.start();
91+
this.gcScheduler = this.createGarbageCollectionScheduler();
92+
this.localStore = this.createLocalStore();
93+
this.remoteStore = this.createRemoteStore();
94+
this.syncEngine = this.createSyncEngine();
95+
this.eventManager = this.createEventManager();
11396

114-
this.sharedClientState = persistenceSettings.synchronizeTabs
115-
? new WebStorageSharedClientState(
116-
asyncQueue,
117-
platform,
118-
persistenceKey,
119-
clientId,
120-
initialUser
121-
)
122-
: new MemorySharedClientState();
12397
this.sharedClientState.onlineStateHandler = onlineState =>
12498
this.syncEngine.applyOnlineStateChange(
12599
onlineState,
126100
OnlineStateSource.SharedClientState
127101
);
102+
this.remoteStore.syncEngine = this.syncEngine;
103+
this.sharedClientState.syncEngine = this.syncEngine;
128104

129-
this.persistence = await IndexedDbPersistence.createIndexedDbPersistence({
130-
allowTabSynchronization: persistenceSettings.synchronizeTabs,
131-
persistenceKey,
132-
clientId,
133-
platform,
134-
queue: asyncQueue,
135-
serializer,
136-
lruParams: LruParams.withCacheSize(persistenceSettings.cacheSizeBytes),
137-
sequenceNumberSyncer: this.sharedClientState
105+
await this.sharedClientState.start();
106+
await this.remoteStore.start();
107+
await this.localStore.start();
108+
109+
// NOTE: This will immediately call the listener, so we make sure to
110+
// set it after localStore / remoteStore are started.
111+
await this.persistence.setPrimaryStateListener(async isPrimary => {
112+
await this.syncEngine.applyPrimaryState(isPrimary);
113+
if (this.gcScheduler) {
114+
if (isPrimary && !this.gcScheduler.started) {
115+
this.gcScheduler.start(this.localStore);
116+
} else if (!isPrimary) {
117+
this.gcScheduler.stop();
118+
}
119+
}
138120
});
121+
}
122+
123+
protected abstract createSharedClientState(): SharedClientState;
124+
protected abstract createPersistence(): Persistence;
125+
protected abstract createGarbageCollectionScheduler(): GarbageCollectionScheduler | null;
126+
protected abstract createLocalStore(): LocalStore;
127+
protected abstract createRemoteStore(): RemoteStore;
128+
protected abstract createSyncEngine(): SyncEngine;
129+
protected abstract createEventManager(): EventManager;
130+
131+
abstract clearPersistence(databaseId: DatabaseInfo): Promise<void>;
132+
}
133+
134+
/**
135+
* Provides all components needed for Firestore with IndexedDB persistence.
136+
*/
137+
export class IndexedDbComponentProvider extends ComponentProvider {
138+
protected createEventManager(): EventManager {
139+
return new EventManager(this.syncEngine);
140+
}
139141

142+
protected createGarbageCollectionScheduler(): GarbageCollectionScheduler | null {
143+
debugAssert(
144+
this.persistence instanceof IndexedDbPersistence,
145+
'Expected persistence to be of type IndexedDbPersistence'
146+
);
140147
const garbageCollector = this.persistence.referenceDelegate
141148
.garbageCollector;
149+
return new LruScheduler(garbageCollector, this.asyncQueue);
150+
}
142151

143-
this.gcScheduler = new LruScheduler(garbageCollector, asyncQueue);
144-
this.localStore = new LocalStore(
152+
protected createLocalStore(): LocalStore {
153+
return new LocalStore(
145154
this.persistence,
146155
new IndexFreeQueryEngine(),
147-
initialUser
156+
this.initialUser
148157
);
149-
this.remoteStore = new RemoteStore(
158+
}
159+
160+
protected createPersistence(): Persistence {
161+
debugAssert(
162+
this.persistenceSettings.durable,
163+
'Can only start durable persistence'
164+
);
165+
166+
const persistenceKey = IndexedDbPersistence.buildStoragePrefix(
167+
this.databaseInfo
168+
);
169+
const serializer = this.platform.newSerializer(
170+
this.databaseInfo.databaseId
171+
);
172+
return IndexedDbPersistence.createIndexedDbPersistence({
173+
allowTabSynchronization: this.persistenceSettings.synchronizeTabs,
174+
persistenceKey,
175+
clientId: this.clientId,
176+
platform: this.platform,
177+
queue: this.asyncQueue,
178+
serializer,
179+
lruParams: LruParams.withCacheSize(
180+
this.persistenceSettings.cacheSizeBytes
181+
),
182+
sequenceNumberSyncer: this.sharedClientState
183+
});
184+
}
185+
186+
protected createRemoteStore(): RemoteStore {
187+
return new RemoteStore(
150188
this.localStore,
151-
datastore,
152-
asyncQueue,
189+
this.datastore,
190+
this.asyncQueue,
153191
onlineState =>
154192
this.syncEngine.applyOnlineStateChange(
155193
onlineState,
156194
OnlineStateSource.RemoteStore
157195
),
158-
platform.newConnectivityMonitor()
196+
this.platform.newConnectivityMonitor()
159197
);
160-
this.syncEngine = new SyncEngine(
198+
}
199+
200+
protected createSharedClientState(): SharedClientState {
201+
debugAssert(
202+
this.persistenceSettings.durable,
203+
'Can only start durable persistence'
204+
);
205+
206+
if (this.persistenceSettings.synchronizeTabs) {
207+
if (!WebStorageSharedClientState.isAvailable(this.platform)) {
208+
throw new FirestoreError(
209+
Code.UNIMPLEMENTED,
210+
'IndexedDB persistence is only available on platforms that support LocalStorage.'
211+
);
212+
}
213+
const persistenceKey = IndexedDbPersistence.buildStoragePrefix(
214+
this.databaseInfo
215+
);
216+
return new WebStorageSharedClientState(
217+
this.asyncQueue,
218+
this.platform,
219+
persistenceKey,
220+
this.clientId,
221+
this.initialUser
222+
);
223+
}
224+
return new MemorySharedClientState();
225+
}
226+
227+
protected createSyncEngine(): SyncEngine {
228+
return new SyncEngine(
161229
this.localStore,
162230
this.remoteStore,
163231
this.sharedClientState,
164-
initialUser,
165-
maxConcurrentLimboResolutions
232+
this.initialUser,
233+
this.maxConcurrentLimboResolutions
166234
);
167-
this.eventManager = new EventManager(this.syncEngine);
168-
169-
this.remoteStore.syncEngine = this.syncEngine;
170-
this.sharedClientState.syncEngine = this.syncEngine;
171-
172-
await this.sharedClientState.start();
173-
await this.remoteStore.start();
174-
await this.localStore.start();
175-
176-
// NOTE: This will immediately call the listener, so we make sure to
177-
// set it after localStore / remoteStore are started.
178-
await this.persistence.setPrimaryStateListener(async isPrimary => {
179-
await this.syncEngine.applyPrimaryState(isPrimary);
180-
if (isPrimary && !this.gcScheduler!.started) {
181-
this.gcScheduler!.start(this.localStore);
182-
} else if (!isPrimary) {
183-
this.gcScheduler!.stop();
184-
}
185-
});
186235
}
187236

188237
clearPersistence(databaseInfo: DatabaseInfo): Promise<void> {
@@ -201,80 +250,71 @@ const MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE =
201250
/**
202251
* Provides all components needed for Firestore with in-memory persistence.
203252
*/
204-
export class MemoryComponentProvider implements ComponentProvider {
205-
persistence!: Persistence;
206-
sharedClientState!: SharedClientState;
207-
localStore!: LocalStore;
208-
syncEngine!: SyncEngine;
209-
gcScheduler!: GarbageCollectionScheduler | null;
210-
remoteStore!: RemoteStore;
211-
eventManager!: EventManager;
212-
253+
export class MemoryComponentProvider extends ComponentProvider {
213254
constructor(
214255
readonly referenceDelegateFactory: (
215256
p: MemoryPersistence
216257
) => MemoryReferenceDelegate = MemoryEagerDelegate.factory
217-
) {}
218-
219-
async initialize(
220-
asyncQueue: AsyncQueue,
221-
databaseInfo: DatabaseInfo,
222-
platform: Platform,
223-
datastore: Datastore,
224-
clientId: ClientId,
225-
initialUser: User,
226-
maxConcurrentLimboResolutions: number,
227-
persistenceSettings: PersistenceSettings
228-
): Promise<void> {
229-
if (persistenceSettings.durable) {
230-
throw new FirestoreError(
231-
Code.FAILED_PRECONDITION,
232-
MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE
233-
);
234-
}
258+
) {
259+
super();
260+
}
235261

236-
this.persistence = new MemoryPersistence(
237-
clientId,
238-
this.referenceDelegateFactory
262+
clearPersistence(): never {
263+
throw new FirestoreError(
264+
Code.FAILED_PRECONDITION,
265+
MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE
239266
);
240-
this.gcScheduler = null;
241-
this.sharedClientState = new MemorySharedClientState();
242-
this.localStore = new LocalStore(
267+
}
268+
269+
protected createEventManager(): EventManager {
270+
return new EventManager(this.syncEngine);
271+
}
272+
273+
protected createGarbageCollectionScheduler(): GarbageCollectionScheduler | null {
274+
return null;
275+
}
276+
277+
protected createLocalStore(): LocalStore {
278+
return new LocalStore(
243279
this.persistence,
244280
new IndexFreeQueryEngine(),
245-
initialUser
281+
this.initialUser
282+
);
283+
}
284+
285+
protected createPersistence(): Persistence {
286+
debugAssert(
287+
!this.persistenceSettings.durable,
288+
'Can only start memory persistence'
246289
);
247-
this.remoteStore = new RemoteStore(
290+
return new MemoryPersistence(this.clientId, this.referenceDelegateFactory);
291+
}
292+
293+
protected createRemoteStore(): RemoteStore {
294+
return new RemoteStore(
248295
this.localStore,
249-
datastore,
250-
asyncQueue,
296+
this.datastore,
297+
this.asyncQueue,
251298
onlineState =>
252299
this.syncEngine.applyOnlineStateChange(
253300
onlineState,
254301
OnlineStateSource.RemoteStore
255302
),
256-
platform.newConnectivityMonitor()
303+
this.platform.newConnectivityMonitor()
257304
);
258-
this.syncEngine = new SyncEngine(
259-
this.localStore,
260-
this.remoteStore,
261-
this.sharedClientState,
262-
initialUser,
263-
maxConcurrentLimboResolutions
264-
);
265-
this.eventManager = new EventManager(this.syncEngine);
266-
267-
this.remoteStore.syncEngine = this.syncEngine;
305+
}
268306

269-
await this.remoteStore.start();
270-
await this.remoteStore.applyPrimaryState(true);
271-
await this.syncEngine.applyPrimaryState(true);
307+
protected createSharedClientState(): SharedClientState {
308+
return new MemorySharedClientState();
272309
}
273310

274-
clearPersistence(): never {
275-
throw new FirestoreError(
276-
Code.FAILED_PRECONDITION,
277-
MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE
311+
protected createSyncEngine(): SyncEngine {
312+
return new SyncEngine(
313+
this.localStore,
314+
this.remoteStore,
315+
this.sharedClientState,
316+
this.initialUser,
317+
this.maxConcurrentLimboResolutions
278318
);
279319
}
280320
}

0 commit comments

Comments
 (0)