Skip to content

Commit 18730fc

Browse files
Merge ea58129 into 7e63eb5
2 parents 7e63eb5 + ea58129 commit 18730fc

10 files changed

+250
-242
lines changed

packages/firestore/src/core/component_provider.ts

Lines changed: 166 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,26 @@ import { IndexFreeQueryEngine } from '../local/index_free_query_engine';
4040
import { IndexedDbPersistence } from '../local/indexeddb_persistence';
4141
import {
4242
MemoryEagerDelegate,
43-
MemoryPersistence,
44-
MemoryReferenceDelegate
43+
MemoryLruDelegate,
44+
MemoryPersistence
4545
} from '../local/memory_persistence';
4646

47+
const MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE =
48+
'You are using the memory-only build of Firestore. Persistence support is ' +
49+
'only available via the @firebase/firestore bundle or the ' +
50+
'firebase-firestore.js build.';
51+
52+
export interface ComponentConfiguration {
53+
asyncQueue: AsyncQueue;
54+
databaseInfo: DatabaseInfo;
55+
platform: Platform;
56+
datastore: Datastore;
57+
clientId: ClientId;
58+
initialUser: User;
59+
maxConcurrentLimboResolutions: number;
60+
persistenceSettings: PersistenceSettings;
61+
}
62+
4763
/**
4864
* Initializes and wires up all core components for Firestore. Implementations
4965
* override `initialize()` to provide all components.
@@ -57,115 +73,39 @@ export interface ComponentProvider {
5773
remoteStore: RemoteStore;
5874
eventManager: EventManager;
5975

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>;
76+
initialize(cfg: ComponentConfiguration): Promise<void>;
7077

7178
clearPersistence(databaseId: DatabaseInfo): Promise<void>;
7279
}
7380

7481
/**
75-
* Provides all components needed for Firestore with IndexedDB persistence.
82+
* Provides all components needed for Firestore with in-memory persistence.
83+
* Uses EagerGC garbage collection.
7684
*/
77-
export class IndexedDbComponentProvider implements ComponentProvider {
78-
persistence!: IndexedDbPersistence;
85+
export class MemoryComponentProvider {
86+
persistence!: Persistence;
7987
sharedClientState!: SharedClientState;
8088
localStore!: LocalStore;
8189
syncEngine!: SyncEngine;
8290
gcScheduler!: GarbageCollectionScheduler | null;
8391
remoteStore!: RemoteStore;
8492
eventManager!: EventManager;
8593

86-
async initialize(
87-
asyncQueue: AsyncQueue,
88-
databaseInfo: DatabaseInfo,
89-
platform: Platform,
90-
datastore: Datastore,
91-
clientId: ClientId,
92-
initialUser: User,
93-
maxConcurrentLimboResolutions: number,
94-
persistenceSettings: PersistenceSettings
95-
): 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-
);
94+
async initialize(cfg: ComponentConfiguration): Promise<void> {
95+
this.sharedClientState = this.createSharedClientState(cfg);
96+
this.persistence = this.createPersistence(cfg);
97+
await this.persistence.start();
98+
this.gcScheduler = this.createGarbageCollectionScheduler(cfg);
99+
this.localStore = this.createLocalStore(cfg);
100+
this.remoteStore = this.createRemoteStore(cfg);
101+
this.syncEngine = this.createSyncEngine(cfg);
102+
this.eventManager = this.createEventManager(cfg);
105103

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-
}
113-
114-
this.sharedClientState = persistenceSettings.synchronizeTabs
115-
? new WebStorageSharedClientState(
116-
asyncQueue,
117-
platform,
118-
persistenceKey,
119-
clientId,
120-
initialUser
121-
)
122-
: new MemorySharedClientState();
123104
this.sharedClientState.onlineStateHandler = onlineState =>
124105
this.syncEngine.applyOnlineStateChange(
125106
onlineState,
126107
OnlineStateSource.SharedClientState
127108
);
128-
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
138-
});
139-
140-
const garbageCollector = this.persistence.referenceDelegate
141-
.garbageCollector;
142-
143-
this.gcScheduler = new LruScheduler(garbageCollector, asyncQueue);
144-
this.localStore = new LocalStore(
145-
this.persistence,
146-
new IndexFreeQueryEngine(),
147-
initialUser
148-
);
149-
this.remoteStore = new RemoteStore(
150-
this.localStore,
151-
datastore,
152-
asyncQueue,
153-
onlineState =>
154-
this.syncEngine.applyOnlineStateChange(
155-
onlineState,
156-
OnlineStateSource.RemoteStore
157-
),
158-
platform.newConnectivityMonitor()
159-
);
160-
this.syncEngine = new SyncEngine(
161-
this.localStore,
162-
this.remoteStore,
163-
this.sharedClientState,
164-
initialUser,
165-
maxConcurrentLimboResolutions
166-
);
167-
this.eventManager = new EventManager(this.syncEngine);
168-
169109
this.remoteStore.syncEngine = this.syncEngine;
170110
this.sharedClientState.syncEngine = this.syncEngine;
171111

