@@ -62,6 +62,22 @@ 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
+ /** We are restarting the streams due to an Auth credential change. */
74
+ CredentialChange ,
75
+ /** The connectivity state of the environment has changed. */
76
+ ConnectivityChange ,
77
+ /** The RemoteStore has been shut down. */
78
+ Shutdown
79
+ }
80
+
65
81
/**
66
82
* RemoteStore - An interface to remotely stored data, basically providing a
67
83
* wrapper around the Datastore that is more reliable for the rest of the
@@ -118,19 +134,10 @@ export class RemoteStore implements TargetMetadataProvider {
118
134
private watchChangeAggregator : WatchChangeAggregator | null = null ;
119
135
120
136
/**
121
- * Set to true by enableNetwork() and false by disableNetwork() and indicates
122
- * the user-preferred network state .
137
+ * A set of reasons for why the RemoteStore may be offline. If empty, the
138
+ * RemoteStore may start its network connections .
123
139
*/
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.
132
- */
133
- private indexedDbFailed = false ;
140
+ private offlineCauses = new Set < OfflineCause > ( ) ;
134
141
135
142
private onlineStateTracker : OnlineStateTracker ;
136
143
@@ -194,7 +201,7 @@ export class RemoteStore implements TargetMetadataProvider {
194
201
195
202
/** Re-enables the network. Idempotent. */
196
203
enableNetwork ( ) : Promise < void > {
197
- this . networkEnabled = true ;
204
+ this . offlineCauses . delete ( OfflineCause . UserDisabled ) ;
198
205
return this . enableNetworkInternal ( ) ;
199
206
}
200
207
@@ -216,7 +223,7 @@ export class RemoteStore implements TargetMetadataProvider {
216
223
* enableNetwork().
217
224
*/
218
225
async disableNetwork ( ) : Promise < void > {
219
- this . networkEnabled = false ;
226
+ this . offlineCauses . add ( OfflineCause . UserDisabled ) ;
220
227
await this . disableNetworkInternal ( ) ;
221
228
222
229
// Set the OnlineState to Offline so get()s return from cache, etc.
@@ -240,7 +247,7 @@ export class RemoteStore implements TargetMetadataProvider {
240
247
241
248
async shutdown ( ) : Promise < void > {
242
249
logDebug ( LOG_TAG , 'RemoteStore shutting down.' ) ;
243
- this . networkEnabled = false ;
250
+ this . offlineCauses . add ( OfflineCause . Shutdown ) ;
244
251
await this . disableNetworkInternal ( ) ;
245
252
this . connectivityMonitor . shutdown ( ) ;
246
253
@@ -349,7 +356,7 @@ export class RemoteStore implements TargetMetadataProvider {
349
356
}
350
357
351
358
canUseNetwork ( ) : boolean {
352
- return ! this . indexedDbFailed && this . isPrimary && this . networkEnabled ;
359
+ return this . offlineCauses . size === 0 ;
353
360
}
354
361
355
362
private cleanUpWatchStreamState ( ) : void {
@@ -457,10 +464,10 @@ export class RemoteStore implements TargetMetadataProvider {
457
464
) : Promise < void > {
458
465
if ( isIndexedDbTransactionError ( e ) ) {
459
466
debugAssert (
460
- ! this . indexedDbFailed ,
467
+ ! this . offlineCauses . has ( OfflineCause . IndexedDbFailed ) ,
461
468
'Unexpected network event when IndexedDB was marked failed.'
462
469
) ;
463
- this . indexedDbFailed = true ;
470
+ this . offlineCauses . add ( OfflineCause . IndexedDbFailed ) ;
464
471
465
472
// Disable network and raise offline snapshots
466
473
await this . disableNetworkInternal ( ) ;
@@ -477,7 +484,7 @@ export class RemoteStore implements TargetMetadataProvider {
477
484
this . asyncQueue . enqueueRetryable ( async ( ) => {
478
485
logDebug ( LOG_TAG , 'Retrying IndexedDB access' ) ;
479
486
await op ! ( ) ;
480
- this . indexedDbFailed = false ;
487
+ this . offlineCauses . delete ( OfflineCause . IndexedDbFailed ) ;
481
488
await this . enableNetworkInternal ( ) ;
482
489
} ) ;
483
490
} else {
@@ -751,45 +758,39 @@ export class RemoteStore implements TargetMetadataProvider {
751
758
}
752
759
753
760
private async restartNetwork ( ) : Promise < void > {
754
- this . networkEnabled = false ;
761
+ this . offlineCauses . add ( OfflineCause . ConnectivityChange ) ;
755
762
await this . disableNetworkInternal ( ) ;
756
763
this . onlineStateTracker . set ( OnlineState . Unknown ) ;
757
- await this . enableNetwork ( ) ;
764
+ this . offlineCauses . delete ( OfflineCause . ConnectivityChange ) ;
765
+ await this . enableNetworkInternal ( ) ;
758
766
}
759
767
760
768
async handleCredentialChange ( user : User ) : Promise < void > {
761
769
this . asyncQueue . verifyOperationInProgress ( ) ;
762
770
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' ) ;
771
+ // Tear down and re-create our network streams. This will ensure we get a
772
+ // fresh auth token for the new user and re-fill the write pipeline with
773
+ // new mutations from the LocalStore (since mutations are per-user).
774
+ logDebug ( LOG_TAG , 'RemoteStore received new credentials' ) ;
775
+ this . offlineCauses . add ( OfflineCause . CredentialChange ) ;
768
776
769
- this . networkEnabled = false ;
770
- await this . disableNetworkInternal ( ) ;
771
- this . onlineStateTracker . set ( OnlineState . Unknown ) ;
777
+ await this . disableNetworkInternal ( ) ;
778
+ this . onlineStateTracker . set ( OnlineState . Unknown ) ;
779
+ await this . syncEngine . handleUserChange ( user ) ;
772
780
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
- }
781
+ this . offlineCauses . delete ( OfflineCause . CredentialChange ) ;
782
+ await this . enableNetworkInternal ( ) ;
782
783
}
783
784
784
785
/**
785
786
* Toggles the network state when the client gains or loses its primary lease.
786
787
*/
787
788
async applyPrimaryState ( isPrimary : boolean ) : Promise < void > {
788
- this . isPrimary = isPrimary ;
789
-
790
- if ( isPrimary && this . networkEnabled ) {
791
- await this . enableNetwork ( ) ;
789
+ if ( isPrimary ) {
790
+ this . offlineCauses . delete ( OfflineCause . IsSecondary ) ;
791
+ await this . enableNetworkInternal ( ) ;
792
792
} else if ( ! isPrimary ) {
793
+ this . offlineCauses . add ( OfflineCause . IsSecondary ) ;
793
794
await this . disableNetworkInternal ( ) ;
794
795
this . onlineStateTracker . set ( OnlineState . Unknown ) ;
795
796
}
0 commit comments