Skip to content

Commit 61fcec3

Browse files
milaGGLtom-andersen
authored andcommitted
Fix multi-tab snapshot listener metadata sync issue (#8339)
* Update sync_engine_impl.ts * add spec test * update comment * typo * update spec test * add changeset
1 parent d703548 commit 61fcec3

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed

.changeset/flat-scissors-suffer.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@firebase/firestore': patch
3+
'firebase': patch
4+
---
5+
6+
Fix persistence multi-tab snapshot listener metadata sync issue.

packages/firestore/src/core/sync_engine_impl.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1095,7 +1095,13 @@ export async function syncEngineEmitNewSnapsAndNotifyLocalStore(
10951095
// secondary clients to update query state.
10961096
if (viewSnapshot || remoteEvent) {
10971097
if (syncEngineImpl.isPrimaryClient) {
1098-
const isCurrent = viewSnapshot && !viewSnapshot.fromCache;
1098+
// Query state is set to `current` if:
1099+
// - There is a view change and it is up-to-date, or,
1100+
// - There is a global snapshot, the Target is current, and no changes to be resolved
1101+
const isCurrent = viewSnapshot
1102+
? !viewSnapshot.fromCache
1103+
: remoteEvent?.targetChanges.get(queryView.targetId)?.current;
1104+
10991105
syncEngineImpl.sharedClientState.updateQueryState(
11001106
queryView.targetId,
11011107
isCurrent ? 'current' : 'not-current'

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,4 +1895,40 @@ describeSpec('Listens:', [], () => {
18951895
.restoreListen(query1, 'resume-token-1000', /* expectedCount= */ 1);
18961896
}
18971897
);
1898+
1899+
specTest(
1900+
'Global snapshots would not alter query state if there is no changes',
1901+
['multi-client'],
1902+
() => {
1903+
const query1 = query('collection');
1904+
const docA = doc('collection/a', 1000, { key: 'a' });
1905+
return (
1906+
client(0)
1907+
.becomeVisible()
1908+
.expectPrimaryState(true)
1909+
// Populate the cache first
1910+
.userListens(query1)
1911+
.watchAcksFull(query1, 1000, docA)
1912+
.expectEvents(query1, { added: [docA] })
1913+
.userUnlistens(query1)
1914+
.watchRemoves(query1)
1915+
// Listen to the query in the primary client
1916+
.userListens(query1, { resumeToken: 'resume-token-1000' })
1917+
.expectEvents(query1, {
1918+
added: [docA],
1919+
fromCache: true
1920+
})
1921+
.watchAcksFull(query1, 2000, docA)
1922+
.expectEvents(query1, { fromCache: false })
1923+
// Reproduces: https://github.com/firebase/firebase-js-sdk/issues/8314
1924+
// Watch could send a global snapshot from time to time. If there are no view changes,
1925+
// the query should not be marked as "not-current" as the Target is up to date.
1926+
.watchSnapshots(3000, [], 'resume-token-3000')
1927+
// Listen to the query in the secondary tab. The snapshot is up to date.
1928+
.client(1)
1929+
.userListens(query1)
1930+
.expectEvents(query1, { added: [docA], fromCache: false })
1931+
);
1932+
}
1933+
);
18981934
});

0 commit comments

Comments
 (0)