@@ -177,104 +117,166 @@ export class IndexedDbComponentProvider implements ComponentProvider {
177117
// set it after localStore / remoteStore are started.
178118
await this.persistence.setPrimaryStateListener(async isPrimary => {
179119
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();
120+
if (this.gcScheduler) {
121+
if (isPrimary && !this.gcScheduler.started) {
122+
this.gcScheduler.start(this.localStore);
123+
} else if (!isPrimary) {
124+
this.gcScheduler.stop();
125+
}
184126
}
185127
});
186128
}
187129

188-
clearPersistence(databaseInfo: DatabaseInfo): Promise<void> {
189-
const persistenceKey = IndexedDbPersistence.buildStoragePrefix(
190-
databaseInfo
191-
);
192-
return IndexedDbPersistence.clearPersistence(persistenceKey);
130+
createEventManager(cfg: ComponentConfiguration): EventManager {
131+
return new EventManager(this.syncEngine);
193132
}
194-
}
195-
196-
const MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE =
197-
'You are using the memory-only build of Firestore. Persistence support is ' +
198-
'only available via the @firebase/firestore bundle or the ' +
199-
'firebase-firestore.js build.';
200-
201-
/**
202-
* Provides all components needed for Firestore with in-memory persistence.
203-
*/
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-
213-
constructor(
214-
readonly referenceDelegateFactory: (
215-
p: MemoryPersistence
216-
) => MemoryReferenceDelegate = MemoryEagerDelegate.factory
217-
) {}
218133

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-
}
134+
createGarbageCollectionScheduler(
135+
cfg: ComponentConfiguration
136+
): GarbageCollectionScheduler | null {
137+
return null;
138+
}
235139

