Skip to content

Commit 5ada17a

Browse files
committed
Allow remote updates from watch to heal a cache with synthesized deletes in it
Port of firebase/firebase-js-sdk#1015 Addresses #1548
1 parent 901b90e commit 5ada17a

File tree

1 file changed

+24
-5
lines changed

1 file changed

+24
-5
lines changed

Firestore/Source/Local/FSTLocalStore.mm

+24-5
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ - (FSTMaybeDocumentDictionary *)applyRemoteEvent:(FSTRemoteEvent *)remoteEvent {
268268
FSTListenSequenceNumber sequenceNumber = [self.listenSequence next];
269269
id<FSTQueryCache> queryCache = self.queryCache;
270270

271+
DocumentKeySet authoritativeUpdates;
271272
for (const auto &entry : remoteEvent.targetChanges) {
272273
FSTTargetID targetID = entry.first;
273274
FSTBoxedTargetID *boxedTargetID = @(targetID);
@@ -279,6 +280,21 @@ - (FSTMaybeDocumentDictionary *)applyRemoteEvent:(FSTRemoteEvent *)remoteEvent {
279280
continue;
280281
}
281282

283+
// When a global snapshot contains updates (either add or modify) we can completely trust
284+
// these updates as authoritative and blindly apply them to our cache (as a defensive measure
285+
// to promote self-healing in the unfortunate case that our cache is ever somehow corrupted /
286+
// out-of-sync).
287+
//
288+
// If the document is only updated while removing it from a target then watch isn't obligated
289+
// to send the absolute latest version: it can send the first version that caused the document
290+
// not to match.
291+
for (const DocumentKey& key : change.addedDocuments) {
292+
authoritativeUpdates = authoritativeUpdates.insert(key);
293+
}
294+
for (const DocumentKey& key : change.modifiedDocuments) {
295+
authoritativeUpdates = authoritativeUpdates.insert(key);
296+
}
297+
282298
[queryCache removeMatchingKeys:change.removedDocuments forTargetID:targetID];
283299
[queryCache addMatchingKeys:change.addedDocuments forTargetID:targetID];
284300

@@ -303,11 +319,14 @@ - (FSTMaybeDocumentDictionary *)applyRemoteEvent:(FSTRemoteEvent *)remoteEvent {
303319
FSTMaybeDocument *doc = kv.second;
304320
changedDocKeys = changedDocKeys.insert(key);
305321
FSTMaybeDocument *existingDoc = [self.remoteDocumentCache entryForKey:key];
306-
// Make sure we don't apply an old document version to the remote cache, though we
307-
// make an exception for SnapshotVersion::None() which can happen for manufactured
308-
// events (e.g. in the case of a limbo document resolution failing).
309-
if (!existingDoc || SnapshotVersion{doc.version} == SnapshotVersion::None() ||
310-
SnapshotVersion{doc.version} >= SnapshotVersion{existingDoc.version}) {
322+
323+
// If a document update isn't authoritative, make sure we don't apply an old document version
324+
// to the remote cache. We make an exception for SnapshotVersion.MIN which can happen for
325+
// manufactured events (e.g. in the case of a limbo document resolution failing).
326+
if (!existingDoc ||
327+
doc.version == SnapshotVersion::None() ||
328+
authoritativeUpdates.contains(doc.key) ||
329+
doc.version >= existingDoc.version) {
311330
[self.remoteDocumentCache addEntry:doc];
312331
} else {
313332
LOG_DEBUG(

0 commit comments

Comments
 (0)