Skip to content

Commit 7737f6e

Browse files
Merge 67070f1 into 1325b58
2 parents 1325b58 + 67070f1 commit 7737f6e

File tree

4 files changed

+53
-8
lines changed

4 files changed

+53
-8
lines changed

packages/firestore/src/core/firestore_client.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ export class FirestoreClient {
178178
persistenceResult
179179
).then(initializationDone.resolve, initializationDone.reject);
180180
} else {
181-
this.asyncQueue.enqueueAndForget(() => {
181+
this.asyncQueue.enqueueRetryable(() => {
182182
return this.handleCredentialChange(user);
183183
});
184184
}

packages/firestore/src/core/sync_engine.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -860,15 +860,15 @@ export class SyncEngine implements RemoteSyncer {
860860

861861
async handleCredentialChange(user: User): Promise<void> {
862862
const userChanged = !this.currentUser.isEqual(user);
863-
this.currentUser = user;
864863

865864
if (userChanged) {
865+
const result = await this.localStore.handleUserChange(user);
866+
this.currentUser = user;
867+
866868
// Fails tasks waiting for pending writes requested by previous user.
867869
this.rejectOutstandingPendingWritesCallbacks(
868870
"'waitForPendingWrites' promise is rejected due to a user change."
869871
);
870-
871-
const result = await this.localStore.handleUserChange(user);
872872
// TODO(b/114226417): Consider calling this only in the primary tab.
873873
this.sharedClientState.handleUserChange(
874874
user,

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

+37
Original file line numberDiff line numberDiff line change
@@ -484,4 +484,41 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => {
484484
removed: [doc1]
485485
});
486486
});
487+
488+
specTest(
489+
'User change handles transaction failures (with recovery)',
490+
['durable-persistence'],
491+
() => {
492+
const query = Query.atPath(path('collection'));
493+
const doc1 = doc(
494+
'collection/key1',
495+
0,
496+
{ foo: 'a' },
497+
{ hasLocalMutations: true }
498+
);
499+
return spec()
500+
.changeUser('user1')
501+
.userSets('collection/key1', { foo: 'a' })
502+
.userListens(query)
503+
.expectEvents(query, {
504+
added: [doc1],
505+
fromCache: true,
506+
hasPendingWrites: true
507+
})
508+
.failDatabaseTransactions({ 'Handle user change': true })
509+
.changeUser('user2')
510+
.recoverDatabase()
511+
.runTimer(TimerId.AsyncQueueRetry)
512+
.expectEvents(query, { removed: [doc1], fromCache: true })
513+
.failDatabaseTransactions({ 'Handle user change': true })
514+
.changeUser('user1')
515+
.recoverDatabase()
516+
.runTimer(TimerId.AsyncQueueRetry)
517+
.expectEvents(query, {
518+
added: [doc1],
519+
fromCache: true,
520+
hasPendingWrites: true
521+
});
522+
}
523+
);
487524
});

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

+12-4
Original file line numberDiff line numberDiff line change
@@ -710,11 +710,19 @@ abstract class TestRunner {
710710
return Promise.resolve();
711711
}
712712

713-
private doChangeUser(user: string | null): Promise<void> {
713+
private async doChangeUser(user: string | null): Promise<void> {
714714
this.user = new User(user);
715-
return this.queue.enqueue(() =>
716-
this.syncEngine.handleCredentialChange(this.user)
717-
);
715+
const deferred = new Deferred<void>();
716+
await this.queue.enqueueRetryable(async () => {
717+
try {
718+
await this.syncEngine.handleCredentialChange(this.user);
719+
} finally {
720+
// Resolve the deferred Promise even if the operation failed. This allows
721+
// the spec tests to manually retry the failed user change.
722+
deferred.resolve();
723+
}
724+
});
725+
return deferred.promise;
718726
}
719727

720728
private async doFailDatabase(

0 commit comments

Comments
 (0)