Skip to content

Commit 115a17e

Browse files
Take RemoteStore offline during user change
1 parent 1e3721c commit 115a17e

File tree

6 files changed

+67
-52
lines changed

6 files changed

+67
-52
lines changed

packages/firestore/src/core/firestore_client.ts

+3-10
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,9 @@ export class FirestoreClient {
179179
persistenceResult
180180
).then(initializationDone.resolve, initializationDone.reject);
181181
} else {
182-
this.asyncQueue.enqueueRetryable(() => {
183-
return this.handleCredentialChange(user);
184-
});
182+
this.asyncQueue.enqueueAndForget(() =>
183+
this.remoteStore.handleCredentialChange(user)
184+
);
185185
}
186186
});
187187

@@ -339,13 +339,6 @@ export class FirestoreClient {
339339
}
340340
}
341341

342-
private handleCredentialChange(user: User): Promise<void> {
343-
this.asyncQueue.verifyOperationInProgress();
344-
345-
logDebug(LOG_TAG, 'Credential Changed. Current user: ' + user.uid);
346-
return this.syncEngine.handleCredentialChange(user);
347-
}
348-
349342
/** Disables the network connection. Pending operations will not complete. */
350343
disableNetwork(): Promise<void> {
351344
this.verifyNotTerminated();

packages/firestore/src/core/sync_engine.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -860,10 +860,12 @@ export class SyncEngine implements RemoteSyncer {
860860
);
861861
}
862862

863-
async handleCredentialChange(user: User): Promise<void> {
863+
async handleUserChange(user: User): Promise<void> {
864864
const userChanged = !this.currentUser.isEqual(user);
865865

866866
if (userChanged) {
867+
logDebug(LOG_TAG, 'Handling user change. New user:', user.toKey());
868+
867869
const result = await this.localStore.handleUserChange(user);
868870
this.currentUser = user;
869871

@@ -879,8 +881,6 @@ export class SyncEngine implements RemoteSyncer {
879881
);
880882
await this.emitNewSnapsAndNotifyLocalStore(result.affectedDocuments);
881883
}
882-
883-
await this.remoteStore.handleCredentialChange();
884884
}
885885

886886
enableNetwork(): Promise<void> {

packages/firestore/src/remote/remote_store.ts

+20-5
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import {
5555
} from './watch_change';
5656
import { ByteString } from '../util/byte_string';
5757
import { isIndexedDbTransactionError } from '../local/simple_db';
58+
import { User } from '../auth/user';
5859

5960
const LOG_TAG = 'RemoteStore';
6061

@@ -756,13 +757,27 @@ export class RemoteStore implements TargetMetadataProvider {
756757
await this.enableNetwork();
757758
}
758759

759-
async handleCredentialChange(): Promise<void> {
760+
async handleCredentialChange(user: User): Promise<void> {
761+
this.asyncQueue.verifyOperationInProgress();
762+
760763
if (this.canUseNetwork()) {
761-
// Tear down and re-create our network streams. This will ensure we get a fresh auth token
762-
// for the new user and re-fill the write pipeline with new mutations from the LocalStore
763-
// (since mutations are per-user).
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).
764767
logDebug(LOG_TAG, 'RemoteStore restarting streams for new credential');
765-
await this.restartNetwork();
768+
769+
this.networkEnabled = false;
770+
await this.disableNetworkInternal();
771+
this.onlineStateTracker.set(OnlineState.Unknown);
772+
773+
await this.executeWithRecovery(async () => {
774+
await this.syncEngine.handleUserChange(user);
775+
this.networkEnabled = true;
776+
});
777+
} else {
778+
await this.executeWithRecovery(() =>
779+
this.syncEngine.handleUserChange(user)
780+
);
766781
}
767782
}
768783

packages/firestore/src/remote/remote_syncer.ts

+7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { DocumentKeySet } from '../model/collections';
2020
import { MutationBatchResult } from '../model/mutation_batch';
2121
import { FirestoreError } from '../util/error';
2222
import { RemoteEvent } from './remote_event';
23+
import { User } from '../auth/user';
2324

2425
/**
2526
* An interface that describes the actions the RemoteStore needs to perform on
@@ -65,4 +66,10 @@ export interface RemoteSyncer {
6566
* the last snapshot.
6667
*/
6768
getRemoteKeysForTarget(targetId: TargetId): DocumentKeySet;
69+
70+
/**
71+
* Updates all local state to match the pending mutations for the given user.
72+
* May be called repeatedly for the same user.
73+
*/
74+
handleUserChange(user: User): Promise<void>;
6875
}

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

+31-23
Original file line numberDiff line numberDiff line change
@@ -721,29 +721,37 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => {
721721
{ foo: 'a' },
722722
{ hasLocalMutations: true }
723723
);
724-
return spec()
725-
.changeUser('user1')
726-
.userSets('collection/key1', { foo: 'a' })
727-
.userListens(query)
728-
.expectEvents(query, {
729-
added: [doc1],
730-
fromCache: true,
731-
hasPendingWrites: true
732-
})
733-
.failDatabaseTransactions('Handle user change')
734-
.changeUser('user2')
735-
.recoverDatabase()
736-
.runTimer(TimerId.AsyncQueueRetry)
737-
.expectEvents(query, { removed: [doc1], fromCache: true })
738-
.failDatabaseTransactions('Handle user change')
739-
.changeUser('user1')
740-
.recoverDatabase()
741-
.runTimer(TimerId.AsyncQueueRetry)
742-
.expectEvents(query, {
743-
added: [doc1],
744-
fromCache: true,
745-
hasPendingWrites: true
746-
});
724+
return (
725+
spec()
726+
.changeUser('user1')
727+
.userSets('collection/key1', { foo: 'a' })
728+
.userListens(query)
729+
.expectEvents(query, {
730+
added: [doc1],
731+
fromCache: true,
732+
hasPendingWrites: true
733+
})
734+
.failDatabaseTransactions('Handle user change')
735+
.changeUser('user2')
736+
// The network is offline due to the failed user change
737+
.expectActiveTargets()
738+
.recoverDatabase()
739+
.runTimer(TimerId.AsyncQueueRetry)
740+
.expectActiveTargets({ query })
741+
.expectEvents(query, { removed: [doc1], fromCache: true })
742+
.failDatabaseTransactions('Handle user change')
743+
.changeUser('user1')
744+
// The network is offline due to the failed user change
745+
.expectActiveTargets()
746+
.recoverDatabase()
747+
.runTimer(TimerId.AsyncQueueRetry)
748+
.expectActiveTargets({ query })
749+
.expectEvents(query, {
750+
added: [doc1],
751+
fromCache: true,
752+
hasPendingWrites: true
753+
})
754+
);
747755
}
748756
);
749757
});

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

+3-11
Original file line numberDiff line numberDiff line change
@@ -716,17 +716,9 @@ abstract class TestRunner {
716716

717717
private async doChangeUser(user: string | null): Promise<void> {
718718
this.user = new User(user);
719-
const deferred = new Deferred<void>();
720-
await this.queue.enqueueRetryable(async () => {
721-
try {
722-
await this.syncEngine.handleCredentialChange(this.user);
723-
} finally {
724-
// Resolve the deferred Promise even if the operation failed. This allows
725-
// the spec tests to manually retry the failed user change.
726-
deferred.resolve();
727-
}
728-
});
729-
return deferred.promise;
719+
return this.queue.enqueue(() =>
720+
this.remoteStore.handleCredentialChange(this.user)
721+
);
730722
}
731723

732724
private async doFailDatabase(

0 commit comments

Comments
 (0)