Skip to content

Commit a7255a2

Browse files
authored
Fix b/249494921: Secondary Tab time travels with document deletes (#6635)
1 parent fdd4ab4 commit a7255a2

File tree

2 files changed

+62
-2
lines changed

2 files changed

+62
-2
lines changed

packages/firestore/src/core/sync_engine_impl.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,13 +1021,19 @@ export async function syncEngineEmitNewSnapsAndNotifyLocalStore(
10211021
syncEngineImpl
10221022
.applyDocChanges(queryView, changes, remoteEvent)
10231023
.then(viewSnapshot => {
1024-
if (viewSnapshot) {
1024+
// If there are changes, or we are handling a global snapshot, notify
1025+
// secondary clients to update query state.
1026+
if (viewSnapshot || remoteEvent) {
10251027
if (syncEngineImpl.isPrimaryClient) {
10261028
syncEngineImpl.sharedClientState.updateQueryState(
10271029
queryView.targetId,
1028-
viewSnapshot.fromCache ? 'not-current' : 'current'
1030+
viewSnapshot?.fromCache ? 'not-current' : 'current'
10291031
);
10301032
}
1033+
}
1034+
1035+
// Update views if there are actual changes.
1036+
if (!!viewSnapshot) {
10311037
newSnaps.push(viewSnapshot);
10321038
const docChanges = LocalViewChanges.fromSnapshot(
10331039
queryView.targetId,

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,60 @@ describeSpec('Listens:', [], () => {
950950
}
951951
);
952952

953+
// Reproduces b/249494921.
954+
specTest(
955+
'Secondary client advances query state with global snapshot from primary',
956+
['multi-client'],
957+
() => {
958+
const query1 = query('collection');
959+
const docA = doc('collection/a', 1000, { key: '1' });
960+
const docADeleted = deletedDoc('collection/a', 2000);
961+
const docARecreated = doc('collection/a', 2000, {
962+
key: '2'
963+
}).setHasLocalMutations();
964+
965+
return (
966+
client(0)
967+
.becomeVisible()
968+
.expectPrimaryState(true)
969+
.userListens(query1)
970+
.watchAcksFull(query1, 1000, docA)
971+
.expectEvents(query1, { added: [docA] })
972+
.userUnlistens(query1)
973+
.watchRemoves(query1)
974+
.client(1)
975+
.userListens(query1)
976+
.expectEvents(query1, { added: [docA], fromCache: true })
977+
.client(0)
978+
.expectListen(query1, { resumeToken: 'resume-token-1000' })
979+
.watchAcksFull(query1, 1500, docA)
980+
.client(1)
981+
.expectEvents(query1, {})
982+
.client(0)
983+
.userDeletes('collection/a')
984+
.client(1)
985+
.expectEvents(query1, {
986+
removed: [docA]
987+
})
988+
.client(0)
989+
.writeAcks('collection/a', 2000)
990+
.watchAcksFull(query1, 2000, docADeleted)
991+
.client(1) // expects no event
992+
.client(0)
993+
.userSets('collection/a', { key: '2' })
994+
.client(1)
995+
// Without the fix for b/249494921, two snapshots will be raised: a first
996+
// one as show below, and a second one with `docADeleted` because
997+
// `client(1)` failed to advance its cursor in remote document cache, and
998+
// read a stale document.
999+
.expectEvents(query1, {
1000+
added: [docARecreated],
1001+
hasPendingWrites: true
1002+
})
1003+
);
1004+
}
1005+
);
1006+
9531007
specTest(
9541008
'Mirror queries from same secondary client',
9551009
['multi-client'],

0 commit comments

Comments
 (0)