Skip to content

Commit 82288ec

Browse files
Remove Mutation tombstones (#116)
1 parent d57b256 commit 82288ec

File tree

3 files changed

+22
-90
lines changed

3 files changed

+22
-90
lines changed

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

Lines changed: 7 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.google.firebase.firestore.util.Util;
3030
import com.google.protobuf.ByteString;
3131
import java.util.ArrayList;
32+
import java.util.Collections;
3233
import java.util.Iterator;
3334
import java.util.List;
3435
import javax.annotation.Nullable;
@@ -113,6 +114,7 @@ public void acknowledgeBatch(MutationBatch batch, ByteString streamToken) {
113114
batchId > highestAcknowledgedBatchId, "Mutation batchIds must be acknowledged in order");
114115

115116
int batchIndex = indexOfExistingBatchId(batchId, "acknowledged");
117+
hardAssert(batchIndex == 0, "Can only acknowledge the first batch in the mutation queue");
116118

117119
// Verify that the batch in the queue is the one to be acknowledged.
118120
MutationBatch check = queue.get(batchIndex);
@@ -121,7 +123,6 @@ public void acknowledgeBatch(MutationBatch batch, ByteString streamToken) {
121123
"Queue ordering failure: expected batch %d, got batch %d",
122124
batchId,
123125
check.getBatchId());
124-
hardAssert(!check.isTombstone(), "Can't acknowledge a previously removed batch");
125126

126127
highestAcknowledgedBatchId = batchId;
127128
lastStreamToken = checkNotNull(streamToken);
@@ -173,14 +174,12 @@ public MutationBatch lookupMutationBatch(int batchId) {
173174

174175
MutationBatch batch = queue.get(index);
175176
hardAssert(batch.getBatchId() == batchId, "If found batch must match");
176-
return batch.isTombstone() ? null : batch;
177+
return batch;
177178
}
178179

179180
@Nullable
180181
@Override
181182
public MutationBatch getNextMutationBatchAfterBatchId(int batchId) {
182-
int size = queue.size();
183-
184183
// All batches with batchId <= highestAcknowledgedBatchId have been acknowledged so the
185184
// first unacknowledged batch after batchId will have a batchId larger than both of these
186185
// values.
@@ -189,21 +188,12 @@ public MutationBatch getNextMutationBatchAfterBatchId(int batchId) {
189188
// The requested batchId may still be out of range so normalize it to the start of the queue.
190189
int rawIndex = indexOfBatchId(nextBatchId);
191190
int index = rawIndex < 0 ? 0 : rawIndex;
192-
193-
// Finally return the first non-tombstone batch.
194-
for (; index < size; index++) {
195-
MutationBatch batch = queue.get(index);
196-
if (!batch.isTombstone()) {
197-
return batch;
198-
}
199-
}
200-
201-
return null;
191+
return queue.size() > index ? queue.get(index) : null;
202192
}
203193

204194
@Override
205195
public List<MutationBatch> getAllMutationBatches() {
206-
return getAllLiveMutationBatchesBeforeIndex(queue.size());
196+
return Collections.unmodifiableList(queue);
207197
}
208198

209199
@Override
@@ -305,27 +295,9 @@ public void removeMutationBatch(MutationBatch batch) {
305295
// Find the position of the first batch for removal. This need not be the first entry in the
306296
// queue.
307297
int batchIndex = indexOfExistingBatchId(batch.getBatchId(), "removed");
308-
hardAssert(
309-
queue.get(batchIndex).getBatchId() == batch.getBatchId(),
310-
"Removed batches must exist in the queue");
311-
312-
// Only actually remove batches if removing at the front of the queue. Previously rejected
313-
// batches may have left tombstones in the queue, so expand the removal range to include any
314-
// tombstones.
315-
if (batchIndex == 0) {
316-
int endIndex = 1;
317-
for (; endIndex < queue.size(); endIndex++) {
318-
MutationBatch currentBatch = queue.get(endIndex);
319-
if (!currentBatch.isTombstone()) {
320-
break;
321-
}
322-
}
323-
324-
queue.subList(batchIndex, endIndex).clear();
298+
hardAssert(batchIndex == 0, "Can only remove the first entry of the mutation queue");
325299

326-
} else {
327-
queue.set(batchIndex, queue.get(batchIndex).toTombstone());
328-
}
300+
queue.remove(0);
329301

330302
// Remove entries from the index too.
331303
ImmutableSortedSet<DocumentReference> references = batchesByDocumentKey;
@@ -364,24 +336,6 @@ boolean containsKey(DocumentKey key) {
364336

365337
// Helpers
366338

367-
/**
368-
* A private helper that collects all the mutation batches in the queue up to but not including
369-
* the given endIndex. All tombstones in the queue are excluded.
370-
*/
371-
private List<MutationBatch> getAllLiveMutationBatchesBeforeIndex(int endIndex) {
372-
List<MutationBatch> result = new ArrayList<>(endIndex);
373-
374-
for (int i = 0; i < endIndex; i++) {
375-
MutationBatch batch = queue.get(i);
376-
377-
if (!batch.isTombstone()) {
378-
result.add(batch);
379-
}
380-
}
381-
382-
return result;
383-
}
384-
385339
/**
386340
* Finds the index of the given batchId in the mutation queue. This operation is O(1).
387341
*

firebase-firestore/src/main/java/com/google/firebase/firestore/model/mutation/MutationBatch.java

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import com.google.firebase.Timestamp;
2020
import com.google.firebase.firestore.model.DocumentKey;
2121
import com.google.firebase.firestore.model.MaybeDocument;
22-
import java.util.Collections;
2322
import java.util.HashSet;
2423
import java.util.List;
2524
import java.util.Set;
@@ -45,6 +44,7 @@ public final class MutationBatch {
4544
private final List<Mutation> mutations;
4645

4746
public MutationBatch(int batchId, Timestamp localWriteTime, List<Mutation> mutations) {
47+
hardAssert(!mutations.isEmpty(), "Cannot create an empty mutation batch");
4848
this.batchId = batchId;
4949
this.localWriteTime = localWriteTime;
5050
this.mutations = mutations;
@@ -164,22 +164,6 @@ public Timestamp getLocalWriteTime() {
164164
return localWriteTime;
165165
}
166166

167-
/**
168-
* Returns true if this mutation batch has already been removed from the mutation queue.
169-
*
170-
* <p>Note that not all implementations of the MutationQueue necessarily use tombstones as a part
171-
* of their implementation and generally speaking no code outside the mutation queues should
172-
* really care about this.
173-
*/
174-
public boolean isTombstone() {
175-
return mutations.isEmpty();
176-
}
177-
178-
/** Converts this batch to a tombstone. */
179-
public MutationBatch toTombstone() {
180-
return new MutationBatch(batchId, localWriteTime, Collections.emptyList());
181-
}
182-
183167
public List<Mutation> getMutations() {
184168
return mutations;
185169
}

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

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ public void testCountBatches() {
9494
assertEquals(2, batchCount());
9595
assertFalse(mutationQueue.isEmpty());
9696

97-
removeMutationBatches(batch2);
97+
removeMutationBatches(batch1);
9898
assertEquals(1, batchCount());
9999

100-
removeMutationBatches(batch1);
100+
removeMutationBatches(batch2);
101101
assertEquals(0, batchCount());
102102
assertTrue(mutationQueue.isEmpty());
103103
}
@@ -123,7 +123,7 @@ public void testLookupMutationBatch() {
123123
assertNull(notFound);
124124

125125
List<MutationBatch> batches = createBatches(10);
126-
List<MutationBatch> removed = makeHoles(asList(2, 6, 7), batches);
126+
List<MutationBatch> removed = removeFirstBatches(3, batches);
127127

128128
// After removing, a batch should not be found
129129
for (MutationBatch batch : removed) {
@@ -146,10 +146,7 @@ public void testLookupMutationBatch() {
146146
@Test
147147
public void testNextMutationBatchAfterBatchId() {
148148
List<MutationBatch> batches = createBatches(10);
149-
150-
// This is an array of successors assuming the removals below will happen:
151-
List<MutationBatch> afters = asList(batches.get(3), batches.get(8), batches.get(8));
152-
List<MutationBatch> removed = makeHoles(asList(2, 6, 7), batches);
149+
List<MutationBatch> removed = removeFirstBatches(3, batches);
153150

154151
for (int i = 0; i < batches.size() - 1; i++) {
155152
MutationBatch current = batches.get(i);
@@ -161,7 +158,7 @@ public void testNextMutationBatchAfterBatchId() {
161158

162159
for (int i = 0; i < removed.size(); i++) {
163160
MutationBatch current = removed.get(i);
164-
MutationBatch next = afters.get(i);
161+
MutationBatch next = batches.get(0);
165162
MutationBatch found = mutationQueue.getNextMutationBatchAfterBatchId(current.getBatchId());
166163
assertNotNull(found);
167164
assertEquals(next.getBatchId(), found.getBatchId());
@@ -367,17 +364,17 @@ public void testRemoveMutationBatches() {
367364
assertEquals(batches, found);
368365
assertEquals(6, found.size());
369366

370-
removeMutationBatches(batches.remove(batches.size() - 1));
367+
removeMutationBatches(batches.remove(0));
371368
assertEquals(5, batchCount());
372369

373370
found = mutationQueue.getAllMutationBatches();
374371
assertEquals(batches, found);
375372
assertEquals(5, found.size());
376373

377-
removeMutationBatches(batches.remove(3));
374+
removeMutationBatches(batches.remove(0));
378375
assertEquals(4, batchCount());
379376

380-
removeMutationBatches(batches.remove(1));
377+
removeMutationBatches(batches.remove(0));
381378
assertEquals(3, batchCount());
382379

383380
found = mutationQueue.getAllMutationBatches();
@@ -460,21 +457,18 @@ private int batchCount() {
460457
}
461458

462459
/**
463-
* Removes entries from from the given <tt>batches</tt> and returns them.
460+
* Removes the first n entries from the given <tt>batches</tt> and returns them.
464461
*
465-
* @param holes An list of indexes in the batches list; in increasing order. Indexes are relative
466-
* to the original state of the batches list, not any intermediate state that might occur.
462+
* @param n The number of batches to remove..
467463
* @param batches The list to mutate, removing entries from it.
468464
* @return A new list containing all the entries that were removed from @a batches.
469465
*/
470-
private List<MutationBatch> makeHoles(List<Integer> holes, List<MutationBatch> batches) {
466+
private List<MutationBatch> removeFirstBatches(int n, List<MutationBatch> batches) {
471467
List<MutationBatch> removed = new ArrayList<>();
472-
for (int i = 0; i < holes.size(); i++) {
473-
int index = holes.get(i) - i;
474-
MutationBatch batch = batches.get(index);
468+
for (int i = 0; i < n; i++) {
469+
MutationBatch batch = batches.get(0);
475470
removeMutationBatches(batch);
476-
477-
batches.remove(index);
471+
batches.remove(0);
478472
removed.add(batch);
479473
}
480474
return removed;

0 commit comments

Comments
 (0)