@@ -22,8 +22,8 @@ import {
22
22
IndexBackfillerScheduler
23
23
} from '../local/index_backfiller' ;
24
24
import {
25
- indexedDbStoragePrefix ,
26
- IndexedDbPersistence
25
+ IndexedDbPersistence ,
26
+ indexedDbStoragePrefix
27
27
} from '../local/indexeddb_persistence' ;
28
28
import { LocalStore } from '../local/local_store' ;
29
29
import { newLocalStore } from '../local/local_store_impl' ;
@@ -34,7 +34,7 @@ import {
34
34
MemoryLruDelegate ,
35
35
MemoryPersistence
36
36
} from '../local/memory_persistence' ;
37
- import { Scheduler , Persistence } from '../local/persistence' ;
37
+ import { Persistence , Scheduler } from '../local/persistence' ;
38
38
import { QueryEngine } from '../local/query_engine' ;
39
39
import {
40
40
ClientId ,
@@ -57,6 +57,7 @@ import { JsonProtoSerializer } from '../remote/serializer';
57
57
import { hardAssert } from '../util/assert' ;
58
58
import { AsyncQueue } from '../util/async_queue' ;
59
59
import { Code , FirestoreError } from '../util/error' ;
60
+ import { logDebug } from '../util/log' ;
60
61
61
62
import { DatabaseInfo } from './database_info' ;
62
63
import { EventManager , newEventManager } from './event_manager' ;
@@ -90,6 +91,7 @@ export interface ComponentConfiguration {
90
91
* cache. Implementations override `initialize()` to provide all components.
91
92
*/
92
93
export interface OfflineComponentProvider {
94
+ asyncQueue : AsyncQueue ;
93
95
persistence : Persistence ;
94
96
sharedClientState : SharedClientState ;
95
97
localStore : LocalStore ;
@@ -109,16 +111,23 @@ export interface OfflineComponentProvider {
109
111
export class MemoryOfflineComponentProvider
110
112
implements OfflineComponentProvider
111
113
{
114
+ asyncQueue ! : AsyncQueue ;
112
115
persistence ! : Persistence ;
113
116
sharedClientState ! : SharedClientState ;
114
117
localStore ! : LocalStore ;
115
118
gcScheduler ! : Scheduler | null ;
116
- indexBackfillerScheduler ! : Scheduler | null ;
119
+ indexBackfillerScheduler : Scheduler | null = null ;
117
120
synchronizeTabs = false ;
118
121
119
122
serializer ! : JsonProtoSerializer ;
120
123
124
+ get schedulers ( ) : Scheduler [ ] {
125
+ const schedulers = [ this . gcScheduler , this . indexBackfillerScheduler ] ;
126
+ return schedulers . filter ( scheduler => ! ! scheduler ) as Scheduler [ ] ;
127
+ }
128
+
121
129
async initialize ( cfg : ComponentConfiguration ) : Promise < void > {
130
+ this . asyncQueue = cfg . asyncQueue ;
122
131
this . serializer = newSerializer ( cfg . databaseInfo . databaseId ) ;
123
132
this . sharedClientState = this . createSharedClientState ( cfg ) ;
124
133
this . persistence = this . createPersistence ( cfg ) ;
@@ -128,10 +137,6 @@ export class MemoryOfflineComponentProvider
128
137
cfg ,
129
138
this . localStore
130
139
) ;
131
- this . indexBackfillerScheduler = this . createIndexBackfillerScheduler (
132
- cfg ,
133
- this . localStore
134
- ) ;
135
140
}
136
141
137
142
createGarbageCollectionScheduler (
@@ -141,13 +146,6 @@ export class MemoryOfflineComponentProvider
141
146
return null ;
142
147
}
143
148
144
- createIndexBackfillerScheduler (
145
- cfg : ComponentConfiguration ,
146
- localStore : LocalStore
147
- ) : Scheduler | null {
148
- return null ;
149
- }
150
-
151
149
createLocalStore ( cfg : ComponentConfiguration ) : LocalStore {
152
150
return newLocalStore (
153
151
this . persistence ,
@@ -166,8 +164,9 @@ export class MemoryOfflineComponentProvider
166
164
}
167
165
168
166
async terminate ( ) : Promise < void > {
169
- this . gcScheduler ?. stop ( ) ;
170
- this . indexBackfillerScheduler ?. stop ( ) ;
167
+ for ( const scheduler of this . schedulers ) {
168
+ scheduler . stop ( ) ;
169
+ }
171
170
this . sharedClientState . shutdown ( ) ;
172
171
await this . persistence . shutdown ( ) ;
173
172
}
@@ -215,6 +214,8 @@ export class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentPro
215
214
indexBackfillerScheduler ! : Scheduler | null ;
216
215
synchronizeTabs = false ;
217
216
217
+ private primaryStateListenerNotified = false ;
218
+
218
219
constructor (
219
220
protected readonly onlineComponentProvider : OnlineComponentProvider ,
220
221
protected readonly cacheSizeBytes : number | undefined ,
@@ -237,19 +238,30 @@ export class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentPro
237
238
// NOTE: This will immediately call the listener, so we make sure to
238
239
// set it after localStore / remoteStore are started.
239
240
await this . persistence . setPrimaryStateListener ( ( ) => {
240
- if ( this . gcScheduler && ! this . gcScheduler . started ) {
241
- this . gcScheduler . start ( ) ;
242
- }
243
- if (
244
- this . indexBackfillerScheduler &&
245
- ! this . indexBackfillerScheduler . started
246
- ) {
247
- this . indexBackfillerScheduler . start ( ) ;
248
- }
241
+ this . primaryStateListenerNotified = true ;
242
+ this . startSchedulers ( ) ;
249
243
return Promise . resolve ( ) ;
250
244
} ) ;
251
245
}
252
246
247
+ private startSchedulers ( ) : void {
248
+ if ( ! this . primaryStateListenerNotified ) {
249
+ return ;
250
+ }
251
+
252
+ for ( const scheduler of this . schedulers ) {
253
+ if ( ! scheduler . started ) {
254
+ scheduler . start ( ) ;
255
+ }
256
+ }
257
+ }
258
+
259
+ installIndexBackfillerScheduler ( scheduler : IndexBackfillerScheduler ) : void {
260
+ hardAssert ( ! this . indexBackfillerScheduler ) ;
261
+ this . indexBackfillerScheduler = scheduler ;
262
+ this . startSchedulers ( ) ;
263
+ }
264
+
253
265
createLocalStore ( cfg : ComponentConfiguration ) : LocalStore {
254
266
return newLocalStore (
255
267
this . persistence ,
@@ -268,14 +280,6 @@ export class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentPro
268
280
return new LruScheduler ( garbageCollector , cfg . asyncQueue , localStore ) ;
269
281
}
270
282
271
- createIndexBackfillerScheduler (
272
- cfg : ComponentConfiguration ,
273
- localStore : LocalStore
274
- ) : Scheduler | null {
275
- const indexBackfiller = new IndexBackfiller ( localStore , this . persistence ) ;
276
- return new IndexBackfillerScheduler ( cfg . asyncQueue , indexBackfiller ) ;
277
- }
278
-
279
283
createPersistence ( cfg : ComponentConfiguration ) : IndexedDbPersistence {
280
284
const persistenceKey = indexedDbStoragePrefix (
281
285
cfg . databaseInfo . databaseId ,
@@ -305,6 +309,30 @@ export class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentPro
305
309
}
306
310
}
307
311
312
+ export function indexedDbOfflineComponentProviderInstallFieldIndexPlugin (
313
+ componentProvider : IndexedDbOfflineComponentProvider
314
+ ) : void {
315
+ if ( componentProvider . indexBackfillerScheduler ) {
316
+ return ;
317
+ }
318
+
319
+ logDebug (
320
+ 'Installing IndexBackfillerScheduler into OfflineComponentProvider ' +
321
+ 'to support persistent cache indexing.'
322
+ ) ;
323
+
324
+ const indexBackfiller = new IndexBackfiller (
325
+ componentProvider . localStore ,
326
+ componentProvider . persistence
327
+ ) ;
328
+ const scheduler = new IndexBackfillerScheduler (
329
+ componentProvider . asyncQueue ,
330
+ indexBackfiller
331
+ ) ;
332
+
333
+ componentProvider . installIndexBackfillerScheduler ( scheduler ) ;
334
+ }
335
+
308
336
/**
309
337
* Provides all components needed for Firestore with multi-tab IndexedDB
310
338
* persistence.
@@ -316,6 +344,8 @@ export class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentPro
316
344
export class MultiTabOfflineComponentProvider extends IndexedDbOfflineComponentProvider {
317
345
synchronizeTabs = true ;
318
346
347
+ private isPrimary : boolean | null = null ;
348
+
319
349
constructor (
320
350
protected readonly onlineComponentProvider : OnlineComponentProvider ,
321
351
protected readonly cacheSizeBytes : number | undefined
@@ -346,27 +376,33 @@ export class MultiTabOfflineComponentProvider extends IndexedDbOfflineComponentP
346
376
// NOTE: This will immediately call the listener, so we make sure to
347
377
// set it after localStore / remoteStore are started.
348
378
await this . persistence . setPrimaryStateListener ( async isPrimary => {
379
+ this . isPrimary = isPrimary ;
380
+
349
381
await syncEngineApplyPrimaryState (
350
382
this . onlineComponentProvider . syncEngine ,
351
383
isPrimary
352
384
) ;
353
- if ( this . gcScheduler ) {
354
- if ( isPrimary && ! this . gcScheduler . started ) {
355
- this . gcScheduler . start ( ) ;
356
- } else if ( ! isPrimary ) {
357
- this . gcScheduler . stop ( ) ;
358
- }
359
- }
360
- if ( this . indexBackfillerScheduler ) {
361
- if ( isPrimary && ! this . indexBackfillerScheduler . started ) {
362
- this . indexBackfillerScheduler . start ( ) ;
363
- } else if ( ! isPrimary ) {
364
- this . indexBackfillerScheduler . stop ( ) ;
365
- }
366
- }
385
+
386
+ this . startOrStopSchedulers ( ) ;
367
387
} ) ;
368
388
}
369
389
390
+ private startOrStopSchedulers ( ) : void {
391
+ for ( const scheduler of this . schedulers ) {
392
+ if ( this . isPrimary === true && ! scheduler . started ) {
393
+ scheduler . start ( ) ;
394
+ } else if ( this . isPrimary === false ) {
395
+ scheduler . stop ( ) ;
396
+ }
397
+ }
398
+ }
399
+
400
+ installIndexBackfillerScheduler ( scheduler : IndexBackfillerScheduler ) : void {
401
+ hardAssert ( ! this . indexBackfillerScheduler ) ;
402
+ this . indexBackfillerScheduler = scheduler ;
403
+ this . startOrStopSchedulers ( ) ;
404
+ }
405
+
370
406
createSharedClientState ( cfg : ComponentConfiguration ) : SharedClientState {
371
407
const window = getWindow ( ) ;
372
408
if ( ! WebStorageSharedClientState . isAvailable ( window ) ) {
0 commit comments