Skip to content

Commit 1291b03

Browse files
Remove Mutation tombstones (#2052)
* Remove Mutation tombstones * Review comments
1 parent e77ba8c commit 1291b03

File tree

6 files changed

+35
-185
lines changed

6 files changed

+35
-185
lines changed

Firestore/Example/Tests/Local/FSTMutationQueueTests.mm

Lines changed: 28 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ - (void)testCountBatches {
7171
FSTMutationBatch *batch2 = [self addMutationBatch];
7272
XCTAssertEqual(2, [self batchCount]);
7373

74-
[self.mutationQueue removeMutationBatch:batch2];
74+
[self.mutationQueue removeMutationBatch:batch1];
7575
XCTAssertEqual(1, [self batchCount]);
7676

77-
[self.mutationQueue removeMutationBatch:batch1];
77+
[self.mutationQueue removeMutationBatch:batch2];
7878
XCTAssertEqual(0, [self batchCount]);
7979
XCTAssertTrue([self.mutationQueue isEmpty]);
8080
});
@@ -98,10 +98,9 @@ - (void)testAcknowledgeBatchID {
9898
XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown);
9999

100100
[self.mutationQueue acknowledgeBatch:batch1 streamToken:nil];
101-
[self.mutationQueue acknowledgeBatch:batch2 streamToken:nil];
102-
XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID);
103-
104101
[self.mutationQueue removeMutationBatch:batch1];
102+
103+
[self.mutationQueue acknowledgeBatch:batch2 streamToken:nil];
105104
XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID);
106105

