Skip to content

Commit d37cf9f

Browse files
committed
perf: merge pending snapshots into 1 snapshot to prevent bounced updateds from being re-applied, even if synchronously
1 parent c4d4b8e commit d37cf9f

File tree

1 file changed

+32
-21
lines changed
  • packages/plugin-firestore/src/actions

1 file changed

+32
-21
lines changed

packages/plugin-firestore/src/actions/stream.ts

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,25 +103,6 @@ export function streamActionFactory(
103103
const pendingSnapshots: QuerySnapshot[] = []
104104
let isProcessing = false
105105

106-
const processDocChanges = (querySnapshot: QuerySnapshot) => {
107-
querySnapshot
108-
.docChanges()
109-
.forEach((docChange: DocumentChange<{ [key: string]: unknown }>) => {
110-
const docSnapshot = docChange.doc
111-
const docData = docSnapshot.data()
112-
const docMetadata = docSnapshotToDocMetadata(docSnapshot)
113-
if (docChange.type === 'added' && docData) {
114-
added(docData, docMetadata)
115-
}
116-
if (docChange.type === 'modified' && docData) {
117-
modified(docData, docMetadata)
118-
}
119-
if (docChange.type === 'removed') {
120-
removed(docData, docMetadata)
121-
}
122-
})
123-
}
124-
125106
closeStream = onSnapshot(
126107
query,
127108
async (querySnapshot: QuerySnapshot) => {
@@ -142,13 +123,43 @@ export function streamActionFactory(
142123
await collectionWriteLock.promise
143124
}
144125

145-
// Process all pending snapshots synchronously
126+
// Merge all pending snapshots into a single map of docId -> final state
127+
const mergedDocs = new Map<
128+
string,
129+
{
130+
type: 'added' | 'modified' | 'removed'
131+
docData: { [key: string]: unknown }
132+
docMetadata: Parameters<typeof added>[1]
133+
}
134+
>()
135+
146136
let snapshot = pendingSnapshots.shift()
147137
while (snapshot) {
148-
processDocChanges(snapshot)
138+
snapshot
139+
.docChanges()
140+
.forEach((docChange: DocumentChange<{ [key: string]: unknown }>) => {
141+
const docSnapshot = docChange.doc
142+
const docData = docSnapshot.data()
143+
const docMetadata = docSnapshotToDocMetadata(docSnapshot)
144+
// Always overwrite with the latest state for this doc
145+
mergedDocs.set(docSnapshot.id, { type: docChange.type, docData, docMetadata })
146+
})
149147
snapshot = pendingSnapshots.shift()
150148
}
151149

150+
// Process the merged final states
151+
for (const { type, docData, docMetadata } of mergedDocs.values()) {
152+
if (type === 'added' && docData) {
153+
added(docData, docMetadata)
154+
}
155+
if (type === 'modified' && docData) {
156+
modified(docData, docMetadata)
157+
}
158+
if (type === 'removed') {
159+
removed(docData, docMetadata)
160+
}
161+
}
162+
152163
// Call onFirstData after processing (after write lock, whether collection has docs or not)
153164
if (!firstDataReceived && onFirstData) {
154165
firstDataReceived = true

0 commit comments

Comments
 (0)