Skip to content

Commit c544bdc

Browse files
Remove held back handling from LocalStore (#34)
1 parent 9a30621 commit c544bdc

File tree

3 files changed

+35
-140
lines changed

3 files changed

+35
-140
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/core/Transaction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
package com.google.firebase.firestore.core;
1616

17+
import static com.google.firebase.firestore.util.Assert.fail;
18+
1719
import com.google.android.gms.tasks.Task;
1820
import com.google.android.gms.tasks.Tasks;
1921
import com.google.firebase.firestore.FirebaseFirestoreException;
@@ -41,8 +43,6 @@
4143
import java.util.concurrent.TimeUnit;
4244
import javax.annotation.Nullable;
4345

44-
import static com.google.firebase.firestore.util.Assert.fail;
45-
4646
/**
4747
* Internal transaction object responsible for accumulating the mutations to perform and the base
4848
* versions for any documents read.

firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalStore.java

Lines changed: 10 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import static com.google.firebase.firestore.util.Assert.hardAssert;
1818
import static java.util.Arrays.asList;
19-
import static java.util.Collections.singletonList;
2019

2120
import android.util.SparseArray;
2221
import com.google.firebase.Timestamp;
@@ -36,8 +35,6 @@
3635
import com.google.firebase.firestore.remote.TargetChange;
3736
import com.google.firebase.firestore.util.Logger;
3837
import com.google.protobuf.ByteString;
39-
import java.util.ArrayList;
40-
import java.util.Collections;
4138
import java.util.HashSet;
4239
import java.util.List;
4340
import java.util.Map;
@@ -122,17 +119,6 @@ public final class LocalStore {
122119
/** Used to generate targetIds for queries tracked locally. */
123120
private final TargetIdGenerator targetIdGenerator;
124121

125-
/**
126-
* A heldBatchResult is a mutation batch result (from a write acknowledgement) that arrived before
127-
* the watch stream got notified of a snapshot that includes the write. So we "hold" it until the
128-
* watch stream catches up. It ensures that the local write remains visible (latency compensation)
129-
* and doesn't temporarily appear reverted because the watch stream is slower than the write
130-
* stream and so wasn't reflecting it.
131-
*
132-
* <p>NOTE: Eventually we want to move this functionality into the remote store.
133-
*/
134-
private final List<MutationBatchResult> heldBatchResults;
135-
136122
public LocalStore(Persistence persistence, User initialUser) {
137123
hardAssert(
138124
persistence.isStarted(), "LocalStore was passed an unstarted persistence implementation");
@@ -149,39 +135,10 @@ public LocalStore(Persistence persistence, User initialUser) {
149135
persistence.getReferenceDelegate().setAdditionalReferences(localViewReferences);
150136

151137
targetIds = new SparseArray<>();
152-
heldBatchResults = new ArrayList<>();
153138
}
154139

155140
public void start() {
156-
startMutationQueue();
157-
}
158-
159-
private void startMutationQueue() {
160-
persistence.runTransaction(
161-
"Start MutationQueue",
162-
() -> {
163-
mutationQueue.start();
164-
165-
// If we have any leftover mutation batch results from a prior run, just drop them.
166-
// TODO: We may need to repopulate heldBatchResults or similar instead,
167-
// but that is not straightforward since we're not persisting the write ack versions.
168-
heldBatchResults.clear();
169-
170-
// TODO: This is the only usage of getAllMutationBatchesThroughBatchId().
171-
// Consider removing it in favor of a getAcknowledgedBatches method.
172-
int highestAck = mutationQueue.getHighestAcknowledgedBatchId();
173-
if (highestAck != MutationBatch.UNKNOWN) {
174-
List<MutationBatch> batches =
175-
mutationQueue.getAllMutationBatchesThroughBatchId(highestAck);
176-
if (!batches.isEmpty()) {
177-
// NOTE: This could be more efficient if we had a removeBatchesThroughBatchID, but
178-
// this set should be very small and this code should go away eventually.
179-
for (MutationBatch batch : batches) {
180-
mutationQueue.removeMutationBatch(batch);
181-
}
182-
}
183-
}
184-
});
141+
mutationQueue.start();
185142
}
186143

187144
// PORTING NOTE: no shutdown for LocalStore or persistence components on Android.
@@ -191,7 +148,7 @@ public ImmutableSortedMap<DocumentKey, MaybeDocument> handleUserChange(User user
191148
List<MutationBatch> oldBatches = mutationQueue.getAllMutationBatches();
192149

193150
mutationQueue = persistence.getMutationQueue(user);
194-
startMutationQueue();
151+
mutationQueue.start();
195152

196153
List<MutationBatch> newBatches = mutationQueue.getAllMutationBatches();
197154

@@ -249,18 +206,11 @@ public ImmutableSortedMap<DocumentKey, MaybeDocument> acknowledgeBatch(
249206
return persistence.runTransaction(
250207
"Acknowledge batch",
251208
() -> {
252-
mutationQueue.acknowledgeBatch(batchResult.getBatch(), batchResult.getStreamToken());
253-
254-
Set<DocumentKey> affected;
255-
if (shouldHoldBatchResult(batchResult.getCommitVersion())) {
256-
heldBatchResults.add(batchResult);
257-
affected = Collections.emptySet();
258-
} else {
259-
affected = releaseBatchResults(singletonList(batchResult));
260-
}
261-
209+
MutationBatch batch = batchResult.getBatch();
210+
mutationQueue.acknowledgeBatch(batch, batchResult.getStreamToken());
211+
applyBatchResult(batchResult);
262212
mutationQueue.performConsistencyCheck();
263-
return localDocuments.getDocuments(affected);
213+
return localDocuments.getDocuments(batch.getKeys());
264214
});
265215
}
266216

@@ -282,9 +232,9 @@ public ImmutableSortedMap<DocumentKey, MaybeDocument> rejectBatch(int batchId) {
282232
int lastAcked = mutationQueue.getHighestAcknowledgedBatchId();
283233
hardAssert(batchId > lastAcked, "Acknowledged batches can't be rejected.");
284234

285-
Set<DocumentKey> affectedKeys = removeMutationBatch(toReject);
235+
mutationQueue.removeMutationBatch(toReject);
286236
mutationQueue.performConsistencyCheck();
287-
return localDocuments.getDocuments(affectedKeys);
237+
return localDocuments.getDocuments(toReject.getKeys());
288238
});
289239
}
290240

@@ -421,10 +371,6 @@ public ImmutableSortedMap<DocumentKey, MaybeDocument> applyRemoteEvent(RemoteEve
421371
queryCache.setLastRemoteSnapshotVersion(remoteVersion);
422372
}
423373

424-
Set<DocumentKey> releasedWriteKeys = releaseHeldBatchResults();
425-
426-
// Union the two key sets.
427-
changedDocKeys.addAll(releasedWriteKeys);
428374
return localDocuments.getDocuments(changedDocKeys);
429375
});
430376
}
@@ -563,12 +509,6 @@ public void releaseQuery(Query query) {
563509
localViewReferences.removeReferencesForId(queryData.getTargetId());
564510
persistence.getReferenceDelegate().removeTarget(queryData);
565511
targetIds.remove(queryData.getTargetId());
566-
567-
// If this was the last watch target, then we won't get any more watch snapshots, so we
568-
// should release any held batch results.
569-
if (targetIds.size() == 0) {
570-
releaseHeldBatchResults();
571-
}
572512
});
573513
}
574514