236-
this.persistence = new MemoryPersistence(
237-
clientId,
238-
this.referenceDelegateFactory
239-
);
240-
this.gcScheduler = null;
241-
this.sharedClientState = new MemorySharedClientState();
242-
this.localStore = new LocalStore(
140+
createLocalStore(cfg: ComponentConfiguration): LocalStore {
141+
return new LocalStore(
243142
this.persistence,
244143
new IndexFreeQueryEngine(),
245-
initialUser
144+
cfg.initialUser
246145
);
247-
this.remoteStore = new RemoteStore(
146+
}
147+
148+
createPersistence(cfg: ComponentConfiguration): Persistence {
149+
debugAssert(
150+
!cfg.persistenceSettings.durable,
151+
'Can only start memory persistence'
152+
);
153+
return new MemoryPersistence(cfg.clientId, MemoryEagerDelegate.factory);
154+
}
155+
156+
createRemoteStore(cfg: ComponentConfiguration): RemoteStore {
157+
return new RemoteStore(
248158
this.localStore,
249-
datastore,
250-
asyncQueue,
159+
cfg.datastore,
160+
cfg.asyncQueue,
251161
onlineState =>
252162
this.syncEngine.applyOnlineStateChange(
253163
onlineState,
254164
OnlineStateSource.RemoteStore
255165
),
256-
platform.newConnectivityMonitor()
166+
cfg.platform.newConnectivityMonitor()
257167
);
258-
this.syncEngine = new SyncEngine(
168+
}
169+
170+
createSharedClientState(cfg: ComponentConfiguration): SharedClientState {
171+
return new MemorySharedClientState();
172+
}
173+
174+
createSyncEngine(cfg: ComponentConfiguration): SyncEngine {
175+
return new SyncEngine(
259176
this.localStore,
260177
this.remoteStore,
261178
this.sharedClientState,
262-
initialUser,
263-
maxConcurrentLimboResolutions
179+
cfg.initialUser,
180+
cfg.maxConcurrentLimboResolutions
264181
);
265-
this.eventManager = new EventManager(this.syncEngine);
266-
267-
this.remoteStore.syncEngine = this.syncEngine;
268-
269-
await this.remoteStore.start();
270-
await this.remoteStore.applyPrimaryState(true);
271-
await this.syncEngine.applyPrimaryState(true);
272182
}
273183

274-
clearPersistence(): never {
184+
clearPersistence(databaseInfo: DatabaseInfo): Promise<void> {
275185
throw new FirestoreError(
276186
Code.FAILED_PRECONDITION,
277187
MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE
278188
);
279189
}
280190
}
191+
192+
/**
193+
* Provides all components needed for Firestore with in-memory persistence.
194+
* Uses LRU garbage collection.
195+
*/
196+
export class MemoryLruComponentProvider extends MemoryComponentProvider {
197+
createPersistence(cfg: ComponentConfiguration): Persistence {
198+
debugAssert(
199+
!cfg.persistenceSettings.durable,
200+
'Can only start memory persistence'
201+
);
202+
return new MemoryPersistence(
203+
cfg.clientId,
204+
p => new MemoryLruDelegate(p, LruParams.DEFAULT)
205+
);
206+
}
207+
}
208+
209+
/**
210+
* Provides all components needed for Firestore with IndexedDB persistence.
211+
*/
212+
export class IndexedDbComponentProvider extends MemoryComponentProvider {
213+
createGarbageCollectionScheduler(
214+
cfg: ComponentConfiguration
215+
): GarbageCollectionScheduler | null {
216+
debugAssert(
217+
this.persistence instanceof IndexedDbPersistence,
218+
'IndexedDbComponentProvider should provide IndexedDBPersistence'
219+
);
220+
const garbageCollector = this.persistence.referenceDelegate
221+
.garbageCollector;
222+
return new LruScheduler(garbageCollector, cfg.asyncQueue);
223+
}
224+
225+
createPersistence(cfg: ComponentConfiguration): Persistence {
226+
debugAssert(
227+
cfg.persistenceSettings.durable,
228+
'Can only start durable persistence'
229+
);
230+
231+
const persistenceKey = IndexedDbPersistence.buildStoragePrefix(
232+
cfg.databaseInfo
233+
);
234+
const serializer = cfg.platform.newSerializer(cfg.databaseInfo.databaseId);
235+
return IndexedDbPersistence.createIndexedDbPersistence({
236+
allowTabSynchronization: cfg.persistenceSettings.synchronizeTabs,
237+
persistenceKey,
238+
clientId: cfg.clientId,
239+
platform: cfg.platform,
240+
queue: cfg.asyncQueue,
241+
serializer,
242+
lruParams: LruParams.withCacheSize(
243+
cfg.persistenceSettings.cacheSizeBytes
244+
),
245+
sequenceNumberSyncer: this.sharedClientState
246+
});
247+
}
248+
249+
createSharedClientState(cfg: ComponentConfiguration): SharedClientState {
250+
debugAssert(
251+
cfg.persistenceSettings.durable,
252+
'Can only start durable persistence'
253+
);
254+
255+
if (cfg.persistenceSettings.synchronizeTabs) {
256+
if (!WebStorageSharedClientState.isAvailable(cfg.platform)) {
257+
throw new FirestoreError(
258+
Code.UNIMPLEMENTED,
259+
'IndexedDB persistence is only available on platforms that support LocalStorage.'
260+
);
261+
}
262+
const persistenceKey = IndexedDbPersistence.buildStoragePrefix(
263+
cfg.databaseInfo
264+
);
265+
return new WebStorageSharedClientState(
266+
cfg.asyncQueue,
267+
cfg.platform,
268+
persistenceKey,
269+
cfg.clientId,
270+
cfg.initialUser
271+
);
272+
}
273+
return new MemorySharedClientState();
274+
}
275+
276+
clearPersistence(databaseInfo: DatabaseInfo): Promise<void> {
277+
const persistenceKey = IndexedDbPersistence.buildStoragePrefix(
278+
databaseInfo
279+
);
280+
return IndexedDbPersistence.clearPersistence(persistenceKey);
281+
}
282+
}

0 commit comments

Comments
 (0)