Skip to content

Commit b7516be

Browse files
Merge
1 parent a619433 commit b7516be

File tree

2 files changed

+41
-41
lines changed

2 files changed

+41
-41
lines changed

packages/firestore/src/remote/remote_store.ts

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,6 @@ export class RemoteStore implements TargetMetadataProvider {
132132

133133
private onlineStateTracker: OnlineStateTracker;
134134

135-
/**
136-
* A barrier to track unresolved operations that block the restart of the
137-
* write stream. This is used to remove writes from the mutation queue if the
138-
* initial removal attempt failed.
139-
*/
140-
private writeStreamBarrier = 0;
141-
142135
constructor(
143136
/**
144137
* The local store, used to fill the write pipeline with outbound mutations.
@@ -449,12 +442,13 @@ export class RemoteStore implements TargetMetadataProvider {
449442

450443
/**
451444
* Recovery logic for IndexedDB errors that takes the network offline until
452-
* IndexedDb probing succeeds. Retries are scheduled with backoff using
453-
* `enqueueRetryable()`.
445+
* `op` succeeds. Retries are scheduled with backoff using
446+
* `enqueueRetryable()`. If `op()` is not provided, IndexedDB access is
447+
* validated via a generic operation.
454448
*/
455449
private async disableNetworkUntilRecovery(
456450
e: FirestoreError,
457-
op?: () => Promise<void>
451+
op?: () => Promise<unknown>
458452
): Promise<void> {
459453
if (e.name === 'IndexedDbTransactionError') {
460454
debugAssert(
@@ -467,17 +461,17 @@ export class RemoteStore implements TargetMetadataProvider {
467461
await this.disableNetworkInternal();
468462
this.onlineStateTracker.set(OnlineState.Offline);
469463

464+
if (!op) {
465+
// Use a simple read operation to determine if IndexedDB recovered.
466+
// Ideally, we would expose a health check directly on SimpleDb, but
467+
// RemoteStore only has access to persistence through LocalStore.
468+
op = () => this.localStore.getLastRemoteSnapshotVersion();
469+
}
470+
470471
// Probe IndexedDB periodically and re-enable network
471472
this.asyncQueue.enqueueRetryable(async () => {
472473
logDebug(LOG_TAG, 'Retrying IndexedDB access');
473-
if (op) {
474-
await op();
475-
} else {
476-
// Issue a simple read operation to determine if IndexedDB recovered.
477-
// Ideally, we would expose a health check directly on SimpleDb, but
478-
// RemoteStore only has access to persistence through LocalStore.
479-
await this.localStore.getLastRemoteSnapshotVersion();
480-
}
474+
await op!();
481475
this.indexedDbFailed = false;
482476
await this.enableNetworkInternal();
483477
});
@@ -580,31 +574,32 @@ export class RemoteStore implements TargetMetadataProvider {
580574
* Starts the write stream if necessary.
581575
*/
582576
async fillWritePipeline(): Promise<void> {
583-
try {
584-
while (this.canAddToWritePipeline()) {
585-
const lastBatchIdRetrieved =
586-
this.writePipeline.length > 0
587-
? this.writePipeline[this.writePipeline.length - 1].batchId
588-
: BATCHID_UNKNOWN;
577+
while (this.canAddToWritePipeline()) {
578+
const lastBatchIdRetrieved =
579+
this.writePipeline.length > 0
580+
? this.writePipeline[this.writePipeline.length - 1].batchId
581+
: BATCHID_UNKNOWN;
589582

583+
try {
590584
const batch = await this.localStore.nextMutationBatch(
591585
lastBatchIdRetrieved
592586
);
593-
594-
if (batch) {
587+
if (batch !== null) {
595588
this.addToWritePipeline(batch);
596589
} else {
597590
break;
598591
}
592+
} catch (e) {
593+
await this.disableNetworkUntilRecovery(e);
599594
}
595+
}
600596

601-
if (this.shouldStartWriteStream()) {
602-
this.startWriteStream();
603-
} else if (this.writePipeline.length === 0) {
604-
this.writeStream.markIdle();
605-
}
606-
} catch (e) {
607-
await this.disableNetworkUntilRecovery(e);
597+
if (this.writePipeline.length === 0) {
598+
this.writeStream.markIdle();
599+
}
600+
601+
if (this.shouldStartWriteStream()) {
602+
this.startWriteStream();
608603
}
609604
}
610605

@@ -642,7 +637,6 @@ export class RemoteStore implements TargetMetadataProvider {
642637
private shouldStartWriteStream(): boolean {
643638
return (
644639
this.canUseNetwork() &&
645-
this.writeStreamBarrier === 0 &&
646640
!this.writeStream.isStarted() &&
647641
this.writePipeline.length > 0
648642
);
@@ -660,11 +654,12 @@ export class RemoteStore implements TargetMetadataProvider {
660654
this.writeStream.writeHandshake();
661655
}
662656

663-
private async onWriteHandshakeComplete(): Promise<void> {
657+
private onWriteHandshakeComplete(): Promise<void> {
664658
// Send the write pipeline now that the stream is established.
665659
for (const batch of this.writePipeline) {
666660
this.writeStream.writeMutations(batch.mutations);
667661
}
662+
return Promise.resolve();
668663
}
669664

670665
private async onMutationResult(
@@ -681,14 +676,16 @@ export class RemoteStore implements TargetMetadataProvider {
681676
const success = MutationBatchResult.from(batch, commitVersion, results);
682677
try {
683678
await this.syncEngine.applySuccessfulWrite(success);
684-
// It's possible that with the completion of this mutation another
685-
// slot has freed up.
686-
await this.fillWritePipeline();
687679
} catch (e) {
688680
await this.disableNetworkUntilRecovery(e, () =>
689681
this.syncEngine.applySuccessfulWrite(success)
690682
);
683+
return;
691684
}
685+
686+
// It's possible that with the completion of this mutation another
687+
// slot has freed up.
688+
await this.fillWritePipeline();
692689
}
693690

694691
private async onWriteStreamClose(error?: FirestoreError): Promise<void> {
@@ -730,14 +727,16 @@ export class RemoteStore implements TargetMetadataProvider {
730727

731728
try {
732729
await this.syncEngine.rejectFailedWrite(batch.batchId, error);
733-
// It's possible that with the completion of this mutation
734-
// another slot has freed up.
735-
await this.fillWritePipeline();
736730
} catch (e) {
737731
await this.disableNetworkUntilRecovery(e, () =>
738732
this.syncEngine.rejectFailedWrite(batch.batchId, error)
739733
);
734+
return;
740735
}
736+
737+
// It's possible that with the completion of this mutation
738+
// another slot has freed up.
739+
await this.fillWritePipeline();
741740
} else {
742741
// Transient error, just let the retry logic kick in.
743742
}

packages/firestore/test/unit/specs/recovery_spec.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => {
258258
// The write ack cannot be persisted and the client goes offline, which
259259
// clears all active targets, but doesn't raise a new snapshot since
260260
// the document is still marked `hasPendingWrites`.
261+
.expectEvents(query, { fromCache: true, hasPendingWrites: true })
261262
.expectActiveTargets()
262263
.recoverDatabase()
263264
.runTimer(TimerId.AsyncQueueRetry)

0 commit comments

Comments
 (0)