From 3f36f5467de4c191fa2903743e5a210420e9d49a Mon Sep 17 00:00:00 2001 From: Greg Soltis Date: Fri, 30 Mar 2018 10:18:25 -0700 Subject: Drop FSTWriteGroup (#986) * Drop write group from remote document change buffer * Unwind some group dependendencies in local store * Write group dropped from local store * Drop write group from mutation queue tests * Drop write group usage from query cache tests * Drop write groups from remote document cache tests * Drop write groups from remote document change buffer tests * Drop write groups and the write group tracker * Style * Put the action in transaction * Merge master, fix test * Fix some compiler warnings but mostly trigger travis * Responses to feedback --- .../Example/Firestore.xcodeproj/project.pbxproj | 4 - .../Tests/Local/FSTLevelDBMigrationsTests.mm | 8 +- .../Tests/Local/FSTLevelDBMutationQueueTests.mm | 5 +- .../Tests/Local/FSTLevelDBTransactionTests.mm | 24 +- .../Example/Tests/Local/FSTMutationQueueTests.mm | 690 ++++++++++----------- .../Example/Tests/Local/FSTQueryCacheTests.mm | 486 ++++++++------- .../Tests/Local/FSTRemoteDocumentCacheTests.mm | 101 ++- .../Local/FSTRemoteDocumentChangeBufferTests.mm | 57 +- .../Example/Tests/Local/FSTWriteGroupTests.mm | 101 --- Firestore/Source/Core/FSTSyncEngine.mm | 4 +- Firestore/Source/Local/FSTLevelDB.mm | 23 +- Firestore/Source/Local/FSTLocalStore.mm | 472 +++++++------- Firestore/Source/Local/FSTMemoryPersistence.mm | 14 - Firestore/Source/Local/FSTPersistence.h | 26 +- .../Source/Local/FSTRemoteDocumentChangeBuffer.h | 6 +- .../Source/Local/FSTRemoteDocumentChangeBuffer.mm | 2 +- Firestore/Source/Local/FSTWriteGroup.h | 89 --- Firestore/Source/Local/FSTWriteGroup.mm | 96 --- Firestore/Source/Local/FSTWriteGroupTracker.h | 50 -- Firestore/Source/Local/FSTWriteGroupTracker.mm | 63 -- .../firestore/local/leveldb_transaction.cc | 6 +- .../firebase/firestore/local/leveldb_transaction.h | 2 + 22 files changed, 905 insertions(+), 1424 deletions(-) delete mode 100644 Firestore/Example/Tests/Local/FSTWriteGroupTests.mm delete mode 100644 Firestore/Source/Local/FSTWriteGroup.h delete mode 100644 Firestore/Source/Local/FSTWriteGroup.mm delete mode 100644 Firestore/Source/Local/FSTWriteGroupTracker.h delete mode 100644 Firestore/Source/Local/FSTWriteGroupTracker.mm (limited to 'Firestore') diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index eb88210..7dbb45d 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -95,7 +95,6 @@ 5492E0AD2021552D00B64F25 /* FSTMemoryMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0972021552C00B64F25 /* FSTMemoryMutationQueueTests.mm */; }; 5492E0AE2021552D00B64F25 /* FSTLevelDBQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */; }; 5492E0AF2021552D00B64F25 /* FSTReferenceSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09A2021552C00B64F25 /* FSTReferenceSetTests.mm */; }; - 5492E0B02021552D00B64F25 /* FSTWriteGroupTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09B2021552C00B64F25 /* FSTWriteGroupTests.mm */; }; 5492E0B12021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */; }; 5492E0B92021555100B64F25 /* FSTDocumentKeyTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */; }; 5492E0BA2021555100B64F25 /* FSTDocumentSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */; }; @@ -311,7 +310,6 @@ 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBQueryCacheTests.mm; sourceTree = ""; }; 5492E0992021552C00B64F25 /* FSTPersistenceTestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTPersistenceTestHelpers.h; sourceTree = ""; }; 5492E09A2021552C00B64F25 /* FSTReferenceSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTReferenceSetTests.mm; sourceTree = ""; }; - 5492E09B2021552C00B64F25 /* FSTWriteGroupTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTWriteGroupTests.mm; sourceTree = ""; }; 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTRemoteDocumentCacheTests.mm; sourceTree = ""; }; 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDocumentKeyTests.mm; sourceTree = ""; }; 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDocumentSetTests.mm; sourceTree = ""; }; @@ -755,7 +753,6 @@ 5492E0852021552A00B64F25 /* FSTRemoteDocumentCacheTests.h */, 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */, 5492E0902021552B00B64F25 /* FSTRemoteDocumentChangeBufferTests.mm */, - 5492E09B2021552C00B64F25 /* FSTWriteGroupTests.mm */, 5492E0932021552B00B64F25 /* StringViewTests.mm */, 132E36BB104830BD806351AC /* FSTLevelDBTransactionTests.mm */, ); @@ -1477,7 +1474,6 @@ B686F2B22025000D0028D6BE /* resource_path_test.cc in Sources */, 5492E0CA2021557E00B64F25 /* FSTWatchChangeTests.mm in Sources */, 5492E063202154B900B64F25 /* FSTViewSnapshotTest.mm in Sources */, - 5492E0B02021552D00B64F25 /* FSTWriteGroupTests.mm in Sources */, 5492E058202154AB00B64F25 /* FSTAPIHelpers.mm in Sources */, AB380CFB2019388600D97691 /* target_id_generator_test.cc in Sources */, 5492E0A82021552D00B64F25 /* FSTLevelDBLocalStoreTests.mm in Sources */, diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm index ad892d8..14910ce 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm @@ -60,7 +60,7 @@ using leveldb::Status; - (void)testAddsTargetGlobal { FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; XCTAssertNil(metadata, @"Not expecting metadata yet, we should have an empty db"); - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testAddsTargetGlobal"); [FSTLevelDBMigrations runMigrationsWithTransaction:&transaction]; transaction.Commit(); metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; @@ -68,7 +68,7 @@ using leveldb::Status; } - (void)testSetsVersionNumber { - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testSetsVersionNumber"); FSTLevelDBSchemaVersion initial = [FSTLevelDBMigrations schemaVersionWithTransaction:&transaction]; XCTAssertEqual(0, initial, "No version should be equivalent to 0"); @@ -83,7 +83,7 @@ using leveldb::Status; NSUInteger expected = 50; { // Setup some targets to be counted in the migration. - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCountsQueries setup"); for (int i = 0; i < expected; i++) { std::string key = [FSTLevelDBTargetKey keyWithTargetID:i]; transaction.Put(key, "dummy"); @@ -100,7 +100,7 @@ using leveldb::Status; } { - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCountsQueries"); [FSTLevelDBMigrations runMigrationsWithTransaction:&transaction]; transaction.Commit(); FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm index cb03bf2..cd8176e 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm @@ -22,7 +22,6 @@ #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" #import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Source/Local/FSTLevelDBKey.h" -#import "Firestore/Source/Local/FSTWriteGroup.h" #import "Firestore/Example/Tests/Local/FSTMutationQueueTests.h" #import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" @@ -73,9 +72,7 @@ std::string MutationLikeKey(StringView table, StringView userID, FSTBatchID batc self.mutationQueue = [_db mutationQueueForUser:User("user")]; self.persistence = _db; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Start MutationQueue"]; - [self.mutationQueue start]; - [self.persistence commitGroup:group]; + self.persistence.run("Setup", [&]() { [self.mutationQueue start]; }); } - (void)testLoadNextBatchID_zeroWhenTotallyEmpty { diff --git a/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm index c959b4f..a32ce9f 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm @@ -58,7 +58,7 @@ using firebase::firestore::local::LevelDbTransaction; } - (void)testCreateTransaction { - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCreateTransaction"); std::string key = "key1"; transaction.Put(key, "value"); @@ -83,7 +83,7 @@ using firebase::firestore::local::LevelDbTransaction; status = _db->Put(writeOptions, committed_key2, committed_value2); XCTAssertTrue(status.ok()); - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCanReadCommittedAndMutations"); const std::string mutation_key1 = "m_key1"; const std::string mutation_value1 = "m_value1"; transaction.Put(mutation_key1, mutation_value1); @@ -113,7 +113,7 @@ using firebase::firestore::local::LevelDbTransaction; "value_" + std::to_string(i)); XCTAssertTrue(status.ok()); } - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testDeleteCommitted"); transaction.Put("key_1", "new_value"); std::string value; Status status = transaction.Get("key_1", &value); @@ -142,7 +142,7 @@ using firebase::firestore::local::LevelDbTransaction; XCTAssertTrue(status.ok()); } std::string value; - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testMutateDeleted"); transaction.Delete("key_1"); Status status = transaction.Get("key_1", &value); XCTAssertTrue(status.IsNotFound()); @@ -186,7 +186,7 @@ using firebase::firestore::local::LevelDbTransaction; } - (void)testProtobufSupport { - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testProtobufSupport"); FSTPBTarget *target = [FSTPBTarget message]; target.targetId = 1; @@ -206,7 +206,7 @@ using firebase::firestore::local::LevelDbTransaction; } - (void)testCanIterateAndDelete { - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCanIterateAndDelete"); for (int i = 0; i < 4; ++i) { transaction.Put("key_" + std::to_string(i), "value_" + std::to_string(i)); @@ -233,7 +233,7 @@ using firebase::firestore::local::LevelDbTransaction; } // Create a transaction, iterate, deleting key_0. Verify we still iterate key_1. - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCanIterateFromDeletionToCommitted"); auto it = transaction.NewIterator(); it->Seek("key_0"); XCTAssertTrue(it->Valid()); @@ -255,7 +255,7 @@ using firebase::firestore::local::LevelDbTransaction; } // Create a transaction, iterate to key_1, delete key_2. Verify we still iterate key_3. - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testDeletingAheadOfAnIterator"); auto it = transaction.NewIterator(); it->Seek("key_0"); XCTAssertTrue(it->Valid()); @@ -277,21 +277,21 @@ using firebase::firestore::local::LevelDbTransaction; FSTPBWriteBatch *message = [FSTPBWriteBatch message]; message.batchId = 42; - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testToString"); std::string description = transaction.ToString(); - XCTAssertEqual(description, ""); + XCTAssertEqual(description, ""); transaction.Put(key, message); description = transaction.ToString(); XCTAssertEqual(description, - ""); std::string key2 = LevelDbMutationKey::Key("user1", 43); transaction.Delete(key2); description = transaction.ToString(); XCTAssertEqual(description, - ""); } diff --git a/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm b/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm index 465a2cb..b228657 100644 --- a/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm +++ b/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm @@ -21,7 +21,6 @@ #import "Firestore/Source/Local/FSTEagerGarbageCollector.h" #import "Firestore/Source/Local/FSTMutationQueue.h" #import "Firestore/Source/Local/FSTPersistence.h" -#import "Firestore/Source/Local/FSTWriteGroup.h" #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Model/FSTMutationBatch.h" @@ -56,32 +55,24 @@ NS_ASSUME_NONNULL_BEGIN - (void)testCountBatches { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Count batches"]; - XCTAssertEqual(0, [self batchCount]); - XCTAssertTrue([self.mutationQueue isEmpty]); - [self.persistence commitGroup:group]; - - FSTMutationBatch *batch1 = [self addMutationBatch]; - group = [self.persistence startGroupWithAction:@"Count batches"]; - XCTAssertEqual(1, [self batchCount]); - XCTAssertFalse([self.mutationQueue isEmpty]); - [self.persistence commitGroup:group]; - - FSTMutationBatch *batch2 = [self addMutationBatch]; - group = [self.persistence startGroupWithAction:@"Count batches"]; - XCTAssertEqual(2, [self batchCount]); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batch2 ]]; - group = [self.persistence startGroupWithAction:@"Count batches"]; - XCTAssertEqual(1, [self batchCount]); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batch1 ]]; - group = [self.persistence startGroupWithAction:@"Count batches"]; - XCTAssertEqual(0, [self batchCount]); - XCTAssertTrue([self.mutationQueue isEmpty]); - [self.persistence commitGroup:group]; + self.persistence.run("testCountBatches", [&]() { + XCTAssertEqual(0, [self batchCount]); + XCTAssertTrue([self.mutationQueue isEmpty]); + + FSTMutationBatch *batch1 = [self addMutationBatch]; + XCTAssertEqual(1, [self batchCount]); + XCTAssertFalse([self.mutationQueue isEmpty]); + + FSTMutationBatch *batch2 = [self addMutationBatch]; + XCTAssertEqual(2, [self batchCount]); + + [self.mutationQueue removeMutationBatches:@[ batch2 ]]; + XCTAssertEqual(1, [self batchCount]); + + [self.mutationQueue removeMutationBatches:@[ batch1 ]]; + XCTAssertEqual(0, [self batchCount]); + XCTAssertTrue([self.mutationQueue isEmpty]); + }); } - (void)testAcknowledgeBatchID { @@ -91,334 +82,327 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); // Adding mutation batches should not change the highest acked batchID. - FSTMutationBatch *batch1 = [self addMutationBatch]; - FSTMutationBatch *batch2 = [self addMutationBatch]; - FSTMutationBatch *batch3 = [self addMutationBatch]; - XCTAssertGreaterThan(batch1.batchID, kFSTBatchIDUnknown); - XCTAssertGreaterThan(batch2.batchID, batch1.batchID); - XCTAssertGreaterThan(batch3.batchID, batch2.batchID); - - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); - - [self acknowledgeBatch:batch1]; - [self acknowledgeBatch:batch2]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); - - [self removeMutationBatches:@[ batch1 ]]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); - - [self removeMutationBatches:@[ batch2 ]]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); - - // Batch 3 never acknowledged. - [self removeMutationBatches:@[ batch3 ]]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + self.persistence.run("testAcknowledgeBatchID", [&]() { + FSTMutationBatch *batch1 = [self addMutationBatch]; + FSTMutationBatch *batch2 = [self addMutationBatch]; + FSTMutationBatch *batch3 = [self addMutationBatch]; + XCTAssertGreaterThan(batch1.batchID, kFSTBatchIDUnknown); + XCTAssertGreaterThan(batch2.batchID, batch1.batchID); + XCTAssertGreaterThan(batch3.batchID, batch2.batchID); + + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); + + [self.mutationQueue acknowledgeBatch:batch1 streamToken:nil]; + [self.mutationQueue acknowledgeBatch:batch2 streamToken:nil]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + + [self.mutationQueue removeMutationBatches:@[ batch1 ]]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + + [self.mutationQueue removeMutationBatches:@[ batch2 ]]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + + // Batch 3 never acknowledged. + [self.mutationQueue removeMutationBatches:@[ batch3 ]]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + }); } - (void)testAcknowledgeThenRemove { if ([self isTestBaseClass]) return; - FSTMutationBatch *batch1 = [self addMutationBatch]; + self.persistence.run("testAcknowledgeThenRemove", [&]() { + FSTMutationBatch *batch1 = [self addMutationBatch]; - FSTWriteGroup *group = [self.persistence startGroupWithAction:NSStringFromSelector(_cmd)]; - [self.mutationQueue acknowledgeBatch:batch1 streamToken:nil]; - [self.mutationQueue removeMutationBatches:@[ batch1 ]]; + [self.mutationQueue acknowledgeBatch:batch1 streamToken:nil]; + [self.mutationQueue removeMutationBatches:@[ batch1 ]]; - XCTAssertEqual([self batchCount], 0); - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch1.batchID); - [self.persistence commitGroup:group]; + XCTAssertEqual([self batchCount], 0); + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch1.batchID); + }); } - (void)testHighestAcknowledgedBatchIDNeverExceedsNextBatchID { if ([self isTestBaseClass]) return; - FSTMutationBatch *batch1 = [self addMutationBatch]; - FSTMutationBatch *batch2 = [self addMutationBatch]; - [self acknowledgeBatch:batch1]; - [self acknowledgeBatch:batch2]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + FSTMutationBatch *batch1 = + self.persistence.run("testHighestAcknowledgedBatchIDNeverExceedsNextBatchID batch1", + [&]() -> FSTMutationBatch * { return [self addMutationBatch]; }); + FSTMutationBatch *batch2 = + self.persistence.run("testHighestAcknowledgedBatchIDNeverExceedsNextBatchID batch2", + [&]() -> FSTMutationBatch * { return [self addMutationBatch]; }); - [self removeMutationBatches:@[ batch1, batch2 ]]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + self.persistence.run("testHighestAcknowledgedBatchIDNeverExceedsNextBatchID", [&]() { + [self.mutationQueue acknowledgeBatch:batch1 streamToken:nil]; + [self.mutationQueue acknowledgeBatch:batch2 streamToken:nil]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + + [self.mutationQueue removeMutationBatches:@[ batch1, batch2 ]]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + }); // Restart the queue so that nextBatchID will be reset. - self.mutationQueue = [self.persistence mutationQueueForUser:User("user")]; + FSTMutationBatch *batch = self.persistence.run( + "testHighestAcknowledgedBatchIDNeverExceedsNextBatchID restart", [&]() -> FSTMutationBatch * { + self.mutationQueue = [self.persistence mutationQueueForUser:User("user")]; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Start MutationQueue"]; - [self.mutationQueue start]; - [self.persistence commitGroup:group]; + [self.mutationQueue start]; - // Verify that on restart with an empty queue, nextBatchID falls to a lower value. - XCTAssertLessThan(self.mutationQueue.nextBatchID, batch2.batchID); + // Verify that on restart with an empty queue, nextBatchID falls to a lower value. + XCTAssertLessThan(self.mutationQueue.nextBatchID, batch2.batchID); - // As a result highestAcknowledgedBatchID must also reset lower. - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); + // As a result highestAcknowledgedBatchID must also reset lower. + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); - // The mutation queue will reset the next batchID after all mutations are removed so adding - // another mutation will cause a collision. - FSTMutationBatch *newBatch = [self addMutationBatch]; - XCTAssertEqual(newBatch.batchID, batch1.batchID); + // The mutation queue will reset the next batchID after all mutations are removed so adding + // another mutation will cause a collision. + FSTMutationBatch *newBatch = [self addMutationBatch]; + XCTAssertEqual(newBatch.batchID, batch1.batchID); + return newBatch; + }); + self.persistence.run("testHighestAcknowledgedBatchIDNeverExceedsNextBatchID restart2", [&]() { + // Restart the queue with one unacknowledged batch in it. + [self.mutationQueue start]; - // Restart the queue with one unacknowledged batch in it. - self.persistence.run([&]() { [self.mutationQueue start]; }); + XCTAssertEqual([self.mutationQueue nextBatchID], batch.batchID + 1); - XCTAssertEqual([self.mutationQueue nextBatchID], newBatch.batchID + 1); - - // highestAcknowledgedBatchID must still be kFSTBatchIDUnknown. - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); + // highestAcknowledgedBatchID must still be kFSTBatchIDUnknown. + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); + }); } - (void)testLookupMutationBatch { if ([self isTestBaseClass]) return; // Searching on an empty queue should not find a non-existent batch - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"LookupMutationBatch"]; - FSTMutationBatch *notFound = [self.mutationQueue lookupMutationBatch:42]; - [self.persistence commitGroup:group]; - XCTAssertNil(notFound); - - NSMutableArray *batches = [self createBatches:10]; - NSArray *removed = [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; - - // After removing, a batch should not be found - group = [self.persistence startGroupWithAction:@"LookupMutationBatch"]; - for (NSUInteger i = 0; i < removed.count; i++) { - notFound = [self.mutationQueue lookupMutationBatch:removed[i].batchID]; + self.persistence.run("testLookupMutationBatch", [&]() { + FSTMutationBatch *notFound = [self.mutationQueue lookupMutationBatch:42]; XCTAssertNil(notFound); - } - // Remaining entries should still be found - for (FSTMutationBatch *batch in batches) { - FSTMutationBatch *found = [self.mutationQueue lookupMutationBatch:batch.batchID]; - XCTAssertEqual(found.batchID, batch.batchID); - } + NSMutableArray *batches = [self createBatches:10]; + NSArray *removed = [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; + + // After removing, a batch should not be found + for (NSUInteger i = 0; i < removed.count; i++) { + notFound = [self.mutationQueue lookupMutationBatch:removed[i].batchID]; + XCTAssertNil(notFound); + } - // Even on a nonempty queue searching should not find a non-existent batch - notFound = [self.mutationQueue lookupMutationBatch:42]; - XCTAssertNil(notFound); - [self.persistence commitGroup:group]; + // Remaining entries should still be found + for (FSTMutationBatch *batch in batches) { + FSTMutationBatch *found = [self.mutationQueue lookupMutationBatch:batch.batchID]; + XCTAssertEqual(found.batchID, batch.batchID); + } + + // Even on a nonempty queue searching should not find a non-existent batch + notFound = [self.mutationQueue lookupMutationBatch:42]; + XCTAssertNil(notFound); + }); } - (void)testNextMutationBatchAfterBatchID { if ([self isTestBaseClass]) return; - NSMutableArray *batches = [self createBatches:10]; - - // This is an array of successors assuming the removals below will happen: - NSArray *afters = @[ batches[3], batches[8], batches[8] ]; - NSArray *removed = [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; - - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"NextMutationBatchAfterBatchID"]; - for (NSUInteger i = 0; i < batches.count - 1; i++) { - FSTMutationBatch *current = batches[i]; - FSTMutationBatch *next = batches[i + 1]; - FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:current.batchID]; - XCTAssertEqual(found.batchID, next.batchID); - } - - for (NSUInteger i = 0; i < removed.count; i++) { - FSTMutationBatch *current = removed[i]; - FSTMutationBatch *next = afters[i]; - FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:current.batchID]; - XCTAssertEqual(found.batchID, next.batchID); - } - - FSTMutationBatch *first = batches[0]; - FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:first.batchID - 42]; - XCTAssertEqual(found.batchID, first.batchID); - - FSTMutationBatch *last = batches[batches.count - 1]; - FSTMutationBatch *notFound = [self.mutationQueue nextMutationBatchAfterBatchID:last.batchID]; - XCTAssertNil(notFound); - [self.persistence commitGroup:group]; + self.persistence.run("testNextMutationBatchAfterBatchID", [&]() { + NSMutableArray *batches = [self createBatches:10]; + + // This is an array of successors assuming the removals below will happen: + NSArray *afters = @[ batches[3], batches[8], batches[8] ]; + NSArray *removed = [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; + + for (NSUInteger i = 0; i < batches.count - 1; i++) { + FSTMutationBatch *current = batches[i]; + FSTMutationBatch *next = batches[i + 1]; + FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:current.batchID]; + XCTAssertEqual(found.batchID, next.batchID); + } + + for (NSUInteger i = 0; i < removed.count; i++) { + FSTMutationBatch *current = removed[i]; + FSTMutationBatch *next = afters[i]; + FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:current.batchID]; + XCTAssertEqual(found.batchID, next.batchID); + } + + FSTMutationBatch *first = batches[0]; + FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:first.batchID - 42]; + XCTAssertEqual(found.batchID, first.batchID); + + FSTMutationBatch *last = batches[batches.count - 1]; + FSTMutationBatch *notFound = [self.mutationQueue nextMutationBatchAfterBatchID:last.batchID]; + XCTAssertNil(notFound); + }); } - (void)testNextMutationBatchAfterBatchIDSkipsAcknowledgedBatches { if ([self isTestBaseClass]) return; - NSMutableArray *batches = [self createBatches:3]; - FSTWriteGroup *group = [self.persistence - startGroupWithAction:@"NextMutationBatchAfterBatchIDSkipsAcknowledgedBatches"]; - XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:kFSTBatchIDUnknown], - batches[0]); - [self.persistence commitGroup:group]; - - [self acknowledgeBatch:batches[0]]; - group = [self.persistence - startGroupWithAction:@"NextMutationBatchAfterBatchIDSkipsAcknowledgedBatches"]; - XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:kFSTBatchIDUnknown], - batches[1]); - XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:batches[0].batchID], - batches[1]); - XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:batches[1].batchID], - batches[2]); - [self.persistence commitGroup:group]; + NSMutableArray *batches = self.persistence.run( + "testNextMutationBatchAfterBatchIDSkipsAcknowledgedBatches newBatches", + [&]() -> NSMutableArray * { + NSMutableArray *newBatches = [self createBatches:3]; + XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:kFSTBatchIDUnknown], + newBatches[0]); + return newBatches; + }); + self.persistence.run("testNextMutationBatchAfterBatchIDSkipsAcknowledgedBatches", [&]() { + + [self.mutationQueue acknowledgeBatch:batches[0] streamToken:nil]; + XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:kFSTBatchIDUnknown], + batches[1]); + XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:batches[0].batchID], + batches[1]); + XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:batches[1].batchID], + batches[2]); + }); } - (void)testAllMutationBatchesThroughBatchID { if ([self isTestBaseClass]) return; - NSMutableArray *batches = [self createBatches:10]; - [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; + self.persistence.run("testAllMutationBatchesThroughBatchID", [&]() { + NSMutableArray *batches = [self createBatches:10]; + [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; - NSArray *found, *expected; + NSArray *found, *expected; - FSTWriteGroup *group = - [self.persistence startGroupWithAction:@"AllMutationBatchesThroughBatchID"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:batches[0].batchID - 1]; - XCTAssertEqualObjects(found, (@[])); + found = [self.mutationQueue allMutationBatchesThroughBatchID:batches[0].batchID - 1]; + XCTAssertEqualObjects(found, (@[])); - for (NSUInteger i = 0; i < batches.count; i++) { - found = [self.mutationQueue allMutationBatchesThroughBatchID:batches[i].batchID]; - expected = [batches subarrayWithRange:NSMakeRange(0, i + 1)]; - XCTAssertEqualObjects(found, expected, @"for index %lu", (unsigned long)i); - } - [self.persistence commitGroup:group]; + for (NSUInteger i = 0; i < batches.count; i++) { + found = [self.mutationQueue allMutationBatchesThroughBatchID:batches[i].batchID]; + expected = [batches subarrayWithRange:NSMakeRange(0, i + 1)]; + XCTAssertEqualObjects(found, expected, @"for index %lu", (unsigned long)i); + } + }); } - (void)testAllMutationBatchesAffectingDocumentKey { if ([self isTestBaseClass]) return; - NSArray *mutations = @[ - FSTTestSetMutation(@"fob/bar", - @{ @"a" : @1 }), - FSTTestSetMutation(@"foo/bar", - @{ @"a" : @1 }), - FSTTestPatchMutation("foo/bar", - @{ @"b" : @1 }, {}), - FSTTestSetMutation(@"foo/bar/suffix/key", - @{ @"a" : @1 }), - FSTTestSetMutation(@"foo/baz", - @{ @"a" : @1 }), - FSTTestSetMutation(@"food/bar", - @{ @"a" : @1 }) - ]; - - // Store all the mutations. - NSMutableArray *batches = [NSMutableArray array]; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"New mutation batch"]; - for (FSTMutation *mutation in mutations) { - FSTMutationBatch *batch = - [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] - mutations:@[ mutation ]]; - [batches addObject:batch]; - } - - NSArray *expected = @[ batches[1], batches[2] ]; - NSArray *matches = - [self.mutationQueue allMutationBatchesAffectingDocumentKey:testutil::Key("foo/bar")]; - - [self.persistence commitGroup:group]; - XCTAssertEqualObjects(matches, expected); + self.persistence.run("testAllMutationBatchesAffectingDocumentKey", [&]() { + NSArray *mutations = @[ + FSTTestSetMutation(@"fob/bar", + @{ @"a" : @1 }), + FSTTestSetMutation(@"foo/bar", + @{ @"a" : @1 }), + FSTTestPatchMutation("foo/bar", + @{ @"b" : @1 }, {}), + FSTTestSetMutation(@"foo/bar/suffix/key", + @{ @"a" : @1 }), + FSTTestSetMutation(@"foo/baz", + @{ @"a" : @1 }), + FSTTestSetMutation(@"food/bar", + @{ @"a" : @1 }) + ]; + + // Store all the mutations. + NSMutableArray *batches = [NSMutableArray array]; + for (FSTMutation *mutation in mutations) { + FSTMutationBatch *batch = + [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] + mutations:@[ mutation ]]; + [batches addObject:batch]; + } + + NSArray *expected = @[ batches[1], batches[2] ]; + NSArray *matches = + [self.mutationQueue allMutationBatchesAffectingDocumentKey:testutil::Key("foo/bar")]; + + XCTAssertEqualObjects(matches, expected); + }); } - (void)testAllMutationBatchesAffectingQuery { if ([self isTestBaseClass]) return; - NSArray *mutations = @[ - FSTTestSetMutation(@"fob/bar", - @{ @"a" : @1 }), - FSTTestSetMutation(@"foo/bar", - @{ @"a" : @1 }), - FSTTestPatchMutation("foo/bar", - @{ @"b" : @1 }, {}), - FSTTestSetMutation(@"foo/bar/suffix/key", - @{ @"a" : @1 }), - FSTTestSetMutation(@"foo/baz", - @{ @"a" : @1 }), - FSTTestSetMutation(@"food/bar", - @{ @"a" : @1 }) - ]; - - // Store all the mutations. - NSMutableArray *batches = [NSMutableArray array]; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"New mutation batch"]; - for (FSTMutation *mutation in mutations) { - FSTMutationBatch *batch = - [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] - mutations:@[ mutation ]]; - [batches addObject:batch]; - } - - NSArray *expected = @[ batches[1], batches[2], batches[4] ]; - FSTQuery *query = FSTTestQuery("foo"); - NSArray *matches = - [self.mutationQueue allMutationBatchesAffectingQuery:query]; - [self.persistence commitGroup:group]; - - XCTAssertEqualObjects(matches, expected); + self.persistence.run("testAllMutationBatchesAffectingQuery", [&]() { + NSArray *mutations = @[ + FSTTestSetMutation(@"fob/bar", + @{ @"a" : @1 }), + FSTTestSetMutation(@"foo/bar", + @{ @"a" : @1 }), + FSTTestPatchMutation("foo/bar", + @{ @"b" : @1 }, {}), + FSTTestSetMutation(@"foo/bar/suffix/key", + @{ @"a" : @1 }), + FSTTestSetMutation(@"foo/baz", + @{ @"a" : @1 }), + FSTTestSetMutation(@"food/bar", + @{ @"a" : @1 }) + ]; + + // Store all the mutations. + NSMutableArray *batches = [NSMutableArray array]; + for (FSTMutation *mutation in mutations) { + FSTMutationBatch *batch = + [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] + mutations:@[ mutation ]]; + [batches addObject:batch]; + } + + NSArray *expected = @[ batches[1], batches[2], batches[4] ]; + FSTQuery *query = FSTTestQuery("foo"); + NSArray *matches = + [self.mutationQueue allMutationBatchesAffectingQuery:query]; + + XCTAssertEqualObjects(matches, expected); + }); } - (void)testRemoveMutationBatches { if ([self isTestBaseClass]) return; - NSMutableArray *batches = [self createBatches:10]; - FSTMutationBatch *last = batches[batches.count - 1]; - - [self removeMutationBatches:@[ batches[0] ]]; - [batches removeObjectAtIndex:0]; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - XCTAssertEqual([self batchCount], 9); - [self.persistence commitGroup:group]; - - NSArray *found; - - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; - XCTAssertEqualObjects(found, batches); - XCTAssertEqual(found.count, 9); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[0], batches[1], batches[2] ]]; - [batches removeObjectsInRange:NSMakeRange(0, 3)]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - XCTAssertEqual([self batchCount], 6); - [self.persistence commitGroup:group]; - - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; - XCTAssertEqualObjects(found, batches); - XCTAssertEqual(found.count, 6); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[batches.count - 1] ]]; - [batches removeObjectAtIndex:batches.count - 1]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - XCTAssertEqual([self batchCount], 5); - [self.persistence commitGroup:group]; - - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; - XCTAssertEqualObjects(found, batches); - XCTAssertEqual(found.count, 5); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[3] ]]; - [batches removeObjectAtIndex:3]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - XCTAssertEqual([self batchCount], 4); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[1] ]]; - [batches removeObjectAtIndex:1]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - XCTAssertEqual([self batchCount], 3); - [self.persistence commitGroup:group]; - - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; - XCTAssertEqualObjects(found, batches); - XCTAssertEqual(found.count, 3); - XCTAssertFalse([self.mutationQueue isEmpty]); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:batches]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; - XCTAssertEqualObjects(found, @[]); - XCTAssertEqual(found.count, 0); - XCTAssertTrue([self.mutationQueue isEmpty]); - [self.persistence commitGroup:group]; + self.persistence.run("testRemoveMutationBatches", [&]() { + NSMutableArray *batches = [self createBatches:10]; + + [self.mutationQueue removeMutationBatches:@[ batches[0] ]]; + [batches removeObjectAtIndex:0]; + + FSTMutationBatch *last = batches[batches.count - 1]; + XCTAssertEqual([self batchCount], 9); + + NSArray *found; + + found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; + XCTAssertEqualObjects(found, batches); + XCTAssertEqual(found.count, 9); + + [self.mutationQueue removeMutationBatches:@[ batches[0], batches[1], batches[2] ]]; + [batches removeObjectsInRange:NSMakeRange(0, 3)]; + XCTAssertEqual([self batchCount], 6); + + found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; + XCTAssertEqualObjects(found, batches); + XCTAssertEqual(found.count, 6); + + [self.mutationQueue removeMutationBatches:@[ batches[batches.count - 1] ]]; + [batches removeObjectAtIndex:batches.count - 1]; + XCTAssertEqual([self batchCount], 5); + + found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; + XCTAssertEqualObjects(found, batches); + XCTAssertEqual(found.count, 5); + + [self.mutationQueue removeMutationBatches:@[ batches[3] ]]; + [batches removeObjectAtIndex:3]; + XCTAssertEqual([self batchCount], 4); + + [self.mutationQueue removeMutationBatches:@[ batches[1] ]]; + [batches removeObjectAtIndex:1]; + XCTAssertEqual([self batchCount], 3); + + found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; + XCTAssertEqualObjects(found, batches); + XCTAssertEqual(found.count, 3); + XCTAssertFalse([self.mutationQueue isEmpty]); + + [self.mutationQueue removeMutationBatches:batches]; + found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; + XCTAssertEqualObjects(found, @[]); + XCTAssertEqual(found.count, 0); + XCTAssertTrue([self.mutationQueue isEmpty]); + }); } - (void)testRemoveMutationBatchesEmitsGarbageEvents { @@ -428,52 +412,41 @@ NS_ASSUME_NONNULL_BEGIN [garbageCollector addGarbageSource:self.mutationQueue]; NSMutableArray *batches = [NSMutableArray array]; - [batches addObjectsFromArray:@[ - [self addMutationBatchWithKey:@"foo/bar"], - [self addMutationBatchWithKey:@"foo/ba"], - [self addMutationBatchWithKey:@"foo/bar2"], - [self addMutationBatchWithKey:@"foo/bar"], - [self addMutationBatchWithKey:@"foo/bar/suffix/baz"], - [self addMutationBatchWithKey:@"bar/baz"], - ]]; - - [self removeMutationBatches:@[ batches[0] ]]; - FSTWriteGroup *group = - [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - std::set garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, std::set({})); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[1] ]]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, std::set({testutil::Key("foo/ba")})); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[5] ]]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, std::set({testutil::Key("bar/baz")})); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[2], batches[3] ]]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, - std::set({testutil::Key("foo/bar"), testutil::Key("foo/bar2")})); - [self.persistence commitGroup:group]; - - [batches addObject:[self addMutationBatchWithKey:@"foo/bar/suffix/baz"]]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, std::set({})); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[4], batches[6] ]]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, std::set({testutil::Key("foo/bar/suffix/baz")})); - [self.persistence commitGroup:group]; + self.persistence.run("testRemoveMutationBatchesEmitsGarbageEvents", [&]() { + [batches addObjectsFromArray:@[ + [self addMutationBatchWithKey:@"foo/bar"], + [self addMutationBatchWithKey:@"foo/ba"], + [self addMutationBatchWithKey:@"foo/bar2"], + [self addMutationBatchWithKey:@"foo/bar"], + [self addMutationBatchWithKey:@"foo/bar/suffix/baz"], + [self addMutationBatchWithKey:@"bar/baz"], + ]]; + + [self.mutationQueue removeMutationBatches:@[ batches[0] ]]; + std::set garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, std::set({})); + + [self.mutationQueue removeMutationBatches:@[ batches[1] ]]; + garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, std::set({testutil::Key("foo/ba")})); + + [self.mutationQueue removeMutationBatches:@[ batches[5] ]]; + garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, std::set({testutil::Key("bar/baz")})); + + [self.mutationQueue removeMutationBatches:@[ batches[2], batches[3] ]]; + garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, + std::set({testutil::Key("foo/bar"), testutil::Key("foo/bar2")})); + + [batches addObject:[self addMutationBatchWithKey:@"foo/bar/suffix/baz"]]; + garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, std::set({})); + + [self.mutationQueue removeMutationBatches:@[ batches[4], batches[6] ]]; + garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, std::set({testutil::Key("foo/bar/suffix/baz")})); + }); } - (void)testStreamToken { @@ -482,20 +455,22 @@ NS_ASSUME_NONNULL_BEGIN NSData *streamToken1 = [@"token1" dataUsingEncoding:NSUTF8StringEncoding]; NSData *streamToken2 = [@"token2" dataUsingEncoding:NSUTF8StringEncoding]; - self.persistence.run([&]() { [self.mutationQueue setLastStreamToken:streamToken1]; }); - - FSTMutationBatch *batch1 = [self addMutationBatch]; - [self addMutationBatch]; + self.persistence.run("testStreamToken", [&]() { + [self.mutationQueue setLastStreamToken:streamToken1]; - XCTAssertEqualObjects([self.mutationQueue lastStreamToken], streamToken1); + FSTMutationBatch *batch1 = [self addMutationBatch]; + [self addMutationBatch]; - self.persistence.run( - [&]() { [self.mutationQueue acknowledgeBatch:batch1 streamToken:streamToken2]; }); + XCTAssertEqualObjects([self.mutationQueue lastStreamToken], streamToken1); - XCTAssertEqual(self.mutationQueue.highestAcknowledgedBatchID, batch1.batchID); - XCTAssertEqualObjects([self.mutationQueue lastStreamToken], streamToken2); + [self.mutationQueue acknowledgeBatch:batch1 streamToken:streamToken2]; + XCTAssertEqual(self.mutationQueue.highestAcknowledgedBatchID, batch1.batchID); + XCTAssertEqualObjects([self.mutationQueue lastStreamToken], streamToken2); + }); } +#pragma mark - Helpers + /** Creates a new FSTMutationBatch with the next batch ID and a set of dummy mutations. */ - (FSTMutationBatch *)addMutationBatch { return [self addMutationBatchWithKey:@"foo/bar"]; @@ -508,11 +483,9 @@ NS_ASSUME_NONNULL_BEGIN - (FSTMutationBatch *)addMutationBatchWithKey:(NSString *)key { FSTSetMutation *mutation = FSTTestSetMutation(key, @{ @"a" : @1 }); - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"New mutation batch"]; FSTMutationBatch *batch = [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] mutations:@[ mutation ]]; - [self.persistence commitGroup:group]; return batch; } @@ -531,23 +504,6 @@ NS_ASSUME_NONNULL_BEGIN return batches; } -/** - * Calls -acknowledgeBatch:streamToken:group: on the mutation queue in a new group and commits the - * the group. - */ -// TODO(gsoltis): delete this helper, just run it in a transaction directly -- (void)acknowledgeBatch:(FSTMutationBatch *)batch { - self.persistence.run([&]() { [self.mutationQueue acknowledgeBatch:batch streamToken:nil]; }); -} - -/** - * Calls -removeMutationBatches:group: on the mutation queue in a new group and commits the group. - */ -// TODO(gsoltis): delete this helper, just run it in a transaction directly -- (void)removeMutationBatches:(NSArray *)batches { - self.persistence.run([&]() { [self.mutationQueue removeMutationBatches:batches]; }); -} - /** Returns the number of mutation batches in the mutation queue. */ - (NSUInteger)batchCount { return [self.mutationQueue allMutationBatches].count; @@ -567,7 +523,7 @@ NS_ASSUME_NONNULL_BEGIN for (NSUInteger i = 0; i < holes.count; i++) { NSUInteger index = holes[i].unsignedIntegerValue - i; FSTMutationBatch *batch = batches[index]; - [self removeMutationBatches:@[ batch ]]; + [self.mutationQueue removeMutationBatches:@[ batch ]]; [batches removeObjectAtIndex:index]; [removed addObject:batch]; diff --git a/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm b/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm index b575004..956afa9 100644 --- a/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm +++ b/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm @@ -21,7 +21,6 @@ #import "Firestore/Source/Local/FSTEagerGarbageCollector.h" #import "Firestore/Source/Local/FSTPersistence.h" #import "Firestore/Source/Local/FSTQueryData.h" -#import "Firestore/Source/Local/FSTWriteGroup.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" #import "Firestore/third_party/Immutable/Tests/FSTImmutableSortedSet+Testing.h" @@ -61,339 +60,344 @@ NS_ASSUME_NONNULL_BEGIN - (void)testReadQueryNotInCache { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"ReadQueryNotInCache"]; - XCTAssertNil([self.queryCache queryDataForQuery:_queryRooms]); - [self.persistence commitGroup:group]; + self.persistence.run("testReadQueryNotInCache", + [&]() { XCTAssertNil([self.queryCache queryDataForQuery:_queryRooms]); }); } - (void)testSetAndReadAQuery { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"SetAndReadQuery"]; - FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms]; - [self.queryCache addQueryData:queryData]; + self.persistence.run("testSetAndReadAQuery", [&]() { + FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms]; + [self.queryCache addQueryData:queryData]; - FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; - XCTAssertEqualObjects(result.query, queryData.query); - XCTAssertEqual(result.targetID, queryData.targetID); - XCTAssertEqualObjects(result.resumeToken, queryData.resumeToken); - [self.persistence commitGroup:group]; + FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; + XCTAssertEqualObjects(result.query, queryData.query); + XCTAssertEqual(result.targetID, queryData.targetID); + XCTAssertEqualObjects(result.resumeToken, queryData.resumeToken); + }); } - (void)testCanonicalIDCollision { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"CanonicalIDCollision"]; - // Type information is currently lost in our canonicalID implementations so this currently an - // easy way to force colliding canonicalIDs - FSTQuery *q1 = [FSTTestQuery("a") queryByAddingFilter:FSTTestFilter("foo", @"==", @(1))]; - FSTQuery *q2 = [FSTTestQuery("a") queryByAddingFilter:FSTTestFilter("foo", @"==", @"1")]; - XCTAssertEqualObjects(q1.canonicalID, q2.canonicalID); - - FSTQueryData *data1 = [self queryDataWithQuery:q1]; - [self.queryCache addQueryData:data1]; - - // Using the other query should not return the query cache entry despite equal canonicalIDs. - XCTAssertNil([self.queryCache queryDataForQuery:q2]); - XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1); - - FSTQueryData *data2 = [self queryDataWithQuery:q2]; - [self.queryCache addQueryData:data2]; - XCTAssertEqual([self.queryCache count], 2); - - XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1); - XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2); - - [self.queryCache removeQueryData:data1]; - XCTAssertNil([self.queryCache queryDataForQuery:q1]); - XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2); - XCTAssertEqual([self.queryCache count], 1); - - [self.queryCache removeQueryData:data2]; - XCTAssertNil([self.queryCache queryDataForQuery:q1]); - XCTAssertNil([self.queryCache queryDataForQuery:q2]); - XCTAssertEqual([self.queryCache count], 0); - [self.persistence commitGroup:group]; + self.persistence.run("testCanonicalIDCollision", [&]() { + // Type information is currently lost in our canonicalID implementations so this currently an + // easy way to force colliding canonicalIDs + FSTQuery *q1 = [FSTTestQuery("a") queryByAddingFilter:FSTTestFilter("foo", @"==", @(1))]; + FSTQuery *q2 = [FSTTestQuery("a") queryByAddingFilter:FSTTestFilter("foo", @"==", @"1")]; + XCTAssertEqualObjects(q1.canonicalID, q2.canonicalID); + + FSTQueryData *data1 = [self queryDataWithQuery:q1]; + [self.queryCache addQueryData:data1]; + + // Using the other query should not return the query cache entry despite equal canonicalIDs. + XCTAssertNil([self.queryCache queryDataForQuery:q2]); + XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1); + + FSTQueryData *data2 = [self queryDataWithQuery:q2]; + [self.queryCache addQueryData:data2]; + XCTAssertEqual([self.queryCache count], 2); + + XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1); + XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2); + + [self.queryCache removeQueryData:data1]; + XCTAssertNil([self.queryCache queryDataForQuery:q1]); + XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2); + XCTAssertEqual([self.queryCache count], 1); + + [self.queryCache removeQueryData:data2]; + XCTAssertNil([self.queryCache queryDataForQuery:q1]); + XCTAssertNil([self.queryCache queryDataForQuery:q2]); + XCTAssertEqual([self.queryCache count], 0); + }); } - (void)testSetQueryToNewValue { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"SetQueryToNewValue"]; - FSTQueryData *queryData1 = - [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:1]; - [self.queryCache addQueryData:queryData1]; - - FSTQueryData *queryData2 = - [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:2]; - [self.queryCache addQueryData:queryData2]; - - FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; - XCTAssertNotEqualObjects(queryData2.resumeToken, queryData1.resumeToken); - XCTAssertNotEqualObjects(queryData2.snapshotVersion, queryData1.snapshotVersion); - XCTAssertEqualObjects(result.resumeToken, queryData2.resumeToken); - XCTAssertEqualObjects(result.snapshotVersion, queryData2.snapshotVersion); - [self.persistence commitGroup:group]; + self.persistence.run("testSetQueryToNewValue", [&]() { + FSTQueryData *queryData1 = + [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:1]; + [self.queryCache addQueryData:queryData1]; + + FSTQueryData *queryData2 = + [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:2]; + [self.queryCache addQueryData:queryData2]; + + FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; + XCTAssertNotEqualObjects(queryData2.resumeToken, queryData1.resumeToken); + XCTAssertNotEqualObjects(queryData2.snapshotVersion, queryData1.snapshotVersion); + XCTAssertEqualObjects(result.resumeToken, queryData2.resumeToken); + XCTAssertEqualObjects(result.snapshotVersion, queryData2.snapshotVersion); + }); } - (void)testRemoveQuery { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveQuery"]; - FSTQueryData *queryData1 = [self queryDataWithQuery:_queryRooms]; - [self.queryCache addQueryData:queryData1]; + self.persistence.run("testRemoveQuery", [&]() { + FSTQueryData *queryData1 = [self queryDataWithQuery:_queryRooms]; + [self.queryCache addQueryData:queryData1]; - [self.queryCache removeQueryData:queryData1]; + [self.queryCache removeQueryData:queryData1]; - FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; - XCTAssertNil(result); - [self.persistence commitGroup:group]; + FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; + XCTAssertNil(result); + }); } - (void)testRemoveNonExistentQuery { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveNonExistentQuery"]; - FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms]; + self.persistence.run("testRemoveNonExistentQuery", [&]() { + FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms]; - // no-op, but make sure it doesn't throw. - XCTAssertNoThrow([self.queryCache removeQueryData:queryData]); - [self.persistence commitGroup:group]; + // no-op, but make sure it doesn't throw. + XCTAssertNoThrow([self.queryCache removeQueryData:queryData]); + }); } - (void)testRemoveQueryRemovesMatchingKeysToo { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = - [self.persistence startGroupWithAction:@"RemoveQueryRemovesMatchingKeysToo"]; - FSTQueryData *rooms = [self queryDataWithQuery:_queryRooms]; - [self.queryCache addQueryData:rooms]; + self.persistence.run("testRemoveQueryRemovesMatchingKeysToo", [&]() { + FSTQueryData *rooms = [self queryDataWithQuery:_queryRooms]; + [self.queryCache addQueryData:rooms]; - DocumentKey key1 = testutil::Key("rooms/foo"); - DocumentKey key2 = testutil::Key("rooms/bar"); - [self addMatchingKey:key1 forTargetID:rooms.targetID]; - [self addMatchingKey:key2 forTargetID:rooms.targetID]; + DocumentKey key1 = testutil::Key("rooms/foo"); + DocumentKey key2 = testutil::Key("rooms/bar"); + [self addMatchingKey:key1 forTargetID:rooms.targetID]; + [self addMatchingKey:key2 forTargetID:rooms.targetID]; - XCTAssertTrue([self.queryCache containsKey:key1]); - XCTAssertTrue([self.queryCache containsKey:key2]); + XCTAssertTrue([self.queryCache containsKey:key1]); + XCTAssertTrue([self.queryCache containsKey:key2]); - [self.queryCache removeQueryData:rooms]; - XCTAssertFalse([self.queryCache containsKey:key1]); - XCTAssertFalse([self.queryCache containsKey:key2]); - [self.persistence commitGroup:group]; + [self.queryCache removeQueryData:rooms]; + XCTAssertFalse([self.queryCache containsKey:key1]); + XCTAssertFalse([self.queryCache containsKey:key2]); + }); } - (void)testAddOrRemoveMatchingKeys { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"AddOrRemoveMatchingKeys"]; - DocumentKey key = testutil::Key("foo/bar"); + self.persistence.run("testAddOrRemoveMatchingKeys", [&]() { + DocumentKey key = testutil::Key("foo/bar"); - XCTAssertFalse([self.queryCache containsKey:key]); + XCTAssertFalse([self.queryCache containsKey:key]); - [self addMatchingKey:key forTargetID:1]; - XCTAssertTrue([self.queryCache containsKey:key]); + [self addMatchingKey:key forTargetID:1]; + XCTAssertTrue([self.queryCache containsKey:key]); - [self addMatchingKey:key forTargetID:2]; - XCTAssertTrue([self.queryCache containsKey:key]); + [self addMatchingKey:key forTargetID:2]; + XCTAssertTrue([self.queryCache containsKey:key]); - [self removeMatchingKey:key forTargetID:1]; - XCTAssertTrue([self.queryCache containsKey:key]); + [self removeMatchingKey:key forTargetID:1]; + XCTAssertTrue([self.queryCache containsKey:key]); - [self removeMatchingKey:key forTargetID:2]; - XCTAssertFalse([self.queryCache containsKey:key]); - [self.persistence commitGroup:group]; + [self removeMatchingKey:key forTargetID:2]; + XCTAssertFalse([self.queryCache containsKey:key]); + }); } - (void)testRemoveMatchingKeysForTargetID { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveMatchingKeysForTargetID"]; - DocumentKey key1 = testutil::Key("foo/bar"); - DocumentKey key2 = testutil::Key("foo/baz"); - DocumentKey key3 = testutil::Key("foo/blah"); - - [self addMatchingKey:key1 forTargetID:1]; - [self addMatchingKey:key2 forTargetID:1]; - [self addMatchingKey:key3 forTargetID:2]; - XCTAssertTrue([self.queryCache containsKey:key1]); - XCTAssertTrue([self.queryCache containsKey:key2]); - XCTAssertTrue([self.queryCache containsKey:key3]); - - [self.queryCache removeMatchingKeysForTargetID:1]; - XCTAssertFalse([self.queryCache containsKey:key1]); - XCTAssertFalse([self.queryCache containsKey:key2]); - XCTAssertTrue([self.queryCache containsKey:key3]); - - [self.queryCache removeMatchingKeysForTargetID:2]; - XCTAssertFalse([self.queryCache containsKey:key1]); - XCTAssertFalse([self.queryCache containsKey:key2]); - XCTAssertFalse([self.queryCache containsKey:key3]); - [self.persistence commitGroup:group]; + self.persistence.run("testRemoveMatchingKeysForTargetID", [&]() { + DocumentKey key1 = testutil::Key("foo/bar"); + DocumentKey key2 = testutil::Key("foo/baz"); + DocumentKey key3 = testutil::Key("foo/blah"); + + [self addMatchingKey:key1 forTargetID:1]; + [self addMatchingKey:key2 forTargetID:1]; + [self addMatchingKey:key3 forTargetID:2]; + XCTAssertTrue([self.queryCache containsKey:key1]); + XCTAssertTrue([self.queryCache containsKey:key2]); + XCTAssertTrue([self.queryCache containsKey:key3]); + + [self.queryCache removeMatchingKeysForTargetID:1]; + XCTAssertFalse([self.queryCache containsKey:key1]); + XCTAssertFalse([self.queryCache containsKey:key2]); + XCTAssertTrue([self.queryCache containsKey:key3]); + + [self.queryCache removeMatchingKeysForTargetID:2]; + XCTAssertFalse([self.queryCache containsKey:key1]); + XCTAssertFalse([self.queryCache containsKey:key2]); + XCTAssertFalse([self.queryCache containsKey:key3]); + }); } - (void)testRemoveEmitsGarbageEvents { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveEmitsGarbageEvents"]; - FSTEagerGarbageCollector *garbageCollector = [[FSTEagerGarbageCollector alloc] init]; - [garbageCollector addGarbageSource:self.queryCache]; - XCTAssertEqual([garbageCollector collectGarbage], std::set({})); + self.persistence.run("testRemoveEmitsGarbageEvents", [&]() { + FSTEagerGarbageCollector *garbageCollector = [[FSTEagerGarbageCollector alloc] init]; + [garbageCollector addGarbageSource:self.queryCache]; + XCTAssertEqual([garbageCollector collectGarbage], std::set({})); - FSTQueryData *rooms = [self queryDataWithQuery:FSTTestQuery("rooms")]; - DocumentKey room1 = testutil::Key("rooms/bar"); - DocumentKey room2 = testutil::Key("rooms/foo"); - [self.queryCache addQueryData:rooms]; - [self addMatchingKey:room1 forTargetID:rooms.targetID]; - [self addMatchingKey:room2 forTargetID:rooms.targetID]; + FSTQueryData *rooms = [self queryDataWithQuery:FSTTestQuery("rooms")]; + DocumentKey room1 = testutil::Key("rooms/bar"); + DocumentKey room2 = testutil::Key("rooms/foo"); + [self.queryCache addQueryData:rooms]; + [self addMatchingKey:room1 forTargetID:rooms.targetID]; + [self addMatchingKey:room2 forTargetID:rooms.targetID]; - FSTQueryData *halls = [self queryDataWithQuery:FSTTestQuery("halls")]; - DocumentKey hall1 = testutil::Key("halls/bar"); - DocumentKey hall2 = testutil::Key("halls/foo"); - [self.queryCache addQueryData:halls]; - [self addMatchingKey:hall1 forTargetID:halls.targetID]; - [self addMatchingKey:hall2 forTargetID:halls.targetID]; + FSTQueryData *halls = [self queryDataWithQuery:FSTTestQuery("halls")]; + DocumentKey hall1 = testutil::Key("halls/bar"); + DocumentKey hall2 = testutil::Key("halls/foo"); + [self.queryCache addQueryData:halls]; + [self addMatchingKey:hall1 forTargetID:halls.targetID]; + [self addMatchingKey:hall2 forTargetID:halls.targetID]; - XCTAssertEqual([garbageCollector collectGarbage], std::set({})); + XCTAssertEqual([garbageCollector collectGarbage], std::set({})); - [self removeMatchingKey:room1 forTargetID:rooms.targetID]; - XCTAssertEqual([garbageCollector collectGarbage], std::set({room1})); + [self removeMatchingKey:room1 forTargetID:rooms.targetID]; + XCTAssertEqual([garbageCollector collectGarbage], std::set({room1})); - [self.queryCache removeQueryData:rooms]; - XCTAssertEqual([garbageCollector collectGarbage], std::set({room2})); + [self.queryCache removeQueryData:rooms]; + XCTAssertEqual([garbageCollector collectGarbage], std::set({room2})); - [self.queryCache removeMatchingKeysForTargetID:halls.targetID]; - XCTAssertEqual([garbageCollector collectGarbage], std::set({hall1, hall2})); - [self.persistence commitGroup:group]; + [self.queryCache removeMatchingKeysForTargetID:halls.targetID]; + XCTAssertEqual([garbageCollector collectGarbage], std::set({hall1, hall2})); + }); } - (void)testMatchingKeysForTargetID { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"MatchingKeysForTargetID"]; - DocumentKey key1 = testutil::Key("foo/bar"); - DocumentKey key2 = testutil::Key("foo/baz"); - DocumentKey key3 = testutil::Key("foo/blah"); + self.persistence.run("testMatchingKeysForTargetID", [&]() { + DocumentKey key1 = testutil::Key("foo/bar"); + DocumentKey key2 = testutil::Key("foo/baz"); + DocumentKey key3 = testutil::Key("foo/blah"); - [self addMatchingKey:key1 forTargetID:1]; - [self addMatchingKey:key2 forTargetID:1]; - [self addMatchingKey:key3 forTargetID:2]; + [self addMatchingKey:key1 forTargetID:1]; + [self addMatchingKey:key2 forTargetID:1]; + [self addMatchingKey:key3 forTargetID:2]; - FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:1], (@[ key1, key2 ])); - FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:2], @[ key3 ]); + FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:1], (@[ key1, key2 ])); + FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:2], @[ key3 ]); - [self addMatchingKey:key1 forTargetID:2]; - FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:1], (@[ key1, key2 ])); - FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:2], (@[ key1, key3 ])); - [self.persistence commitGroup:group]; + [self addMatchingKey:key1 forTargetID:2]; + FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:1], (@[ key1, key2 ])); + FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:2], (@[ key1, key3 ])); + }); } - (void)testHighestListenSequenceNumber { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"HighestListenSequenceNumber"]; - FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("rooms") - targetID:1 - listenSequenceNumber:10 - purpose:FSTQueryPurposeListen]; - [self.queryCache addQueryData:query1]; - FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("halls") - targetID:2 - listenSequenceNumber:20 - purpose:FSTQueryPurposeListen]; - [self.queryCache addQueryData:query2]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20); - - // TargetIDs never come down. - [self.queryCache removeQueryData:query2]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20); - - // A query with an empty result set still counts. - FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("garages") - targetID:42 - listenSequenceNumber:100 - purpose:FSTQueryPurposeListen]; - [self.queryCache addQueryData:query3]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); - - [self.queryCache removeQueryData:query1]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); - - [self.queryCache removeQueryData:query3]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); - [self.persistence commitGroup:group]; + self.persistence.run("testHighestListenSequenceNumber", [&]() { + FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("rooms") + targetID:1 + listenSequenceNumber:10 + purpose:FSTQueryPurposeListen]; + [self.queryCache addQueryData:query1]; + FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("halls") + targetID:2 + listenSequenceNumber:20 + purpose:FSTQueryPurposeListen]; + [self.queryCache addQueryData:query2]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20); + + // TargetIDs never come down. + [self.queryCache removeQueryData:query2]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20); + + // A query with an empty result set still counts. + FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("garages") + targetID:42 + listenSequenceNumber:100 + purpose:FSTQueryPurposeListen]; + [self.queryCache addQueryData:query3]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + + [self.queryCache removeQueryData:query1]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + + [self.queryCache removeQueryData:query3]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + }); // Verify that the highestTargetID even survives restarts. - self.queryCache = [self.persistence queryCache]; - [self.queryCache start]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + self.persistence.run("testHighestListenSequenceNumber restart", [&]() { + self.queryCache = [self.persistence queryCache]; + [self.queryCache start]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + }); } - (void)testHighestTargetID { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveMatchingKeysForTargetID"]; - XCTAssertEqual([self.queryCache highestTargetID], 0); - - FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("rooms") - targetID:1 - listenSequenceNumber:10 - purpose:FSTQueryPurposeListen]; - DocumentKey key1 = testutil::Key("rooms/bar"); - DocumentKey key2 = testutil::Key("rooms/foo"); - [self.queryCache addQueryData:query1]; - [self addMatchingKey:key1 forTargetID:1]; - [self addMatchingKey:key2 forTargetID:1]; - - FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("halls") - targetID:2 - listenSequenceNumber:20 - purpose:FSTQueryPurposeListen]; - DocumentKey key3 = testutil::Key("halls/foo"); - [self.queryCache addQueryData:query2]; - [self addMatchingKey:key3 forTargetID:2]; - XCTAssertEqual([self.queryCache highestTargetID], 2); - - // TargetIDs never come down. - [self.queryCache removeQueryData:query2]; - XCTAssertEqual([self.queryCache highestTargetID], 2); - - // A query with an empty result set still counts. - FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("garages") - targetID:42 - listenSequenceNumber:100 - purpose:FSTQueryPurposeListen]; - [self.queryCache addQueryData:query3]; - XCTAssertEqual([self.queryCache highestTargetID], 42); - - [self.queryCache removeQueryData:query1]; - XCTAssertEqual([self.queryCache highestTargetID], 42); - - [self.queryCache removeQueryData:query3]; - XCTAssertEqual([self.queryCache highestTargetID], 42); - [self.persistence commitGroup:group]; + self.persistence.run("testHighestTargetID", [&]() { + XCTAssertEqual([self.queryCache highestTargetID], 0); + + FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("rooms") + targetID:1 + listenSequenceNumber:10 + purpose:FSTQueryPurposeListen]; + DocumentKey key1 = testutil::Key("rooms/bar"); + DocumentKey key2 = testutil::Key("rooms/foo"); + [self.queryCache addQueryData:query1]; + [self addMatchingKey:key1 forTargetID:1]; + [self addMatchingKey:key2 forTargetID:1]; + + FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("halls") + targetID:2 + listenSequenceNumber:20 + purpose:FSTQueryPurposeListen]; + DocumentKey key3 = testutil::Key("halls/foo"); + [self.queryCache addQueryData:query2]; + [self addMatchingKey:key3 forTargetID:2]; + XCTAssertEqual([self.queryCache highestTargetID], 2); + + // TargetIDs never come down. + [self.queryCache removeQueryData:query2]; + XCTAssertEqual([self.queryCache highestTargetID], 2); + + // A query with an empty result set still counts. + FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("garages") + targetID:42 + listenSequenceNumber:100 + purpose:FSTQueryPurposeListen]; + [self.queryCache addQueryData:query3]; + XCTAssertEqual([self.queryCache highestTargetID], 42); + + [self.queryCache removeQueryData:query1]; + XCTAssertEqual([self.queryCache highestTargetID], 42); + + [self.queryCache removeQueryData:query3]; + XCTAssertEqual([self.queryCache highestTargetID], 42); + }); + // Verify that the highestTargetID even survives restarts. - self.queryCache = [self.persistence queryCache]; - [self.queryCache start]; - XCTAssertEqual([self.queryCache highestTargetID], 42); + self.persistence.run("testHighestTargetID restart", [&]() { + self.queryCache = [self.persistence queryCache]; + [self.queryCache start]; + XCTAssertEqual([self.queryCache highestTargetID], 42); + }); } - (void)testLastRemoteSnapshotVersion { if ([self isTestBaseClass]) return; - XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], - [FSTSnapshotVersion noVersion]); + self.persistence.run("testLastRemoteSnapshotVersion", [&]() { + XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], + [FSTSnapshotVersion noVersion]); - // Can set the snapshot version. - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"setLastRemoteSnapshotVersion"]; - [self.queryCache setLastRemoteSnapshotVersion:FSTTestVersion(42)]; - [self.persistence commitGroup:group]; - XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], FSTTestVersion(42)); + // Can set the snapshot version. + [self.queryCache setLastRemoteSnapshotVersion:FSTTestVersion(42)]; + XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], FSTTestVersion(42)); + }); // Snapshot version persists restarts. self.queryCache = [self.persistence queryCache]; - [self.queryCache start]; - XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], FSTTestVersion(42)); + self.persistence.run("testLastRemoteSnapshotVersion restart", [&]() { + [self.queryCache start]; + XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], FSTTestVersion(42)); + }); } #pragma mark - Helpers diff --git a/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm b/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm index e4c68fe..2e32591 100644 --- a/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm +++ b/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm @@ -18,7 +18,6 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Local/FSTPersistence.h" -#import "Firestore/Source/Local/FSTWriteGroup.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTDocumentSet.h" @@ -52,14 +51,18 @@ static const int kVersion = 42; - (void)testReadDocumentNotInCache { if (!self.remoteDocumentCache) return; - XCTAssertNil([self readEntryAtPath:kDocPath]); + self.persistence.run("testReadDocumentNotInCache", [&]() { + XCTAssertNil([self.remoteDocumentCache entryForKey:testutil::Key(kDocPath)]); + }); } // Helper for next two tests. - (void)setAndReadADocumentAtPath:(const absl::string_view)path { - FSTDocument *written = [self setTestDocumentAtPath:path]; - FSTMaybeDocument *read = [self readEntryAtPath:path]; - XCTAssertEqualObjects(read, written); + self.persistence.run("setAndReadADocumentAtPath", [&]() { + FSTDocument *written = [self setTestDocumentAtPath:path]; + FSTMaybeDocument *read = [self.remoteDocumentCache entryForKey:testutil::Key(path)]; + XCTAssertEqualObjects(read, written); + }); } - (void)testSetAndReadADocument { @@ -77,58 +80,67 @@ static const int kVersion = 42; - (void)testSetAndReadDeletedDocument { if (!self.remoteDocumentCache) return; - FSTDeletedDocument *deletedDoc = FSTTestDeletedDoc(kDocPath, kVersion); - [self addEntry:deletedDoc]; + self.persistence.run("testSetAndReadDeletedDocument", [&]() { + FSTDeletedDocument *deletedDoc = FSTTestDeletedDoc(kDocPath, kVersion); + [self.remoteDocumentCache addEntry:deletedDoc]; - XCTAssertEqualObjects([self readEntryAtPath:kDocPath], deletedDoc); + XCTAssertEqualObjects([self.remoteDocumentCache entryForKey:testutil::Key(kDocPath)], + deletedDoc); + }); } - (void)testSetDocumentToNewValue { if (!self.remoteDocumentCache) return; - [self setTestDocumentAtPath:kDocPath]; - FSTDocument *newDoc = FSTTestDoc(kDocPath, kVersion, @{ @"data" : @2 }, NO); - [self addEntry:newDoc]; - XCTAssertEqualObjects([self readEntryAtPath:kDocPath], newDoc); + self.persistence.run("testSetDocumentToNewValue", [&]() { + [self setTestDocumentAtPath:kDocPath]; + FSTDocument *newDoc = FSTTestDoc(kDocPath, kVersion, @{ @"data" : @2 }, NO); + [self.remoteDocumentCache addEntry:newDoc]; + XCTAssertEqualObjects([self.remoteDocumentCache entryForKey:testutil::Key(kDocPath)], newDoc); + }); } - (void)testRemoveDocument { if (!self.remoteDocumentCache) return; - [self setTestDocumentAtPath:kDocPath]; - [self removeEntryAtPath:kDocPath]; + self.persistence.run("testRemoveDocument", [&]() { + [self setTestDocumentAtPath:kDocPath]; + [self.remoteDocumentCache removeEntryForKey:testutil::Key(kDocPath)]; - XCTAssertNil([self readEntryAtPath:kDocPath]); + XCTAssertNil([self.remoteDocumentCache entryForKey:testutil::Key(kDocPath)]); + }); } - (void)testRemoveNonExistentDocument { if (!self.remoteDocumentCache) return; - // no-op, but make sure it doesn't throw. - XCTAssertNoThrow([self removeEntryAtPath:kDocPath]); + self.persistence.run("testRemoveNonExistentDocument", [&]() { + // no-op, but make sure it doesn't throw. + XCTAssertNoThrow([self.remoteDocumentCache removeEntryForKey:testutil::Key(kDocPath)]); + }); } // TODO(mikelehen): Write more elaborate tests once we have more elaborate implementations. - (void)testDocumentsMatchingQuery { if (!self.remoteDocumentCache) return; - // TODO(rsgowman): This just verifies that we do a prefix scan against the - // query path. We'll need more tests once we add index support. - [self setTestDocumentAtPath:"a/1"]; - [self setTestDocumentAtPath:"b/1"]; - [self setTestDocumentAtPath:"b/2"]; - [self setTestDocumentAtPath:"c/1"]; - - FSTQuery *query = FSTTestQuery("b"); - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"DocumentsMatchingQuery"]; - FSTDocumentDictionary *results = [self.remoteDocumentCache documentsMatchingQuery:query]; - NSArray *expected = - @[ FSTTestDoc("b/1", kVersion, _kDocData, NO), FSTTestDoc("b/2", kVersion, _kDocData, NO) ]; - XCTAssertEqual([results count], [expected count]); - for (FSTDocument *doc in expected) { - XCTAssertEqualObjects([results objectForKey:doc.key], doc); - } - [self.persistence commitGroup:group]; + self.persistence.run("testDocumentsMatchingQuery", [&]() { + // TODO(rsgowman): This just verifies that we do a prefix scan against the + // query path. We'll need more tests once we add index support. + [self setTestDocumentAtPath:"a/1"]; + [self setTestDocumentAtPath:"b/1"]; + [self setTestDocumentAtPath:"b/2"]; + [self setTestDocumentAtPath:"c/1"]; + + FSTQuery *query = FSTTestQuery("b"); + FSTDocumentDictionary *results = [self.remoteDocumentCache documentsMatchingQuery:query]; + NSArray *expected = + @[ FSTTestDoc("b/1", kVersion, _kDocData, NO), FSTTestDoc("b/2", kVersion, _kDocData, NO) ]; + XCTAssertEqual([results count], [expected count]); + for (FSTDocument *doc in expected) { + XCTAssertEqualObjects([results objectForKey:doc.key], doc); + } + }); } #pragma mark - Helpers @@ -136,29 +148,10 @@ static const int kVersion = 42; - (FSTDocument *)setTestDocumentAtPath:(const absl::string_view)path { FSTDocument *doc = FSTTestDoc(path, kVersion, _kDocData, NO); - [self addEntry:doc]; + [self.remoteDocumentCache addEntry:doc]; return doc; } -- (void)addEntry:(FSTMaybeDocument *)maybeDoc { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"addEntry"]; - [self.remoteDocumentCache addEntry:maybeDoc]; - [self.persistence commitGroup:group]; -} - -- (FSTMaybeDocument *_Nullable)readEntryAtPath:(const absl::string_view)path { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"ReadEntryAtPath"]; - FSTMaybeDocument *result = [self.remoteDocumentCache entryForKey:testutil::Key(path)]; - [self.persistence commitGroup:group]; - return result; -} - -- (void)removeEntryAtPath:(const absl::string_view)path { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"removeEntryAtPath"]; - [self.remoteDocumentCache removeEntryForKey:testutil::Key(path)]; - [self.persistence commitGroup:group]; -} - @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm b/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm index 59def0f..ae50e3e 100644 --- a/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm +++ b/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm @@ -46,14 +46,14 @@ NS_ASSUME_NONNULL_BEGIN _remoteDocumentCache = [_db remoteDocumentCache]; // Add a couple initial items to the cache. - FSTWriteGroup *group = [_db startGroupWithAction:@"Add initial docs."]; - _kInitialADoc = FSTTestDoc("coll/a", 42, @{@"test" : @"data"}, NO); - [_remoteDocumentCache addEntry:_kInitialADoc]; + _db.run("Test setup", [&]() { + _kInitialADoc = FSTTestDoc("coll/a", 42, @{@"test" : @"data"}, NO); + [_remoteDocumentCache addEntry:_kInitialADoc]; - _kInitialBDoc = - [FSTDeletedDocument documentWithKey:FSTTestDocKey(@"coll/b") version:FSTTestVersion(314)]; - [_remoteDocumentCache addEntry:_kInitialBDoc]; - [_db commitGroup:group]; + _kInitialBDoc = + [FSTDeletedDocument documentWithKey:FSTTestDocKey(@"coll/b") version:FSTTestVersion(314)]; + [_remoteDocumentCache addEntry:_kInitialBDoc]; + }); _remoteDocumentBuffer = [FSTRemoteDocumentChangeBuffer changeBufferWithCache:_remoteDocumentCache]; @@ -68,10 +68,10 @@ NS_ASSUME_NONNULL_BEGIN } - (void)testReadUnchangedEntry { - FSTWriteGroup *group = [_db startGroupWithAction:@"ReadUnchangedEntry"]; - XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")], - _kInitialADoc); - [_db commitGroup:group]; + _db.run("testReadUnchangedEntry", [&]() { + XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")], + _kInitialADoc); + }); } - (void)testAddEntryAndReadItBack { @@ -80,38 +80,37 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")], newADoc); // B should still be unchanged. - FSTWriteGroup *group = [_db startGroupWithAction:@"AddEntryAndReadItBack"]; - XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/b")], - _kInitialBDoc); - [_db commitGroup:group]; + _db.run("testAddEntryAndReadItBack", [&]() { + XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/b")], + _kInitialBDoc); + }); } - (void)testApplyChanges { FSTMaybeDocument *newADoc = FSTTestDoc("coll/a", 43, @{@"new" : @"data"}, NO); [_remoteDocumentBuffer addEntry:newADoc]; - FSTWriteGroup *group = [_db startGroupWithAction:@"Apply Changes"]; - XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")], newADoc); + _db.run("testApplyChanges setup", [&]() { + XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")], newADoc); - // Reading directly against the cache should still yield the old result. - XCTAssertEqualObjects([_remoteDocumentCache entryForKey:FSTTestDocKey(@"coll/a")], _kInitialADoc); - [_db commitGroup:group]; + // Reading directly against the cache should still yield the old result. + XCTAssertEqualObjects([_remoteDocumentCache entryForKey:FSTTestDocKey(@"coll/a")], + _kInitialADoc); + }); - group = [_db startGroupWithAction:@"Apply changes"]; - [_remoteDocumentBuffer applyToWriteGroup:group]; + _db.run("testApplyChanges", [&]() { + [_remoteDocumentBuffer apply]; - // Reading against the cache should now yield the new result. - XCTAssertEqualObjects([_remoteDocumentCache entryForKey:FSTTestDocKey(@"coll/a")], newADoc); - [_db commitGroup:group]; + // Reading against the cache should now yield the new result. + XCTAssertEqualObjects([_remoteDocumentCache entryForKey:FSTTestDocKey(@"coll/a")], newADoc); + }); } - (void)testMethodsThrowAfterApply { - FSTWriteGroup *group = [_db startGroupWithAction:@"Apply changes"]; - [_remoteDocumentBuffer applyToWriteGroup:group]; - [_db commitGroup:group]; + _db.run("testMethodsThrowAfterApply", [&]() { [_remoteDocumentBuffer apply]; }); XCTAssertThrows([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")]); XCTAssertThrows([_remoteDocumentBuffer addEntry:_kInitialADoc]); - XCTAssertThrows([_remoteDocumentBuffer applyToWriteGroup:group]); + XCTAssertThrows([_remoteDocumentBuffer apply]); } @end diff --git a/Firestore/Example/Tests/Local/FSTWriteGroupTests.mm b/Firestore/Example/Tests/Local/FSTWriteGroupTests.mm deleted file mode 100644 index 871bd99..0000000 --- a/Firestore/Example/Tests/Local/FSTWriteGroupTests.mm +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "Firestore/Source/Local/FSTWriteGroup.h" - -#import -#include - -#import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" -#import "Firestore/Source/Local/FSTLevelDB.h" -#import "Firestore/Source/Local/FSTLevelDBKey.h" - -#import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" - -using leveldb::ReadOptions; -using leveldb::Status; - -NS_ASSUME_NONNULL_BEGIN - -@interface FSTWriteGroupTests : XCTestCase -@end - -@implementation FSTWriteGroupTests { - FSTLevelDB *_db; -} - -- (void)setUp { - [super setUp]; - - _db = [FSTPersistenceTestHelpers levelDBPersistence]; -} - -- (void)tearDown { - _db = nil; - - [super tearDown]; -} - -- (void)testCommit { - std::string key = [FSTLevelDBMutationKey keyWithUserID:"user1" batchID:42]; - FSTPBWriteBatch *message = [FSTPBWriteBatch message]; - message.batchId = 42; - - // This is a test that shows that committing an empty group does not fail. There are no side - // effects to verify though. - FSTWriteGroup *group = [_db startGroupWithAction:@"Empty commit"]; - XCTAssertNoThrow([_db commitGroup:group]); - - group = [_db startGroupWithAction:@"Put"]; - [group setMessage:message forKey:key]; - - std::string value; - Status status = _db.ptr->Get(ReadOptions(), key, &value); - XCTAssertTrue(status.IsNotFound()); - - [_db commitGroup:group]; - status = _db.ptr->Get(ReadOptions(), key, &value); - XCTAssertTrue(status.ok()); - - group = [_db startGroupWithAction:@"Delete"]; - [group removeMessageForKey:key]; - status = _db.ptr->Get(ReadOptions(), key, &value); - XCTAssertTrue(status.ok()); - - [_db commitGroup:group]; - status = _db.ptr->Get(ReadOptions(), key, &value); - XCTAssertTrue(status.IsNotFound()); -} - -- (void)testCommittingWrongGroupThrows { - // If you don't create the group through persistence, it should throw. - FSTWriteGroup *group = [FSTWriteGroup groupWithAction:@"group"]; - XCTAssertThrows([_db commitGroup:group]); -} - -- (void)testCommittingTwiceThrows { - FSTWriteGroup *group = [_db startGroupWithAction:@"group"]; - [_db commitGroup:group]; - XCTAssertThrows([_db commitGroup:group]); -} - -- (void)testNestingGroupsThrows { - [_db startGroupWithAction:@"group1"]; - XCTAssertThrows([_db startGroupWithAction:@"group2"]); -} -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTSyncEngine.mm b/Firestore/Source/Core/FSTSyncEngine.mm index d834cc2..673991c 100644 --- a/Firestore/Source/Core/FSTSyncEngine.mm +++ b/Firestore/Source/Core/FSTSyncEngine.mm @@ -295,8 +295,8 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1; [remoteEvent.targetChanges enumerateKeysAndObjectsUsingBlock:^( FSTBoxedTargetID *_Nonnull targetID, FSTTargetChange *_Nonnull targetChange, BOOL *_Nonnull stop) { - const auto iter = _limboKeysByTarget.find([targetID intValue]); - if (iter == _limboKeysByTarget.end()) { + const auto iter = self->_limboKeysByTarget.find([targetID intValue]); + if (iter == self->_limboKeysByTarget.end()) { return; } const DocumentKey &limboKey = iter->second; diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm index 922c5b4..4812228 100644 --- a/Firestore/Source/Local/FSTLevelDB.mm +++ b/Firestore/Source/Local/FSTLevelDB.mm @@ -23,8 +23,6 @@ #import "Firestore/Source/Local/FSTLevelDBMutationQueue.h" #import "Firestore/Source/Local/FSTLevelDBQueryCache.h" #import "Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h" -#import "Firestore/Source/Local/FSTWriteGroup.h" -#import "Firestore/Source/Local/FSTWriteGroupTracker.h" #import "Firestore/Source/Remote/FSTSerializerBeta.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTLogger.h" @@ -55,7 +53,6 @@ using leveldb::WriteOptions; @interface FSTLevelDB () @property(nonatomic, copy) NSString *directory; -@property(nonatomic, strong) FSTWriteGroupTracker *writeGroupTracker; @property(nonatomic, assign, getter=isStarted) BOOL started; @property(nonatomic, strong, readonly) FSTLocalSerializer *serializer; @@ -79,7 +76,6 @@ using leveldb::WriteOptions; serializer:(FSTLocalSerializer *)serializer { if (self = [super init]) { _directory = [directory copy]; - _writeGroupTracker = [FSTWriteGroupTracker tracker]; _serializer = serializer; _transactionRunner.SetBackingPersistence(self); } @@ -148,7 +144,7 @@ using leveldb::WriteOptions; return NO; } _ptr.reset(database); - LevelDbTransaction transaction(_ptr.get()); + LevelDbTransaction transaction(_ptr.get(), "Start LevelDB"); [FSTLevelDBMigrations runMigrationsWithTransaction:&transaction]; transaction.Commit(); return YES; @@ -229,9 +225,9 @@ using leveldb::WriteOptions; return [[FSTLevelDBRemoteDocumentCache alloc] initWithDB:self serializer:self.serializer]; } -- (void)startTransaction { +- (void)startTransaction:(absl::string_view)label { FSTAssert(_transaction == nullptr, @"Starting a transaction while one is already outstanding"); - _transaction = absl::make_unique(_ptr.get()); + _transaction = absl::make_unique(_ptr.get(), label); } - (void)commitTransaction { @@ -240,19 +236,6 @@ using leveldb::WriteOptions; _transaction.reset(); } -- (FSTWriteGroup *)startGroupWithAction:(NSString *)action { - FSTAssert(_transaction == nullptr, @"Starting a transaction while one is already outstanding"); - _transaction = absl::make_unique(_ptr.get()); - return [self.writeGroupTracker startGroupWithAction:action transaction:_transaction.get()]; -} - -- (void)commitGroup:(FSTWriteGroup *)group { - FSTAssert(_transaction != nullptr, @"Committing a transaction before one is started"); - [self.writeGroupTracker endGroup:group]; - _transaction->Commit(); - _transaction.reset(); -} - - (void)shutdown { FSTAssert(self.isStarted, @"FSTLevelDB shutdown without start!"); self.started = NO; diff --git a/Firestore/Source/Local/FSTLocalStore.mm b/Firestore/Source/Local/FSTLocalStore.mm index 74eab48..412ade2 100644 --- a/Firestore/Source/Local/FSTLocalStore.mm +++ b/Firestore/Source/Local/FSTLocalStore.mm @@ -31,7 +31,6 @@ #import "Firestore/Source/Local/FSTReferenceSet.h" #import "Firestore/Source/Local/FSTRemoteDocumentCache.h" #import "Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.h" -#import "Firestore/Source/Local/FSTWriteGroup.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentDictionary.h" #import "Firestore/Source/Model/FSTMutation.h" @@ -132,27 +131,27 @@ NS_ASSUME_NONNULL_BEGIN } - (void)startMutationQueue { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Start MutationQueue"]; - [self.mutationQueue start]; - - // If we have any leftover mutation batch results from a prior run, just drop them. - // TODO(http://b/33446471): We probably need to repopulate heldBatchResults or similar instead, - // but that is not straightforward since we're not persisting the write ack versions. - [self.heldBatchResults removeAllObjects]; - - // TODO(mikelehen): This is the only usage of getAllMutationBatchesThroughBatchId:. Consider - // removing it in favor of a getAcknowledgedBatches method. - FSTBatchID highestAck = [self.mutationQueue highestAcknowledgedBatchID]; - if (highestAck != kFSTBatchIDUnknown) { - NSArray *batches = - [self.mutationQueue allMutationBatchesThroughBatchID:highestAck]; - if (batches.count > 0) { - // NOTE: This could be more efficient if we had a removeBatchesThroughBatchID, but this set - // should be very small and this code should go away eventually. - [self.mutationQueue removeMutationBatches:batches]; + self.persistence.run("Start MutationQueue", [&]() { + [self.mutationQueue start]; + + // If we have any leftover mutation batch results from a prior run, just drop them. + // TODO(http://b/33446471): We probably need to repopulate heldBatchResults or similar instead, + // but that is not straightforward since we're not persisting the write ack versions. + [self.heldBatchResults removeAllObjects]; + + // TODO(mikelehen): This is the only usage of getAllMutationBatchesThroughBatchId:. Consider + // removing it in favor of a getAcknowledgedBatches method. + FSTBatchID highestAck = [self.mutationQueue highestAcknowledgedBatchID]; + if (highestAck != kFSTBatchIDUnknown) { + NSArray *batches = + [self.mutationQueue allMutationBatchesThroughBatchID:highestAck]; + if (batches.count > 0) { + // NOTE: This could be more efficient if we had a removeBatchesThroughBatchID, but this set + // should be very small and this code should go away eventually. + [self.mutationQueue removeMutationBatches:batches]; + } } - } - [self.persistence commitGroup:group]; + }); } - (void)startQueryCache { @@ -166,9 +165,9 @@ NS_ASSUME_NONNULL_BEGIN - (FSTMaybeDocumentDictionary *)userDidChange:(const User &)user { // Swap out the mutation queue, grabbing the pending mutation batches before and after. - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"OldBatches"]; - NSArray *oldBatches = [self.mutationQueue allMutationBatches]; - [self.persistence commitGroup:group]; + NSArray *oldBatches = self.persistence.run( + "OldBatches", + [&]() -> NSArray * { return [self.mutationQueue allMutationBatches]; }); [self.garbageCollector removeGarbageSource:self.mutationQueue]; @@ -177,83 +176,79 @@ NS_ASSUME_NONNULL_BEGIN [self startMutationQueue]; - group = [self.persistence startGroupWithAction:@"NewBatches"]; - NSArray *newBatches = [self.mutationQueue allMutationBatches]; - - // Recreate our LocalDocumentsView using the new MutationQueue. - self.localDocuments = [FSTLocalDocumentsView viewWithRemoteDocumentCache:self.remoteDocumentCache - mutationQueue:self.mutationQueue]; - - // Union the old/new changed keys. - FSTDocumentKeySet *changedKeys = [FSTDocumentKeySet keySet]; - for (NSArray *batches in @[ oldBatches, newBatches ]) { - for (FSTMutationBatch *batch in batches) { - for (FSTMutation *mutation in batch.mutations) { - changedKeys = [changedKeys setByAddingObject:mutation.key]; + return self.persistence.run("NewBatches", [&]() -> FSTMaybeDocumentDictionary * { + NSArray *newBatches = [self.mutationQueue allMutationBatches]; + + // Recreate our LocalDocumentsView using the new MutationQueue. + self.localDocuments = + [FSTLocalDocumentsView viewWithRemoteDocumentCache:self.remoteDocumentCache + mutationQueue:self.mutationQueue]; + + // Union the old/new changed keys. + FSTDocumentKeySet *changedKeys = [FSTDocumentKeySet keySet]; + for (NSArray *batches in @[ oldBatches, newBatches ]) { + for (FSTMutationBatch *batch in batches) { + for (FSTMutation *mutation in batch.mutations) { + changedKeys = [changedKeys setByAddingObject:mutation.key]; + } } } - } - // Return the set of all (potentially) changed documents as the result of the user change. - FSTMaybeDocumentDictionary *result = [self.localDocuments documentsForKeys:changedKeys]; - [self.persistence commitGroup:group]; - return result; + // Return the set of all (potentially) changed documents as the result of the user change. + return [self.localDocuments documentsForKeys:changedKeys]; + }); } - (FSTLocalWriteResult *)locallyWriteMutations:(NSArray *)mutations { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Locally write mutations"]; - FIRTimestamp *localWriteTime = [FIRTimestamp timestamp]; - FSTMutationBatch *batch = - [self.mutationQueue addMutationBatchWithWriteTime:localWriteTime mutations:mutations]; - FSTDocumentKeySet *keys = [batch keys]; - FSTMaybeDocumentDictionary *changedDocuments = [self.localDocuments documentsForKeys:keys]; - [self.persistence commitGroup:group]; - return [FSTLocalWriteResult resultForBatchID:batch.batchID changes:changedDocuments]; + return self.persistence.run("Locally write mutations", [&]() -> FSTLocalWriteResult * { + FIRTimestamp *localWriteTime = [FIRTimestamp timestamp]; + FSTMutationBatch *batch = + [self.mutationQueue addMutationBatchWithWriteTime:localWriteTime mutations:mutations]; + FSTDocumentKeySet *keys = [batch keys]; + FSTMaybeDocumentDictionary *changedDocuments = [self.localDocuments documentsForKeys:keys]; + return [FSTLocalWriteResult resultForBatchID:batch.batchID changes:changedDocuments]; + }); } - (FSTMaybeDocumentDictionary *)acknowledgeBatchWithResult:(FSTMutationBatchResult *)batchResult { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Acknowledge batch"]; - id mutationQueue = self.mutationQueue; + return self.persistence.run("Acknowledge batch", [&]() -> FSTMaybeDocumentDictionary * { + id mutationQueue = self.mutationQueue; - [mutationQueue acknowledgeBatch:batchResult.batch streamToken:batchResult.streamToken]; + [mutationQueue acknowledgeBatch:batchResult.batch streamToken:batchResult.streamToken]; - FSTDocumentKeySet *affected; - if ([self shouldHoldBatchResultWithVersion:batchResult.commitVersion]) { - [self.heldBatchResults addObject:batchResult]; - affected = [FSTDocumentKeySet keySet]; - } else { - FSTRemoteDocumentChangeBuffer *remoteDocuments = - [FSTRemoteDocumentChangeBuffer changeBufferWithCache:self.remoteDocumentCache]; + FSTDocumentKeySet *affected; + if ([self shouldHoldBatchResultWithVersion:batchResult.commitVersion]) { + [self.heldBatchResults addObject:batchResult]; + affected = [FSTDocumentKeySet keySet]; + } else { + FSTRemoteDocumentChangeBuffer *remoteDocuments = + [FSTRemoteDocumentChangeBuffer changeBufferWithCache:self.remoteDocumentCache]; - affected = - [self releaseBatchResults:@[ batchResult ] group:group remoteDocuments:remoteDocuments]; + affected = [self releaseBatchResults:@[ batchResult ] remoteDocuments:remoteDocuments]; - [remoteDocuments applyToWriteGroup:group]; - } + [remoteDocuments apply]; + } - [self.mutationQueue performConsistencyCheck]; + [self.mutationQueue performConsistencyCheck]; - FSTMaybeDocumentDictionary *result = [self.localDocuments documentsForKeys:affected]; - [self.persistence commitGroup:group]; - return result; + return [self.localDocuments documentsForKeys:affected]; + }); } - (FSTMaybeDocumentDictionary *)rejectBatchID:(FSTBatchID)batchID { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Reject batch"]; - - FSTMutationBatch *toReject = [self.mutationQueue lookupMutationBatch:batchID]; - FSTAssert(toReject, @"Attempt to reject nonexistent batch!"); + return self.persistence.run("Reject batch", [&]() -> FSTMaybeDocumentDictionary * { + FSTMutationBatch *toReject = [self.mutationQueue lookupMutationBatch:batchID]; + FSTAssert(toReject, @"Attempt to reject nonexistent batch!"); - FSTBatchID lastAcked = [self.mutationQueue highestAcknowledgedBatchID]; - FSTAssert(batchID > lastAcked, @"Acknowledged batches can't be rejected."); + FSTBatchID lastAcked = [self.mutationQueue highestAcknowledgedBatchID]; + FSTAssert(batchID > lastAcked, @"Acknowledged batches can't be rejected."); - FSTDocumentKeySet *affected = [self removeMutationBatch:toReject group:group]; + FSTDocumentKeySet *affected = [self removeMutationBatch:toReject]; - [self.mutationQueue performConsistencyCheck]; + [self.mutationQueue performConsistencyCheck]; - FSTMaybeDocumentDictionary *result = [self.localDocuments documentsForKeys:affected]; - [self.persistence commitGroup:group]; - return result; + return [self.localDocuments documentsForKeys:affected]; + }); } - (nullable NSData *)lastStreamToken { @@ -261,10 +256,8 @@ NS_ASSUME_NONNULL_BEGIN } - (void)setLastStreamToken:(nullable NSData *)streamToken { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Set stream token"]; - - [self.mutationQueue setLastStreamToken:streamToken]; - [self.persistence commitGroup:group]; + self.persistence.run("Set stream token", + [&]() { [self.mutationQueue setLastStreamToken:streamToken]; }); } - (FSTSnapshotVersion *)lastRemoteSnapshotVersion { @@ -272,207 +265,197 @@ NS_ASSUME_NONNULL_BEGIN } - (FSTMaybeDocumentDictionary *)applyRemoteEvent:(FSTRemoteEvent *)remoteEvent { - id queryCache = self.queryCache; - - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Apply remote event"]; - FSTRemoteDocumentChangeBuffer *remoteDocuments = - [FSTRemoteDocumentChangeBuffer changeBufferWithCache:self.remoteDocumentCache]; + return self.persistence.run("Apply remote event", [&]() -> FSTMaybeDocumentDictionary * { + id queryCache = self.queryCache; - [remoteEvent.targetChanges enumerateKeysAndObjectsUsingBlock:^( - NSNumber *targetIDNumber, FSTTargetChange *change, BOOL *stop) { - FSTTargetID targetID = targetIDNumber.intValue; + FSTRemoteDocumentChangeBuffer *remoteDocuments = + [FSTRemoteDocumentChangeBuffer changeBufferWithCache:self.remoteDocumentCache]; - // Do not ref/unref unassigned targetIDs - it may lead to leaks. - FSTQueryData *queryData = self.targetIDs[targetIDNumber]; - if (!queryData) { - return; - } + [remoteEvent.targetChanges enumerateKeysAndObjectsUsingBlock:^( + NSNumber *targetIDNumber, FSTTargetChange *change, BOOL *stop) { + FSTTargetID targetID = targetIDNumber.intValue; - FSTTargetMapping *mapping = change.mapping; - if (mapping) { - // First make sure that all references are deleted. - if ([mapping isKindOfClass:[FSTResetMapping class]]) { - FSTResetMapping *reset = (FSTResetMapping *)mapping; - [queryCache removeMatchingKeysForTargetID:targetID]; - [queryCache addMatchingKeys:reset.documents forTargetID:targetID]; + // Do not ref/unref unassigned targetIDs - it may lead to leaks. + FSTQueryData *queryData = self.targetIDs[targetIDNumber]; + if (!queryData) { + return; + } - } else if ([mapping isKindOfClass:[FSTUpdateMapping class]]) { - FSTUpdateMapping *update = (FSTUpdateMapping *)mapping; - [queryCache removeMatchingKeys:update.removedDocuments forTargetID:targetID]; - [queryCache addMatchingKeys:update.addedDocuments forTargetID:targetID]; + FSTTargetMapping *mapping = change.mapping; + if (mapping) { + // First make sure that all references are deleted. + if ([mapping isKindOfClass:[FSTResetMapping class]]) { + FSTResetMapping *reset = (FSTResetMapping *)mapping; + [queryCache removeMatchingKeysForTargetID:targetID]; + [queryCache addMatchingKeys:reset.documents forTargetID:targetID]; + + } else if ([mapping isKindOfClass:[FSTUpdateMapping class]]) { + FSTUpdateMapping *update = (FSTUpdateMapping *)mapping; + [queryCache removeMatchingKeys:update.removedDocuments forTargetID:targetID]; + [queryCache addMatchingKeys:update.addedDocuments forTargetID:targetID]; + + } else { + FSTFail(@"Unknown mapping type: %@", mapping); + } + } + // Update the resume token if the change includes one. Don't clear any preexisting value. + NSData *resumeToken = change.resumeToken; + if (resumeToken.length > 0) { + queryData = [queryData queryDataByReplacingSnapshotVersion:change.snapshotVersion + resumeToken:resumeToken]; + self.targetIDs[targetIDNumber] = queryData; + [self.queryCache updateQueryData:queryData]; + } + }]; + + // TODO(klimt): This could probably be an NSMutableDictionary. + FSTDocumentKeySet *changedDocKeys = [FSTDocumentKeySet keySet]; + for (const auto &kv : remoteEvent.documentUpdates) { + const DocumentKey &key = kv.first; + FSTMaybeDocument *doc = kv.second; + changedDocKeys = [changedDocKeys setByAddingObject:key]; + FSTMaybeDocument *existingDoc = [remoteDocuments entryForKey:key]; + // Make sure we don't apply an old document version to the remote cache, though we + // make an exception for [SnapshotVersion noVersion] which can happen for manufactured + // events (e.g. in the case of a limbo document resolution failing). + if (!existingDoc || [doc.version isEqual:[FSTSnapshotVersion noVersion]] || + [doc.version compare:existingDoc.version] != NSOrderedAscending) { + [remoteDocuments addEntry:doc]; } else { - FSTFail(@"Unknown mapping type: %@", mapping); + FSTLog( + @"FSTLocalStore Ignoring outdated watch update for %s. " + "Current version: %@ Watch version: %@", + key.ToString().c_str(), existingDoc.version, doc.version); } - } - // Update the resume token if the change includes one. Don't clear any preexisting value. - NSData *resumeToken = change.resumeToken; - if (resumeToken.length > 0) { - queryData = [queryData queryDataByReplacingSnapshotVersion:change.snapshotVersion - resumeToken:resumeToken]; - self.targetIDs[targetIDNumber] = queryData; - [self.queryCache updateQueryData:queryData]; + // The document might be garbage because it was unreferenced by everything. + // Make sure to mark it as garbage if it is... + [self.garbageCollector addPotentialGarbageKey:key]; } - }]; - // TODO(klimt): This could probably be an NSMutableDictionary. - FSTDocumentKeySet *changedDocKeys = [FSTDocumentKeySet keySet]; - for (const auto &kv : remoteEvent.documentUpdates) { - const DocumentKey &key = kv.first; - FSTMaybeDocument *doc = kv.second; - changedDocKeys = [changedDocKeys setByAddingObject:key]; - FSTMaybeDocument *existingDoc = [remoteDocuments entryForKey:key]; - // Make sure we don't apply an old document version to the remote cache, though we - // make an exception for [SnapshotVersion noVersion] which can happen for manufactured - // events (e.g. in the case of a limbo document resolution failing). - if (!existingDoc || [doc.version isEqual:[FSTSnapshotVersion noVersion]] || - [doc.version compare:existingDoc.version] != NSOrderedAscending) { - [remoteDocuments addEntry:doc]; - } else { - FSTLog( - @"FSTLocalStore Ignoring outdated watch update for %s. " - "Current version: %@ Watch version: %@", - key.ToString().c_str(), existingDoc.version, doc.version); + // HACK: The only reason we allow omitting snapshot version is so we can synthesize remote + // events when we get permission denied errors while trying to resolve the state of a locally + // cached document that is in limbo. + FSTSnapshotVersion *lastRemoteVersion = [self.queryCache lastRemoteSnapshotVersion]; + FSTSnapshotVersion *remoteVersion = remoteEvent.snapshotVersion; + if (![remoteVersion isEqual:[FSTSnapshotVersion noVersion]]) { + FSTAssert([remoteVersion compare:lastRemoteVersion] != NSOrderedAscending, + @"Watch stream reverted to previous snapshot?? (%@ < %@)", remoteVersion, + lastRemoteVersion); + [self.queryCache setLastRemoteSnapshotVersion:remoteVersion]; } - // The document might be garbage because it was unreferenced by everything. - // Make sure to mark it as garbage if it is... - [self.garbageCollector addPotentialGarbageKey:key]; - } - - // HACK: The only reason we allow omitting snapshot version is so we can synthesize remote events - // when we get permission denied errors while trying to resolve the state of a locally cached - // document that is in limbo. - FSTSnapshotVersion *lastRemoteVersion = [self.queryCache lastRemoteSnapshotVersion]; - FSTSnapshotVersion *remoteVersion = remoteEvent.snapshotVersion; - if (![remoteVersion isEqual:[FSTSnapshotVersion noVersion]]) { - FSTAssert([remoteVersion compare:lastRemoteVersion] != NSOrderedAscending, - @"Watch stream reverted to previous snapshot?? (%@ < %@)", remoteVersion, - lastRemoteVersion); - [self.queryCache setLastRemoteSnapshotVersion:remoteVersion]; - } - - FSTDocumentKeySet *releasedWriteKeys = - [self releaseHeldBatchResultsWithGroup:group remoteDocuments:remoteDocuments]; + FSTDocumentKeySet *releasedWriteKeys = + [self releaseHeldBatchResultsWithRemoteDocuments:remoteDocuments]; - [remoteDocuments applyToWriteGroup:group]; + [remoteDocuments apply]; - // Union the two key sets. - __block FSTDocumentKeySet *keysToRecalc = changedDocKeys; - [releasedWriteKeys enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) { - keysToRecalc = [keysToRecalc setByAddingObject:key]; - }]; + // Union the two key sets. + __block FSTDocumentKeySet *keysToRecalc = changedDocKeys; + [releasedWriteKeys enumerateObjectsUsingBlock:^(FSTDocumentKey *key, BOOL *stop) { + keysToRecalc = [keysToRecalc setByAddingObject:key]; + }]; - FSTMaybeDocumentDictionary *result = [self.localDocuments documentsForKeys:keysToRecalc]; - [self.persistence commitGroup:group]; - return result; + return [self.localDocuments documentsForKeys:keysToRecalc]; + }); } - (void)notifyLocalViewChanges:(NSArray *)viewChanges { - FSTReferenceSet *localViewReferences = self.localViewReferences; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"NotifyLocalViewChanges"]; - for (FSTLocalViewChanges *view in viewChanges) { - FSTQueryData *queryData = [self.queryCache queryDataForQuery:view.query]; - FSTAssert(queryData, @"Local view changes contain unallocated query."); - FSTTargetID targetID = queryData.targetID; - [localViewReferences addReferencesToKeys:view.addedKeys forID:targetID]; - [localViewReferences removeReferencesToKeys:view.removedKeys forID:targetID]; - } - [self.persistence commitGroup:group]; + self.persistence.run("NotifyLocalViewChanges", [&]() { + FSTReferenceSet *localViewReferences = self.localViewReferences; + for (FSTLocalViewChanges *view in viewChanges) { + FSTQueryData *queryData = [self.queryCache queryDataForQuery:view.query]; + FSTAssert(queryData, @"Local view changes contain unallocated query."); + FSTTargetID targetID = queryData.targetID; + [localViewReferences addReferencesToKeys:view.addedKeys forID:targetID]; + [localViewReferences removeReferencesToKeys:view.removedKeys forID:targetID]; + } + }); } - (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(FSTBatchID)batchID { - FSTMutationBatch *result = self.persistence.run([&]() -> FSTMutationBatch * { - return [self.mutationQueue nextMutationBatchAfterBatchID:batchID]; - }); + FSTMutationBatch *result = + self.persistence.run("NextMutationBatchAfterBatchID", [&]() -> FSTMutationBatch * { + return [self.mutationQueue nextMutationBatchAfterBatchID:batchID]; + }); return result; } - (nullable FSTMaybeDocument *)readDocument:(const DocumentKey &)key { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"ReadDocument"]; - FSTMaybeDocument *result = [self.localDocuments documentForKey:key]; - [self.persistence commitGroup:group]; - return result; + return self.persistence.run("ReadDocument", [&]() -> FSTMaybeDocument *_Nullable { + return [self.localDocuments documentForKey:key]; + }); } - (FSTQueryData *)allocateQuery:(FSTQuery *)query { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Allocate query"]; - FSTQueryData *cached = [self.queryCache queryDataForQuery:query]; - FSTTargetID targetID; - FSTListenSequenceNumber sequenceNumber = [self.listenSequence next]; - if (cached) { - // This query has been listened to previously, so reuse the previous targetID. - // TODO(mcg): freshen last accessed date? - targetID = cached.targetID; - } else { - targetID = _targetIDGenerator.NextId(); - cached = [[FSTQueryData alloc] initWithQuery:query - targetID:targetID - listenSequenceNumber:sequenceNumber - purpose:FSTQueryPurposeListen]; - [self.queryCache addQueryData:cached]; - } - [self.persistence commitGroup:group]; + FSTQueryData *queryData = self.persistence.run("Allocate query", [&]() -> FSTQueryData * { + FSTQueryData *cached = [self.queryCache queryDataForQuery:query]; + // TODO(mcg): freshen last accessed date if cached exists? + if (!cached) { + cached = [[FSTQueryData alloc] initWithQuery:query + targetID:_targetIDGenerator.NextId() + listenSequenceNumber:[self.listenSequence next] + purpose:FSTQueryPurposeListen]; + [self.queryCache addQueryData:cached]; + } + return cached; + }); // Sanity check to ensure that even when resuming a query it's not currently active. - FSTBoxedTargetID *boxedTargetID = @(targetID); + FSTBoxedTargetID *boxedTargetID = @(queryData.targetID); FSTAssert(!self.targetIDs[boxedTargetID], @"Tried to allocate an already allocated query: %@", query); - self.targetIDs[boxedTargetID] = cached; - return cached; + self.targetIDs[boxedTargetID] = queryData; + return queryData; } - (void)releaseQuery:(FSTQuery *)query { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Release query"]; - - FSTQueryData *queryData = [self.queryCache queryDataForQuery:query]; - FSTAssert(queryData, @"Tried to release nonexistent query: %@", query); - - [self.localViewReferences removeReferencesForID:queryData.targetID]; - if (self.garbageCollector.isEager) { - [self.queryCache removeQueryData:queryData]; - } - [self.targetIDs removeObjectForKey:@(queryData.targetID)]; + self.persistence.run("Release query", [&]() { + FSTQueryData *queryData = [self.queryCache queryDataForQuery:query]; + FSTAssert(queryData, @"Tried to release nonexistent query: %@", query); - // If this was the last watch target, then we won't get any more watch snapshots, so we should - // release any held batch results. - if ([self.targetIDs count] == 0) { - FSTRemoteDocumentChangeBuffer *remoteDocuments = - [FSTRemoteDocumentChangeBuffer changeBufferWithCache:self.remoteDocumentCache]; + [self.localViewReferences removeReferencesForID:queryData.targetID]; + if (self.garbageCollector.isEager) { + [self.queryCache removeQueryData:queryData]; + } + [self.targetIDs removeObjectForKey:@(queryData.targetID)]; - [self releaseHeldBatchResultsWithGroup:group remoteDocuments:remoteDocuments]; + // If this was the last watch target, then we won't get any more watch snapshots, so we should + // release any held batch results. + if ([self.targetIDs count] == 0) { + FSTRemoteDocumentChangeBuffer *remoteDocuments = + [FSTRemoteDocumentChangeBuffer changeBufferWithCache:self.remoteDocumentCache]; - [remoteDocuments applyToWriteGroup:group]; - } + [self releaseHeldBatchResultsWithRemoteDocuments:remoteDocuments]; - [self.persistence commitGroup:group]; + [remoteDocuments apply]; + } + }); } - (FSTDocumentDictionary *)executeQuery:(FSTQuery *)query { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"ExecuteQuery"]; - FSTDocumentDictionary *result = [self.localDocuments documentsMatchingQuery:query]; - [self.persistence commitGroup:group]; - return result; + return self.persistence.run("ExecuteQuery", [&]() -> FSTDocumentDictionary * { + return [self.localDocuments documentsMatchingQuery:query]; + }); } - (FSTDocumentKeySet *)remoteDocumentKeysForTarget:(FSTTargetID)targetID { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoteDocumentKeysForTarget"]; - FSTDocumentKeySet *keySet = [self.queryCache matchingKeysForTargetID:targetID]; - [self.persistence commitGroup:group]; - return keySet; + return self.persistence.run("RemoteDocumentKeysForTarget", [&]() -> FSTDocumentKeySet * { + return [self.queryCache matchingKeysForTargetID:targetID]; + }); } - (void)collectGarbage { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Garbage Collection"]; - // Call collectGarbage regardless of whether isGCEnabled so the referenceSet doesn't continue to - // accumulate the garbage keys. - std::set garbage = [self.garbageCollector collectGarbage]; - if (garbage.size() > 0) { - for (const DocumentKey &key : garbage) { - [self.remoteDocumentCache removeEntryForKey:key]; + self.persistence.run("Garbage Collection", [&]() { + // Call collectGarbage regardless of whether isGCEnabled so the referenceSet doesn't continue to + // accumulate the garbage keys. + std::set garbage = [self.garbageCollector collectGarbage]; + if (garbage.size() > 0) { + for (const DocumentKey &key : garbage) { + [self.remoteDocumentCache removeEntryForKey:key]; + } } - } - [self.persistence commitGroup:group]; + }); } /** @@ -481,9 +464,8 @@ NS_ASSUME_NONNULL_BEGIN * * @return the set of keys of docs that were modified by those writes. */ -- (FSTDocumentKeySet *)releaseHeldBatchResultsWithGroup:(FSTWriteGroup *)group - remoteDocuments: - (FSTRemoteDocumentChangeBuffer *)remoteDocuments { +- (FSTDocumentKeySet *)releaseHeldBatchResultsWithRemoteDocuments: + (FSTRemoteDocumentChangeBuffer *)remoteDocuments { NSMutableArray *toRelease = [NSMutableArray array]; for (FSTMutationBatchResult *batchResult in self.heldBatchResults) { if (![self isRemoteUpToVersion:batchResult.commitVersion]) { @@ -496,7 +478,7 @@ NS_ASSUME_NONNULL_BEGIN return [FSTDocumentKeySet keySet]; } else { [self.heldBatchResults removeObjectsInRange:NSMakeRange(0, toRelease.count)]; - return [self releaseBatchResults:toRelease group:group remoteDocuments:remoteDocuments]; + return [self releaseBatchResults:toRelease remoteDocuments:remoteDocuments]; } } @@ -512,7 +494,6 @@ NS_ASSUME_NONNULL_BEGIN } - (FSTDocumentKeySet *)releaseBatchResults:(NSArray *)batchResults - group:(FSTWriteGroup *)group remoteDocuments:(FSTRemoteDocumentChangeBuffer *)remoteDocuments { NSMutableArray *batches = [NSMutableArray array]; for (FSTMutationBatchResult *batchResult in batchResults) { @@ -520,16 +501,15 @@ NS_ASSUME_NONNULL_BEGIN [batches addObject:batchResult.batch]; } - return [self removeMutationBatches:batches group:group]; + return [self removeMutationBatches:batches]; } -- (FSTDocumentKeySet *)removeMutationBatch:(FSTMutationBatch *)batch group:(FSTWriteGroup *)group { - return [self removeMutationBatches:@[ batch ] group:group]; +- (FSTDocumentKeySet *)removeMutationBatch:(FSTMutationBatch *)batch { + return [self removeMutationBatches:@[ batch ]]; } /** Removes all the mutation batches named in the given array. */ -- (FSTDocumentKeySet *)removeMutationBatches:(NSArray *)batches - group:(FSTWriteGroup *)group { +- (FSTDocumentKeySet *)removeMutationBatches:(NSArray *)batches { // TODO(klimt): Could this be an NSMutableDictionary? __block FSTDocumentKeySet *affectedDocs = [FSTDocumentKeySet keySet]; diff --git a/Firestore/Source/Local/FSTMemoryPersistence.mm b/Firestore/Source/Local/FSTMemoryPersistence.mm index f1f9885..8d74881 100644 --- a/Firestore/Source/Local/FSTMemoryPersistence.mm +++ b/Firestore/Source/Local/FSTMemoryPersistence.mm @@ -21,8 +21,6 @@ #import "Firestore/Source/Local/FSTMemoryMutationQueue.h" #import "Firestore/Source/Local/FSTMemoryQueryCache.h" #import "Firestore/Source/Local/FSTMemoryRemoteDocumentCache.h" -#import "Firestore/Source/Local/FSTWriteGroup.h" -#import "Firestore/Source/Local/FSTWriteGroupTracker.h" #import "Firestore/Source/Util/FSTAssert.h" #include "Firestore/core/src/firebase/firestore/auth/user.h" @@ -33,7 +31,6 @@ using firebase::firestore::auth::User; NS_ASSUME_NONNULL_BEGIN @interface FSTMemoryPersistence () -@property(nonatomic, strong, nonnull) FSTWriteGroupTracker *writeGroupTracker; @property(nonatomic, assign, getter=isStarted) BOOL started; @end @@ -62,7 +59,6 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init { if (self = [super init]) { - _writeGroupTracker = [FSTWriteGroupTracker tracker]; _queryCache = [[FSTMemoryQueryCache alloc] init]; _remoteDocumentCache = [[FSTMemoryRemoteDocumentCache alloc] init]; } @@ -103,16 +99,6 @@ NS_ASSUME_NONNULL_BEGIN return _remoteDocumentCache; } -- (FSTWriteGroup *)startGroupWithAction:(NSString *)action { - return [self.writeGroupTracker startGroupWithAction:action]; -} - -- (void)commitGroup:(FSTWriteGroup *)group { - [self.writeGroupTracker endGroup:group]; - - FSTAssert(group.isEmpty, @"Memory persistence shouldn't use write groups: %@", group.action); -} - @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Local/FSTPersistence.h b/Firestore/Source/Local/FSTPersistence.h index 1784163..2294ef1 100644 --- a/Firestore/Source/Local/FSTPersistence.h +++ b/Firestore/Source/Local/FSTPersistence.h @@ -19,7 +19,6 @@ #import "Firestore/Source/Util/FSTAssert.h" #include "Firestore/core/src/firebase/firestore/auth/user.h" -@class FSTWriteGroup; @protocol FSTMutationQueue; @protocol FSTQueryCache; @protocol FSTRemoteDocumentCache; @@ -86,28 +85,13 @@ struct FSTTransactionRunner; /** Creates an FSTRemoteDocumentCache representing the persisted cache of remote documents. */ - (id)remoteDocumentCache; -/** - * Creates an FSTWriteGroup with the specified action description. - * - * @param action A description of the action performed by this group, used for logging. - * @return The created group. - */ -- (FSTWriteGroup *)startGroupWithAction:(NSString *)action; - -/** - * Commits all accumulated changes in the given group. If there are no changes this is a no-op. - * - * @param group The group of changes to write as a unit. - */ -- (void)commitGroup:(FSTWriteGroup *)group; - @property(nonatomic, readonly, assign) const FSTTransactionRunner &run; @end @protocol FSTTransactional -- (void)startTransaction; +- (void)startTransaction:(absl::string_view)label; - (void)commitTransaction; @@ -136,14 +120,14 @@ struct FSTTransactionRunner { */ template - auto operator()(F block) const -> + auto operator()(absl::string_view label, F block) const -> typename std::enable_if::value, void>::type { __strong id strongDb = _db; if (!strongDb && _expect_db) { FSTCFail(@"Transaction runner accessed without underlying db when it expected one"); } if (strongDb) { - [strongDb startTransaction]; + [strongDb startTransaction:label]; } block(); if (strongDb) { @@ -152,7 +136,7 @@ struct FSTTransactionRunner { } template - auto operator()(F block) const -> + auto operator()(absl::string_view label, F block) const -> typename std::enable_if::value, decltype(block())>::type { using ReturnT = decltype(block()); __strong id strongDb = _db; @@ -160,7 +144,7 @@ struct FSTTransactionRunner { FSTCFail(@"Transaction runner accessed without underlying db when it expected one"); } if (strongDb) { - [strongDb startTransaction]; + [strongDb startTransaction:label]; } ReturnT result = block(); if (strongDb) { diff --git a/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.h b/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.h index cfdf55b..be29a89 100644 --- a/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.h +++ b/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.h @@ -22,7 +22,6 @@ NS_ASSUME_NONNULL_BEGIN @protocol FSTRemoteDocumentCache; @class FSTMaybeDocument; -@class FSTWriteGroup; /** * An in-memory buffer of entries to be written to an FSTRemoteDocumentCache. It can be used to @@ -58,10 +57,9 @@ NS_ASSUME_NONNULL_BEGIN (const firebase::firestore::model::DocumentKey &)documentKey; /** - * Applies buffered changes to the underlying FSTRemoteDocumentCache, using the provided - * FSTWriteGroup. + * Applies buffered changes to the underlying FSTRemoteDocumentCache */ -- (void)applyToWriteGroup:(FSTWriteGroup *)group; +- (void)apply; @end diff --git a/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.mm b/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.mm index 0557bce..3812501 100644 --- a/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.mm +++ b/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.mm @@ -73,7 +73,7 @@ NS_ASSUME_NONNULL_BEGIN } } -- (void)applyToWriteGroup:(FSTWriteGroup *)group { +- (void)apply { [self assertValid]; for (const auto &kv : *_changes) { diff --git a/Firestore/Source/Local/FSTWriteGroup.h b/Firestore/Source/Local/FSTWriteGroup.h deleted file mode 100644 index a554597..0000000 --- a/Firestore/Source/Local/FSTWriteGroup.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import - -#include - -#include "Firestore/Source/Local/StringView.h" -#include "Firestore/core/src/firebase/firestore/local/leveldb_transaction.h" -#include "leveldb/db.h" - -NS_ASSUME_NONNULL_BEGIN - -@class GPBMessage; - -/** - * A group of writes that will be applied together atomically to persistent storage. - * - * This class is usable by both Objective-C and Objective-C++ clients. Objective-C clients are able - * to create a new group and commit it. Objective-C++ clients can additionally add to the group - * using deleteKey: and putKey:value:. - * - * Note that this is a write "group" even though the underlying LevelDB concept is a write "batch" - * because Firestore already has a concept of mutation batches, which are user-specified groups of - * changes. This means that an FSTWriteGroup may contain the application of multiple user-specified - * mutation batches. - */ -@interface FSTWriteGroup : NSObject - -/** - * Creates a new, empty write group. - * - * @param action A description of the action performed by this group, used for logging. - */ -+ (instancetype)groupWithAction:(NSString *)action; - -+ (instancetype)groupWithAction:(NSString *)action - transaction:(firebase::firestore::local::LevelDbTransaction *)transaction; - -- (instancetype)init __attribute__((unavailable("Use a static constructor instead"))); - -/** The action description assigned to this write group. */ -@property(nonatomic, copy, readonly) NSString *action; - -@property(nonatomic, readonly) firebase::firestore::local::LevelDbTransaction *transaction; - -/** - * Marks the given key for deletion. - * - * @param key The LevelDB key of the row to delete - */ -- (void)removeMessageForKey:(Firestore::StringView)key; - -/** - * Sets the row identified by the given key to the value of the given protocol buffer message. - * - * @param key The LevelDB Key of the row to set. - * @param message The protocol buffer message whose serialized contents should be used for the - * value associated with the key. - */ -- (void)setMessage:(GPBMessage *)message forKey:(Firestore::StringView)key; - -/** - * Sets the row identified by the given key to the value of the given data bytes. - * - * @param key The LevelDB Key of the row to set. - * @param data The exact value to be associated with the key. - */ -- (void)setData:(Firestore::StringView)data forKey:(Firestore::StringView)key; - -/** Returns YES if the write group has no messages in it. */ -- (BOOL)isEmpty; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Local/FSTWriteGroup.mm b/Firestore/Source/Local/FSTWriteGroup.mm deleted file mode 100644 index 9aee31a..0000000 --- a/Firestore/Source/Local/FSTWriteGroup.mm +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "Firestore/Source/Local/FSTWriteGroup.h" - -#include - -#import "Firestore/Source/Local/FSTLevelDBKey.h" -#import "Firestore/Source/Util/FSTAssert.h" - -using firebase::firestore::local::LevelDbTransaction; -using Firestore::StringView; -using leveldb::DB; -using leveldb::Slice; -using leveldb::Status; -using leveldb::WriteBatch; -using leveldb::WriteOptions; - -NS_ASSUME_NONNULL_BEGIN - -@interface FSTWriteGroup () -- (instancetype)initWithAction:(NSString *)action NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithAction:(NSString *)action transaction:(LevelDbTransaction *)transaction; -@end - -@implementation FSTWriteGroup { - int _changes; -} - -+ (instancetype)groupWithAction:(NSString *)action { - return [[FSTWriteGroup alloc] initWithAction:action]; -} - -+ (instancetype)groupWithAction:(NSString *)action - transaction:(firebase::firestore::local::LevelDbTransaction *)transaction { - return [[FSTWriteGroup alloc] initWithAction:action transaction:transaction]; -} - -- (instancetype)initWithAction:(NSString *)action { - if (self = [super init]) { - _action = action; - _transaction = nullptr; - } - return self; -} - -- (instancetype)initWithAction:(NSString *)action transaction:(LevelDbTransaction *)transaction { - if (self = [self initWithAction:action]) { - _transaction = transaction; - } - return self; -} - -- (void)removeMessageForKey:(StringView)key { - FSTAssert(_transaction != nullptr, @"Using group without a transaction"); - Slice keySlice = key; - _transaction->Delete(keySlice.ToString()); - _changes += 1; -} - -- (void)setMessage:(GPBMessage *)message forKey:(StringView)key { - FSTAssert(_transaction != nullptr, @"Using group without a transaction"); - Slice keySlice = key; - _transaction->Put(keySlice.ToString(), message); - _changes += 1; -} - -- (void)setData:(StringView)data forKey:(StringView)key { - FSTAssert(_transaction != nullptr, @"Using group without a transaction"); - Slice keySlice = key; - Slice valueSlice = data; - std::string value = valueSlice.ToString(); - _transaction->Put(keySlice.ToString(), value); - _changes += 1; -} - -- (BOOL)isEmpty { - return _changes == 0; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Local/FSTWriteGroupTracker.h b/Firestore/Source/Local/FSTWriteGroupTracker.h deleted file mode 100644 index c4651ab..0000000 --- a/Firestore/Source/Local/FSTWriteGroupTracker.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import -#import "Firestore/core/src/firebase/firestore/local/leveldb_transaction.h" - -@class FSTWriteGroup; - -NS_ASSUME_NONNULL_BEGIN - -/** - * Helper class for FSTPersistence implementations to create WriteGroups and verify internal - * contracts are maintained: - * 1. Can't create a group when an uncommitted group exists (no nesting). - * 2. Can't commit a group that differs from the last created one. - */ -@interface FSTWriteGroupTracker : NSObject - -/** Creates and returns an FSTWriteGroupTracker instance. */ -+ (instancetype)tracker; - -/** - * Verifies there's no active group already and then creates a new group and stores it for later - * validation with `endGroup`. - */ -- (FSTWriteGroup *)startGroupWithAction:(NSString *)action; - -- (FSTWriteGroup *)startGroupWithAction:(NSString *)action - transaction: - (firebase::firestore::local::LevelDbTransaction *)transaction; - -/** Ends a group previously started with `startGroupWithAction`. */ -- (void)endGroup:(FSTWriteGroup *)group; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Local/FSTWriteGroupTracker.mm b/Firestore/Source/Local/FSTWriteGroupTracker.mm deleted file mode 100644 index 2cb10bd..0000000 --- a/Firestore/Source/Local/FSTWriteGroupTracker.mm +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "Firestore/Source/Local/FSTWriteGroupTracker.h" - -#import "Firestore/Source/Local/FSTWriteGroup.h" -#import "Firestore/Source/Util/FSTAssert.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface FSTWriteGroupTracker () -@property(nonatomic, strong, nullable) FSTWriteGroup *activeGroup; -@end - -@implementation FSTWriteGroupTracker - -+ (instancetype)tracker { - return [[FSTWriteGroupTracker alloc] init]; -} - -- (FSTWriteGroup *)startGroupWithAction:(NSString *)action { - // NOTE: We can relax this to allow nesting if/when we find we need it. - FSTAssert(!self.activeGroup, - @"Attempt to create write group (%@) while existing write group (%@) still active.", - action, self.activeGroup.action); - self.activeGroup = [FSTWriteGroup groupWithAction:action]; - return self.activeGroup; -} - -- (FSTWriteGroup *)startGroupWithAction:(NSString *)action - transaction: - (firebase::firestore::local::LevelDbTransaction *)transaction { - // NOTE: We can relax this to allow nesting if/when we find we need it. - FSTAssert(!self.activeGroup, - @"Attempt to create write group (%@) while existing write group (%@) still active.", - action, self.activeGroup.action); - self.activeGroup = [FSTWriteGroup groupWithAction:action transaction:transaction]; - return self.activeGroup; -} - -- (void)endGroup:(FSTWriteGroup *)group { - FSTAssert(self.activeGroup == group, - @"Attempted to end write group (%@) which is different from active group (%@)", - group.action, self.activeGroup.action); - self.activeGroup = nil; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc b/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc index f034b19..d84d441 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc +++ b/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc @@ -143,6 +143,7 @@ bool LevelDbTransaction::Iterator::Valid() { } LevelDbTransaction::LevelDbTransaction(DB* db, + absl::string_view label, const ReadOptions& read_options, const WriteOptions& write_options) : db_(db), @@ -150,7 +151,8 @@ LevelDbTransaction::LevelDbTransaction(DB* db, deletions_(), read_options_(read_options), write_options_(write_options), - version_(0) { + version_(0), + label_(std::string{label}) { } const ReadOptions& LevelDbTransaction::DefaultReadOptions() { @@ -225,7 +227,7 @@ void LevelDbTransaction::Commit() { } std::string LevelDbTransaction::ToString() { - std::string dest("