@@ -199,7 +199,11 @@ export class RemoteStore implements TargetMetadataProvider {
199
199
200
200
private async enableNetworkInternal ( ) : Promise < void > {
201
201
if ( this . canUseNetwork ( ) ) {
202
- this . writeStream . lastStreamToken = await this . localStore . getLastStreamToken ( ) ;
202
+ try {
203
+ this . writeStream . lastStreamToken = await this . localStore . getLastStreamToken ( ) ;
204
+ } catch ( e ) {
205
+ return this . disableNetworkUntilRecovery ( e ) ;
206
+ }
203
207
204
208
if ( this . shouldStartWatchStream ( ) ) {
205
209
this . startWatchStream ( ) ;
@@ -445,10 +449,14 @@ export class RemoteStore implements TargetMetadataProvider {
445
449
446
450
/**
447
451
* Recovery logic for IndexedDB errors that takes the network offline until
448
- * IndexedDb probing succeeds. Retries are scheduled with backoff using
449
- * `enqueueRetryable()`.
452
+ * `op` succeeds. Retries are scheduled with backoff using
453
+ * `enqueueRetryable()`. If `op()` is not provided, IndexedDB access is
454
+ * validated via a generic operation.
450
455
*/
451
- private async disableNetworkUntilRecovery ( e : FirestoreError ) : Promise < void > {
456
+ private async disableNetworkUntilRecovery (
457
+ e : FirestoreError ,
458
+ op ?: ( ) => Promise < unknown >
459
+ ) : Promise < void > {
452
460
if ( isIndexedDbTransactionError ( e ) ) {
453
461
debugAssert (
454
462
! this . indexedDbFailed ,
@@ -460,13 +468,17 @@ export class RemoteStore implements TargetMetadataProvider {
460
468
await this . disableNetworkInternal ( ) ;
461
469
this . onlineStateTracker . set ( OnlineState . Offline ) ;
462
470
471
+ if ( ! op ) {
472
+ // Use a simple read operation to determine if IndexedDB recovered.
473
+ // Ideally, we would expose a health check directly on SimpleDb, but
474
+ // RemoteStore only has access to persistence through LocalStore.
475
+ op = ( ) => this . localStore . getLastRemoteSnapshotVersion ( ) ;
476
+ }
477
+
463
478
// Probe IndexedDB periodically and re-enable network
464
479
this . asyncQueue . enqueueRetryable ( async ( ) => {
465
480
logDebug ( LOG_TAG , 'Retrying IndexedDB access' ) ;
466
- // Issue a simple read operation to determine if IndexedDB recovered.
467
- // Ideally, we would expose a health check directly on SimpleDb, but
468
- // RemoteStore only has access to persistence through LocalStore.
469
- await this . localStore . getLastRemoteSnapshotVersion ( ) ;
481
+ await op ! ( ) ;
470
482
this . indexedDbFailed = false ;
471
483
await this . enableNetworkInternal ( ) ;
472
484
} ) ;
@@ -569,25 +581,30 @@ export class RemoteStore implements TargetMetadataProvider {
569
581
* Starts the write stream if necessary.
570
582
*/
571
583
async fillWritePipeline ( ) : Promise < void > {
572
- if ( this . canAddToWritePipeline ( ) ) {
584
+ while ( this . canAddToWritePipeline ( ) ) {
573
585
const lastBatchIdRetrieved =
574
586
this . writePipeline . length > 0
575
587
? this . writePipeline [ this . writePipeline . length - 1 ] . batchId
576
588
: BATCHID_UNKNOWN ;
577
- const batch = await this . localStore . nextMutationBatch (
578
- lastBatchIdRetrieved
579
- ) ;
580
589
581
- if ( batch === null ) {
582
- if ( this . writePipeline . length === 0 ) {
583
- this . writeStream . markIdle ( ) ;
590
+ try {
591
+ const batch = await this . localStore . nextMutationBatch (
592
+ lastBatchIdRetrieved
593
+ ) ;
594
+ if ( batch !== null ) {
595
+ this . addToWritePipeline ( batch ) ;
596
+ } else {
597
+ break ;
584
598
}
585
- } else {
586
- this . addToWritePipeline ( batch ) ;
587
- await this . fillWritePipeline ( ) ;
599
+ } catch ( e ) {
600
+ await this . disableNetworkUntilRecovery ( e ) ;
588
601
}
589
602
}
590
603
604
+ if ( this . writePipeline . length === 0 ) {
605
+ this . writeStream . markIdle ( ) ;
606
+ }
607
+
591
608
if ( this . shouldStartWriteStream ( ) ) {
592
609
this . startWriteStream ( ) ;
593
610
}
@@ -654,10 +671,15 @@ export class RemoteStore implements TargetMetadataProvider {
654
671
this . writeStream . writeMutations ( batch . mutations ) ;
655
672
}
656
673
} )
657
- . catch ( ignoreIfPrimaryLeaseLoss ) ;
674
+ . catch ( ignoreIfPrimaryLeaseLoss )
675
+ . catch ( e =>
676
+ this . disableNetworkUntilRecovery ( e , ( ) =>
677
+ this . localStore . setLastStreamToken ( this . writeStream . lastStreamToken )
678
+ )
679
+ ) ;
658
680
}
659
681
660
- private onMutationResult (
682
+ private async onMutationResult (
661
683
commitVersion : SnapshotVersion ,
662
684
results : MutationResult [ ]
663
685
) : Promise < void > {
@@ -674,11 +696,18 @@ export class RemoteStore implements TargetMetadataProvider {
674
696
results ,
675
697
this . writeStream . lastStreamToken
676
698
) ;
677
- return this . syncEngine . applySuccessfulWrite ( success ) . then ( ( ) => {
678
- // It's possible that with the completion of this mutation another
679
- // slot has freed up.
680
- return this . fillWritePipeline ( ) ;
681
- } ) ;
699
+ try {
700
+ await this . syncEngine . applySuccessfulWrite ( success ) ;
701
+ } catch ( e ) {
702
+ await this . disableNetworkUntilRecovery ( e , ( ) =>
703
+ this . syncEngine . applySuccessfulWrite ( success )
704
+ ) ;
705
+ return ;
706
+ }
707
+
708
+ // It's possible that with the completion of this mutation another
709
+ // slot has freed up.
710
+ await this . fillWritePipeline ( ) ;
682
711
}
683
712
684
713
private async onWriteStreamClose ( error ?: FirestoreError ) : Promise < void > {
@@ -727,7 +756,12 @@ export class RemoteStore implements TargetMetadataProvider {
727
756
728
757
return this . localStore
729
758
. setLastStreamToken ( ByteString . EMPTY_BYTE_STRING )
730
- . catch ( ignoreIfPrimaryLeaseLoss ) ;
759
+ . catch ( ignoreIfPrimaryLeaseLoss )
760
+ . catch ( e =>
761
+ this . disableNetworkUntilRecovery ( e , ( ) =>
762
+ this . localStore . setLastStreamToken ( ByteString . EMPTY_BYTE_STRING )
763
+ )
764
+ ) ;
731
765
} else {
732
766
// Some other error, don't reset stream token. Our stream logic will
733
767
// just retry with exponential backoff.
@@ -747,13 +781,18 @@ export class RemoteStore implements TargetMetadataProvider {
747
781
// restart.
748
782
this . writeStream . inhibitBackoff ( ) ;
749
783
750
- return this . syncEngine
751
- . rejectFailedWrite ( batch . batchId , error )
752
- . then ( ( ) => {
753
- // It's possible that with the completion of this mutation
754
- // another slot has freed up.
755
- return this . fillWritePipeline ( ) ;
756
- } ) ;
784
+ try {
785
+ await this . syncEngine . rejectFailedWrite ( batch . batchId , error ) ;
786
+ } catch ( e ) {
787
+ await this . disableNetworkUntilRecovery ( e , ( ) =>
788
+ this . syncEngine . rejectFailedWrite ( batch . batchId , error )
789
+ ) ;
790
+ return ;
791
+ }
792
+
793
+ // It's possible that with the completion of this mutation
794
+ // another slot has freed up.
795
+ await this . fillWritePipeline ( ) ;
757
796
} else {
758
797
// Transient error, just let the retry logic kick in.
759
798
}
0 commit comments