@@ -585,70 +525,6 @@ public ImmutableSortedSet<DocumentKey> getRemoteDocumentKeys(int targetId) {
585525
return queryCache.getMatchingKeysForTargetId(targetId);
586526
}
587527

588-
/**
589-
* Releases all the held batch results up to the current remote version received, and applies
590-
* their mutations to the docs in the remote documents cache.
591-
*
592-
* @return the set of keys of docs that were modified by those writes.
593-
*/
594-
private Set<DocumentKey> releaseHeldBatchResults() {
595-
ArrayList<MutationBatchResult> toRelease = new ArrayList<>();
596-
for (MutationBatchResult batchResult : heldBatchResults) {
597-
if (!isRemoteUpToVersion(batchResult.getCommitVersion())) {
598-
break;
599-
}
600-
toRelease.add(batchResult);
601-
}
602-
603-
if (toRelease.isEmpty()) {
604-
return Collections.emptySet();
605-
} else {
606-
heldBatchResults.subList(0, toRelease.size()).clear();
607-
return releaseBatchResults(toRelease);
608-
}
609-
}
610-
611-
private boolean isRemoteUpToVersion(SnapshotVersion snapshotVersion) {
612-
// If there are no watch targets, then we won't get remote snapshots, and are always
613-
// "up-to-date."
614-
return snapshotVersion.compareTo(queryCache.getLastRemoteSnapshotVersion()) <= 0
615-
|| targetIds.size() == 0;
616-
}
617-
618-
private boolean shouldHoldBatchResult(SnapshotVersion snapshotVersion) {
619-
// Check if watcher isn't up to date or prior results are already held.
620-
return !isRemoteUpToVersion(snapshotVersion) || !heldBatchResults.isEmpty();
621-
}
622-
623-
private Set<DocumentKey> releaseBatchResults(List<MutationBatchResult> batchResults) {
624-
ArrayList<MutationBatch> batches = new ArrayList<>(batchResults.size());
625-
// TODO: Call queryEngine.handleDocumentChange() as appropriate.
626-
for (MutationBatchResult batchResult : batchResults) {
627-
applyBatchResult(batchResult);
628-
batches.add(batchResult.getBatch());
629-
}
630-
631-
return removeMutationBatches(batches);
632-
}
633-
634-
private Set<DocumentKey> removeMutationBatch(MutationBatch batch) {
635-
return removeMutationBatches(singletonList(batch));
636-
}
637-
638-
/** Removes the given mutation batches. */
639-
private Set<DocumentKey> removeMutationBatches(List<MutationBatch> batches) {
640-
641-
Set<DocumentKey> affectedDocs = new HashSet<>();
642-
for (MutationBatch batch : batches) {
643-
for (Mutation mutation : batch.getMutations()) {
644-
affectedDocs.add(mutation.getKey());
645-
}
646-
mutationQueue.removeMutationBatch(batch);
647-
}
648-
649-
return affectedDocs;
650-
}
651-
652528
private void applyBatchResult(MutationBatchResult batchResult) {
653529
MutationBatch batch = batchResult.getBatch();
654530
Set<DocumentKey> docKeys = batch.getKeys();
@@ -671,5 +547,7 @@ private void applyBatchResult(MutationBatchResult batchResult) {
671547
}
672548
}
673549
}
550+
551+
mutationQueue.removeMutationBatch(batch);
674552
}
675553
}