107106
[self.mutationQueue removeMutationBatch:batch2];
@@ -139,10 +138,10 @@ - (void)testHighestAcknowledgedBatchIDNeverExceedsNextBatchID {
139138

140139
self.persistence.run("testHighestAcknowledgedBatchIDNeverExceedsNextBatchID", [&]() {
141140
[self.mutationQueue acknowledgeBatch:batch1 streamToken:nil];
141+
[self.mutationQueue removeMutationBatch:batch1];
142142
[self.mutationQueue acknowledgeBatch:batch2 streamToken:nil];
143143
XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID);
144144

145-
[self.mutationQueue removeMutationBatch:batch1];
146145
[self.mutationQueue removeMutationBatch:batch2];
147146
XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID);
148147
});
@@ -186,7 +185,7 @@ - (void)testLookupMutationBatch {
186185
XCTAssertNil(notFound);
187186

188187
NSMutableArray<FSTMutationBatch *> *batches = [self createBatches:10];
189-
NSArray<FSTMutationBatch *> *removed = [self makeHoles:@[ @2, @6, @7 ] inBatches:batches];
188+
NSArray<FSTMutationBatch *> *removed = [self removeFirstBatches:3 inBatches:batches];
190189

191190
// After removing, a batch should not be found
192191
for (NSUInteger i = 0; i < removed.count; i++) {
@@ -211,10 +210,7 @@ - (void)testNextMutationBatchAfterBatchID {
211210

212211
self.persistence.run("testNextMutationBatchAfterBatchID", [&]() {
213212
NSMutableArray<FSTMutationBatch *> *batches = [self createBatches:10];
214-
215-
// This is an array of successors assuming the removals below will happen:
216-
NSArray<FSTMutationBatch *> *afters = @[ batches[3], batches[8], batches[8] ];
217-
NSArray<FSTMutationBatch *> *removed = [self makeHoles:@[ @2, @6, @7 ] inBatches:batches];
213+
NSArray<FSTMutationBatch *> *removed = [self removeFirstBatches:3 inBatches:batches];
218214

219215
for (NSUInteger i = 0; i < batches.count - 1; i++) {
220216
FSTMutationBatch *current = batches[i];
@@ -225,7 +221,7 @@ - (void)testNextMutationBatchAfterBatchID {
225221

226222
for (NSUInteger i = 0; i < removed.count; i++) {
227223
FSTMutationBatch *current = removed[i];
228-
FSTMutationBatch *next = afters[i];
224+
FSTMutationBatch *next = batches[0];
229225
FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:current.batchID];
230226
XCTAssertEqual(found.batchID, next.batchID);
231227
}
@@ -262,26 +258,6 @@ - (void)testNextMutationBatchAfterBatchIDSkipsAcknowledgedBatches {
262258
});
263259
}
264260

265-
- (void)testAllMutationBatchesThroughBatchID {
266-
if ([self isTestBaseClass]) return;
267-
268-
self.persistence.run("testAllMutationBatchesThroughBatchID", [&]() {
269-
NSMutableArray<FSTMutationBatch *> *batches = [self createBatches:10];
270-
[self makeHoles:@[ @2, @6, @7 ] inBatches:batches];
271-
272-
NSArray<FSTMutationBatch *> *found, *expected;
273-
274-
found = [self.mutationQueue allMutationBatchesThroughBatchID:batches[0].batchID - 1];
275-
XCTAssertEqualObjects(found, (@[]));
276-
277-
for (NSUInteger i = 0; i < batches.count; i++) {
278-
found = [self.mutationQueue allMutationBatchesThroughBatchID:batches[i].batchID];
279-
expected = [batches subarrayWithRange:NSMakeRange(0, i + 1)];
280-
XCTAssertEqualObjects(found, expected, @"for index %lu", (unsigned long)i);
281-
}
282-
});
283-
}
284-
285261
- (void)testAllMutationBatchesAffectingDocumentKey {
286262
if ([self isTestBaseClass]) return;
287263

@@ -443,12 +419,11 @@ - (void)testRemoveMutationBatches {
443419
[self.mutationQueue removeMutationBatch:batches[0]];
444420
[batches removeObjectAtIndex:0];
445421

446-
FSTMutationBatch *last = batches[batches.count - 1];
447422
XCTAssertEqual([self batchCount], 9);
448423

449424
NSArray<FSTMutationBatch *> *found;
450425

451-
found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID];
426+
found = [self.mutationQueue allMutationBatches];
452427
XCTAssertEqualObjects(found, batches);
453428
XCTAssertEqual(found.count, 9);
454429

@@ -458,35 +433,35 @@ - (void)testRemoveMutationBatches {
458433
[batches removeObjectsInRange:NSMakeRange(0, 3)];
459434
XCTAssertEqual([self batchCount], 6);
460435

461-
found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID];
436+
found = [self.mutationQueue allMutationBatches];
462437
XCTAssertEqualObjects(found, batches);
463438
XCTAssertEqual(found.count, 6);
464439

465-
[self.mutationQueue removeMutationBatch:batches[batches.count - 1]];
466-
[batches removeObjectAtIndex:batches.count - 1];
440+
[self.mutationQueue removeMutationBatch:batches[0]];
441+
[batches removeObjectAtIndex:0];
467442
XCTAssertEqual([self batchCount], 5);
468443

469-
found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID];
444+
found = [self.mutationQueue allMutationBatches];
470445
XCTAssertEqualObjects(found, batches);
471446
XCTAssertEqual(found.count, 5);
472447

473-
[self.mutationQueue removeMutationBatch:batches[3]];
474-
[batches removeObjectAtIndex:3];
448+
[self.mutationQueue removeMutationBatch:batches[0]];
449+
[batches removeObjectAtIndex:0];
475450
XCTAssertEqual([self batchCount], 4);
476451

477-
[self.mutationQueue removeMutationBatch:batches[1]];
478-
[batches removeObjectAtIndex:1];
452+
[self.mutationQueue removeMutationBatch:batches[0]];
453+
[batches removeObjectAtIndex:0];
479454
XCTAssertEqual([self batchCount], 3);
480455

481-
found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID];
456+
found = [self.mutationQueue allMutationBatches];
482457
XCTAssertEqualObjects(found, batches);
483458
XCTAssertEqual(found.count, 3);
484459
XCTAssertFalse([self.mutationQueue isEmpty]);
485460

486461
for (FSTMutationBatch *batch in batches) {
487462
[self.mutationQueue removeMutationBatch:batch];
488463
}
489-
found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID];
464+
found = [self.mutationQueue allMutationBatches];
490465
XCTAssertEqualObjects(found, @[]);
491466
XCTAssertEqual(found.count, 0);
492467
XCTAssertTrue([self.mutationQueue isEmpty]);
@@ -554,24 +529,21 @@ - (NSUInteger)batchCount {
554529
}
555530

556531
/**
557-
* Removes entries from from the given @a batches and returns them.
532+
* Removes the first n entries from the the given batches and returns them.
558533
*
559-
* @param holes An array of indexes in the batches array; in increasing order. Indexes are relative
560-
* to the original state of the batches array, not any intermediate state that might occur.
534+
* @param n The number of batches to remove.
561535
* @param batches The array to mutate, removing entries from it.
562536
* @return A new array containing all the entries that were removed from @a batches.
563537
*/
564-
- (NSArray<FSTMutationBatch *> *)makeHoles:(NSArray<NSNumber *> *)holes
565-
inBatches:(NSMutableArray<FSTMutationBatch *> *)batches {
566-
NSMutableArray<FSTMutationBatch *> *removed = [NSMutableArray array];
567-
for (NSUInteger i = 0; i < holes.count; i++) {
568-
NSUInteger index = holes[i].unsignedIntegerValue - i;
569-
FSTMutationBatch *batch = batches[index];
538+
- (NSArray<FSTMutationBatch *> *)removeFirstBatches:(NSUInteger)n
539+
inBatches:(NSMutableArray<FSTMutationBatch *> *)batches {
540+
NSArray<FSTMutationBatch *> *removed = [batches subarrayWithRange:NSMakeRange(0, n)];
541+
[batches removeObjectsInRange:NSMakeRange(0, n)];
542+
543+
[removed enumerateObjectsUsingBlock:^(FSTMutationBatch *batch, NSUInteger idx, BOOL *stop) {
570544
[self.mutationQueue removeMutationBatch:batch];
545+
}];
571546

572-
[batches removeObjectAtIndex:index];
573-
[removed addObject:batch];
574-
}
575547
return removed;
576548
}
577549

Firestore/Source/Local/FSTLevelDBMutationQueue.mm

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -327,29 +327,6 @@ - (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(BatchId)batchID {
327327
return [self decodedMutationBatch:it->value()];
328328
}
329329

330-
- (NSArray<FSTMutationBatch *> *)allMutationBatchesThroughBatchID:(BatchId)batchID {
331-
std::string userKey = LevelDbMutationKey::KeyPrefix(_userID);
332-
333-
auto it = _db.currentTransaction->NewIterator();
334-
it->Seek(userKey);
335-
336-
NSMutableArray *result = [NSMutableArray array];
337-
LevelDbMutationKey rowKey;
338-
for (; it->Valid() && rowKey.Decode(it->key()); it->Next()) {
339-
if (rowKey.user_id() != _userID) {
340-
// End of this user's mutations
341-
break;
342-
} else if (rowKey.batch_id() > batchID) {
343-
// This mutation is past what we're looking for
344-
break;
345-
}
346-
347-
[result addObject:[self decodedMutationBatch:it->value()]];
348-
}
349-
350-
return result;
351-
}
352-
353330
- (NSArray<FSTMutationBatch *> *)allMutationBatchesAffectingDocumentKey:
354331
(const DocumentKey &)documentKey {
355332
// Scan the document-mutation index starting with a prefix starting with the given documentKey.

Firestore/Source/Local/FSTMemoryMutationQueue.mm

Lines changed: 6 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,12 @@ - (void)acknowledgeBatch:(FSTMutationBatch *)batch streamToken:(nullable NSData
131131
"Mutation batchIDs must be acknowledged in order");
132132

133133
NSInteger batchIndex = [self indexOfExistingBatchID:batchID action:@"acknowledged"];
134+
HARD_ASSERT(batchIndex == 0, "Can only acknowledge the first batch in the mutation queue");
134135

135136
// Verify that the batch in the queue is the one to be acknowledged.
136137
FSTMutationBatch *check = queue[(NSUInteger)batchIndex];
137138
HARD_ASSERT(batchID == check.batchID, "Queue ordering failure: expected batch %s, got batch %s",
138139
batchID, check.batchID);
139-
HARD_ASSERT(![check isTombstone], "Can't acknowledge a previously removed batch");
140140

141141
self.highestAcknowledgedBatchID = batchID;
142142
self.lastStreamToken = streamToken;
@@ -182,12 +182,11 @@ - (nullable FSTMutationBatch *)lookupMutationBatch:(BatchId)batchID {
182182

183183
FSTMutationBatch *batch = queue[(NSUInteger)index];
184184
HARD_ASSERT(batch.batchID == batchID, "If found batch must match");
185-
return [batch isTombstone] ? nil : batch;
185+
return batch;
186186
}
187187

188188
- (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(BatchId)batchID {
189189
NSMutableArray<FSTMutationBatch *> *queue = self.queue;
190-
NSUInteger count = queue.count;
191190

192191
// All batches with batchID <= self.highestAcknowledgedBatchID have been acknowledged so the
193192
// first unacknowledged batch after batchID will have a batchID larger than both of these values.
@@ -196,37 +195,11 @@ - (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(BatchId)batchID {
196195
// The requested batchID may still be out of range so normalize it to the start of the queue.
197196
NSInteger rawIndex = [self indexOfBatchID:nextBatchID];
198197
NSUInteger index = rawIndex < 0 ? 0 : (NSUInteger)rawIndex;
199-
200-
// Finally return the first non-tombstone batch.
201-
for (; index < count; index++) {
202-
FSTMutationBatch *batch = queue[index];
203-
if (![batch isTombstone]) {
204-
return batch;
205-
}
206-
}
207-
208-
return nil;
198+
return queue.count > index ? queue[index] : nil;
209199
}
210200

211201
- (NSArray<FSTMutationBatch *> *)allMutationBatches {
212-
return [self allLiveMutationBatchesBeforeIndex:self.queue.count];
213-
}
214-
215-
- (NSArray<FSTMutationBatch *> *)allMutationBatchesThroughBatchID:(BatchId)batchID {
216-
NSMutableArray<FSTMutationBatch *> *queue = self.queue;
217-
NSUInteger count = queue.count;
218-
219-
NSInteger endIndex = [self indexOfBatchID:batchID];
220-
if (endIndex < 0) {
221-
endIndex = 0;
222-
} else if (endIndex >= count) {
223-
endIndex = count;
224-
} else {
225-
// The endIndex is in the queue so increment to pull everything in the queue including it.
226-
endIndex += 1;
227-
}
228-
229-
return [self allLiveMutationBatchesBeforeIndex:(NSUInteger)endIndex];
202+
return [[self queue] copy];
230203
}
231204

232205
- (NSArray<FSTMutationBatch *> *)allMutationBatchesAffectingDocumentKey:
@@ -328,31 +301,14 @@ - (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(BatchId)batchID {
328301

329302
- (void)removeMutationBatch:(FSTMutationBatch *)batch {
330303
NSMutableArray<FSTMutationBatch *> *queue = self.queue;
331-
NSUInteger queueCount = queue.count;
332304
BatchId batchID = batch.batchID;
333305

334306
// Find the position of the first batch for removal. This need not be the first entry in the
335307
// queue.
336308
NSUInteger batchIndex = [self indexOfExistingBatchID:batchID action:@"removed"];
337-
HARD_ASSERT(queue[batchIndex].batchID == batchID, "Removed batches must exist in the queue");
338-
339-
// Only actually remove batches if removing at the front of the queue. Previously rejected batches
340-
// may have left tombstones in the queue, so expand the removal range to include any tombstones.
341-
if (batchIndex == 0) {
342-
int endIndex = 1;
343-
for (; endIndex < queueCount; endIndex++) {
344-
FSTMutationBatch *batch = queue[endIndex];
345-
if (![batch isTombstone]) {
346-
break;
347-
}
348-
}
309+
HARD_ASSERT(batchIndex == 0, "Can only remove the first entry of the mutation queue");
349310

350-
NSUInteger length = endIndex - batchIndex;
351-
[queue removeObjectsInRange:NSMakeRange(batchIndex, length)];
352-
353-
} else {
354-
queue[batchIndex] = [queue[batchIndex] toTombstone];
355-
}
311+
[queue removeObjectAtIndex:0];
356312

357313
// Remove entries from the index too.
358314
FSTImmutableSortedSet<FSTDocumentReference *> *references = self.batchesByDocumentKey;
@@ -388,25 +344,6 @@ - (BOOL)containsKey:(const DocumentKey &)key {
388344

389345
#pragma mark - Helpers
390346

391-
/**
392-
* A private helper that collects all the mutation batches in the queue up to but not including
393-
* the given endIndex. All tombstones in the queue are excluded.
394-
*/
395-
- (NSArray<FSTMutationBatch *> *)allLiveMutationBatchesBeforeIndex:(NSUInteger)endIndex {
396-
NSMutableArray<FSTMutationBatch *> *result = [NSMutableArray arrayWithCapacity:endIndex];
397-
398-
NSUInteger index = 0;
399-
for (FSTMutationBatch *batch in self.queue) {
400-
if (index++ >= endIndex) break;
401-
402-
if (![batch isTombstone]) {
403-
[result addObject:batch];
404-
}
405-
}
406-
407-
return result;
408-
}
409-
410347
/**
411348
* Finds the index of the given batchID in the mutation queue. This operation is O(1).
412349
*

Firestore/Source/Local/FSTMutationQueue.h

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -92,21 +92,6 @@ NS_ASSUME_NONNULL_BEGIN
9292
// cheaply, we should replace this.
9393
- (NSArray<FSTMutationBatch *> *)allMutationBatches;
9494

95-
/**
96-
* Finds all mutations with a batchID less than or equal to the given batchID.
97-
*
98-
* Generally the caller should be asking for the next unacknowledged batchID and the number of
99-
* acknowledged batches should be very small when things are functioning well.
100-
*
101-
* @param batchID The batch to search through.
102-
*
103-
* @return an NSArray containing all batches with matching batchIDs.
104-
*/
105-
// TODO(mcg): This should really return NSEnumerator and the caller should be adjusted to only
106-
// loop through these once.
107-
- (NSArray<FSTMutationBatch *> *)allMutationBatchesThroughBatchID:
108-
(firebase::firestore::model::BatchId)batchID;
109-
11095
/**
11196
* Finds all mutation batches that could @em possibly affect the given document key. Not all
11297
* mutations in a batch will necessarily affect the document key, so when looping through the

Firestore/Source/Model/FSTMutationBatch.h

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -87,18 +87,6 @@ extern const firebase::firestore::model::BatchId kFSTBatchIDUnknown;
8787
applyToLocalDocument:(FSTMaybeDocument *_Nullable)maybeDoc
8888
documentKey:(const firebase::firestore::model::DocumentKey &)documentKey;
8989

90-
/**
91-
* Returns YES if this mutation batch has already been removed from the mutation queue.
92-
*
93-
* Note that not all implementations of the FSTMutationQueue necessarily use tombstones as a part
94-
* of their implementation and generally speaking no code outside the mutation queues should really
95-
* care about this.
96-
*/
97-
- (BOOL)isTombstone;
98-
99-
/** Converts this batch to a tombstone. */
100-
- (FSTMutationBatch *)toTombstone;
101-
10290
/** Returns the set of unique keys referenced by all mutations in the batch. */
10391
- (firebase::firestore::model::DocumentKeySet)keys;
10492

0 commit comments

Comments
 (0)