@@ -62,6 +62,25 @@ const LOG_TAG = 'RemoteStore';
62
62
// TODO(b/35853402): Negotiate this with the stream.
63
63
const MAX_PENDING_WRITES = 10 ;
64
64
65
+ /** Reasons for why the RemoteStore may be offline. */
66
+ const enum OfflineCause {
67
+ /** The user has explicitly disabled the network (via `disableNetwork()`). */
68
+ UserDisabled ,
69
+ /** An IndexedDb failure occurred while persisting a stream update. */
70
+ IndexedDbFailed ,
71
+ /** The tab is not the primary tab (only relevant with multi-tab). */
72
+ IsSecondary ,
73
+ /**
74
+ * We have received new Auth credentials and the RemoteStore is restarting
75
+ * the streams.
76
+ */
77
+ CredentialChange ,
78
+ /** The connectivity state of the environment has changed. */
79
+ ConnectivityChange ,
80
+ /** The RemoteStore has been shut down. */
81
+ Shutdown
82
+ }
83
+
65
84
/**
66
85
* RemoteStore - An interface to remotely stored data, basically providing a
67
86
* wrapper around the Datastore that is more reliable for the rest of the
@@ -118,19 +137,10 @@ export class RemoteStore implements TargetMetadataProvider {
118
137
private watchChangeAggregator : WatchChangeAggregator | null = null ;
119
138
120
139
/**
121
- * Set to true by enableNetwork() and false by disableNetwork() and indicates
122
- * the user-preferred network state.
123
- */
124
- private networkEnabled = false ;
125
-
126
- private isPrimary = false ;
127
-
128
- /**
129
- * When set to `true`, the network was taken offline due to an IndexedDB
130
- * failure. The state is flipped to `false` when access becomes available
131
- * again.
140
+ * A set of reasons for why the RemoteStore may be offline. If empty, the
141
+ * RemoteStore may start its network connections.
132
142
*/
133
- private indexedDbFailed = false ;
143
+ private offlineCauses = new Set < OfflineCause > ( ) ;
134
144
135
145
private onlineStateTracker : OnlineStateTracker ;
136
146
@@ -194,7 +204,7 @@ export class RemoteStore implements TargetMetadataProvider {
194
204
195
205
/** Re-enables the network. Idempotent. */
196
206
enableNetwork ( ) : Promise < void > {
197
- this . networkEnabled = true ;
207
+ this . offlineCauses . delete ( OfflineCause . UserDisabled ) ;
198
208
return this . enableNetworkInternal ( ) ;
199
209
}
200
210
@@ -216,7 +226,7 @@ export class RemoteStore implements TargetMetadataProvider {
216
226
* enableNetwork().
217
227
*/
218
228
async disableNetwork ( ) : Promise < void > {
219
- this . networkEnabled = false ;
229
+ this . offlineCauses . add ( OfflineCause . UserDisabled ) ;
220
230
await this . disableNetworkInternal ( ) ;
221
231
222
232
// Set the OnlineState to Offline so get()s return from cache, etc.
@@ -240,7 +250,7 @@ export class RemoteStore implements TargetMetadataProvider {
240
250
241
251
async shutdown ( ) : Promise < void > {
242
252
logDebug ( LOG_TAG , 'RemoteStore shutting down.' ) ;
243
- this . networkEnabled = false ;
253
+ this . offlineCauses . add ( OfflineCause . Shutdown ) ;
244
254
await this . disableNetworkInternal ( ) ;
245
255
this . connectivityMonitor . shutdown ( ) ;
246
256
@@ -349,7 +359,7 @@ export class RemoteStore implements TargetMetadataProvider {
349
359
}
350
360
351
361
canUseNetwork ( ) : boolean {
352
- return ! this . indexedDbFailed && this . isPrimary && this . networkEnabled ;
362
+ return this . offlineCauses . size === 0 ;
353
363
}
354
364
355
365
private cleanUpWatchStreamState ( ) : void {
@@ -457,10 +467,10 @@ export class RemoteStore implements TargetMetadataProvider {
457
467
) : Promise < void > {
458
468
if ( isIndexedDbTransactionError ( e ) ) {
459
469
debugAssert (
460
- ! this . indexedDbFailed ,
470
+ ! this . offlineCauses . has ( OfflineCause . IndexedDbFailed ) ,
461
471
'Unexpected network event when IndexedDB was marked failed.'
462
472
) ;
463
- this . indexedDbFailed = true ;
473
+ this . offlineCauses . add ( OfflineCause . IndexedDbFailed ) ;
464
474
465
475
// Disable network and raise offline snapshots
466
476
await this . disableNetworkInternal ( ) ;
@@ -477,7 +487,7 @@ export class RemoteStore implements TargetMetadataProvider {
477
487
this . asyncQueue . enqueueRetryable ( async ( ) => {
478
488
logDebug ( LOG_TAG , 'Retrying IndexedDB access' ) ;
479
489
await op ! ( ) ;
480
- this . indexedDbFailed = false ;
490
+ this . offlineCauses . delete ( OfflineCause . IndexedDbFailed ) ;
481
491
await this . enableNetworkInternal ( ) ;
482
492
} ) ;
483
493
} else {
@@ -751,45 +761,39 @@ export class RemoteStore implements TargetMetadataProvider {
751
761
}
752
762
753
763
private async restartNetwork ( ) : Promise < void > {
754
- this . networkEnabled = false ;
764
+ this . offlineCauses . add ( OfflineCause . ConnectivityChange ) ;
755
765
await this . disableNetworkInternal ( ) ;
756
766
this . onlineStateTracker . set ( OnlineState . Unknown ) ;
757
- await this . enableNetwork ( ) ;
767
+ this . offlineCauses . delete ( OfflineCause . ConnectivityChange ) ;
768
+ await this . enableNetworkInternal ( ) ;
758
769
}
759
770
760
771
async handleCredentialChange ( user : User ) : Promise < void > {
761
772
this . asyncQueue . verifyOperationInProgress ( ) ;
762
773
763
- if ( this . canUseNetwork ( ) ) {
764
- // Tear down and re-create our network streams. This will ensure we get a
765
- // fresh auth token for the new user and re-fill the write pipeline with
766
- // new mutations from the LocalStore (since mutations are per-user).
767
- logDebug ( LOG_TAG , 'RemoteStore restarting streams for new credential' ) ;
774
+ // Tear down and re-create our network streams. This will ensure we get a
775
+ // fresh auth token for the new user and re-fill the write pipeline with
776
+ // new mutations from the LocalStore (since mutations are per-user).
777
+ logDebug ( LOG_TAG , 'RemoteStore received new credentials' ) ;
778
+ this . offlineCauses . add ( OfflineCause . CredentialChange ) ;
768
779
769
- this . networkEnabled = false ;
770
- await this . disableNetworkInternal ( ) ;
771
- this . onlineStateTracker . set ( OnlineState . Unknown ) ;
780
+ await this . disableNetworkInternal ( ) ;
781
+ this . onlineStateTracker . set ( OnlineState . Unknown ) ;
782
+ await this . syncEngine . handleUserChange ( user ) ;
772
783
773
- await this . executeWithRecovery ( async ( ) => {
774
- await this . syncEngine . handleUserChange ( user ) ;
775
- await this . enableNetwork ( ) ;
776
- } ) ;
777
- } else {
778
- await this . executeWithRecovery ( ( ) =>
779
- this . syncEngine . handleUserChange ( user )
780
- ) ;
781
- }
784
+ this . offlineCauses . delete ( OfflineCause . CredentialChange ) ;
785
+ await this . enableNetworkInternal ( ) ;
782
786
}
783
787
784
788
/**
785
789
* Toggles the network state when the client gains or loses its primary lease.
786
790
*/
787
791
async applyPrimaryState ( isPrimary : boolean ) : Promise < void > {
788
- this . isPrimary = isPrimary ;
789
-
790
- if ( isPrimary && this . networkEnabled ) {
791
- await this . enableNetwork ( ) ;
792
+ if ( isPrimary ) {
793
+ this . offlineCauses . delete ( OfflineCause . IsSecondary ) ;
794
+ await this . enableNetworkInternal ( ) ;
792
795
} else if ( ! isPrimary ) {
796
+ this . offlineCauses . add ( OfflineCause . IsSecondary ) ;
793
797
await this . disableNetworkInternal ( ) ;
794
798
this . onlineStateTracker . set ( OnlineState . Unknown ) ;
795
799
}
0 commit comments