firebase-firestore/src/test/java/com/google/firebase/firestore/local/LocalStoreTestCase.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,15 @@ public void testHandlesAckThenRejectThenRemoteEvent() {
255255

256256
// The last seen version is zero, so this ack must be held.
257257
acknowledgeMutation(1);
258-
assertChanged();
259-
assertContains(doc("foo/bar", 0, map("foo", "bar"), Document.DocumentState.LOCAL_MUTATIONS));
258+
assertChanged(doc("foo/bar", 1, map("foo", "bar"), Document.DocumentState.COMMITTED_MUTATIONS));
259+
if (garbageCollectorIsEager()) {
260+
// Nothing is pinning this anymore, as it has been acknowledged and there are no targets
261+
// active.
262+
assertNotContains("foo/bar");
263+
} else {
264+
assertContains(
265+
doc("foo/bar", 1, map("foo", "bar"), Document.DocumentState.COMMITTED_MUTATIONS));
266+
}
260267

261268
writeMutation(setMutation("bar/baz", map("bar", "baz")));
262269
assertChanged(doc("bar/baz", 0, map("bar", "baz"), Document.DocumentState.LOCAL_MUTATIONS));
@@ -327,8 +334,9 @@ public void testHandlesDocumentThenSetMutationThenAckThenDocument() {
327334

328335
acknowledgeMutation(3);
329336
// We haven't seen the remote event yet.
330-
assertChanged();
331-
assertContains(doc("foo/bar", 2, map("foo", "bar"), Document.DocumentState.LOCAL_MUTATIONS));
337+
assertChanged(doc("foo/bar", 3, map("foo", "bar"), Document.DocumentState.COMMITTED_MUTATIONS));
338+
assertContains(
339+
doc("foo/bar", 3, map("foo", "bar"), Document.DocumentState.COMMITTED_MUTATIONS));
332340

333341
applyRemoteEvent(
334342
updateRemoteEvent(doc("foo/bar", 3, map("it", "changed")), asList(targetId), emptyList()));
@@ -374,9 +382,18 @@ public void testHandlesPatchMutationThenDocumentThenAck() {
374382

375383
acknowledgeMutation(2);
376384

377-
assertChanged();
385+
assertChanged(
386+
doc(
387+
"foo/bar",
388+
2,
389+
map("foo", "bar", "it", "base"),
390+
Document.DocumentState.COMMITTED_MUTATIONS));
378391
assertContains(
379-
doc("foo/bar", 1, map("foo", "bar", "it", "base"), Document.DocumentState.LOCAL_MUTATIONS));
392+
doc(
393+
"foo/bar",
394+
2,
395+
map("foo", "bar", "it", "base"),
396+
Document.DocumentState.COMMITTED_MUTATIONS));
380397

381398
applyRemoteEvent(
382399
updateRemoteEvent(

0 commit comments

Comments
 (0)