Skip to content

Commit 15a38d3

Browse files
authored
Lru Memory GC (#10905)
Adds LRU gc for memory cache.
1 parent 9214b14 commit 15a38d3

31 files changed

+11185
-284
lines changed

Firestore/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
22
- [feature] Add new cache config API to customize SDK cache settings.
3+
- [feature] Add LRU garbage collector as an option to memory cache.
34

45
# 10.8.0
56
- [feature] Change Firestore's Swift Package Manager distribution from source

Firestore/Example/Firestore.xcodeproj/project.pbxproj

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
0D88B4CB916A4752B08E5B42 /* query_listener_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7C3F995E040E9E9C5E8514BB /* query_listener_test.cc */; };
8888
0DAA255C2FEB387895ADEE12 /* bits_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D01201BC69F00D97691 /* bits_test.cc */; };
8989
0DBD29A16030CDCD55E38CAB /* mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3068AA9DFBBA86C1FE2A946E /* mutation_queue_test.cc */; };
90+
0DDCAC7C7CA55CF10AE0E809 /* garbage_collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = AAED89D7690E194EF3BA1132 /* garbage_collection_spec_test.json */; };
9091
0DDEE9FE08845BB7CA4607DE /* grpc_connection_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D9649021544D4F00EB9CFB /* grpc_connection_test.cc */; };
9192
0E17927CE45F5E3FC6691E24 /* firebase_auth_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F869D85E900E5AF6CD02E2FC /* firebase_auth_credentials_provider_test.mm */; };
9293
0E4C94369FFF7EC0C9229752 /* iterator_adaptors_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */; };
@@ -162,6 +163,7 @@
162163
1B816F48012524939CA57CB3 /* user_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CCC9BD953F121B9E29F9AA42 /* user_test.cc */; };
163164
1B9653C51491FAA4BCDE1E11 /* byte_stream_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7628664347B9C96462D4BF17 /* byte_stream_apple_test.mm */; };
164165
1B9E54F4C4280A713B825981 /* token_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A082AFDD981B07B5AD78FDE8 /* token_test.cc */; };
166+
1BB0C34B2E8D8BCC5882430A /* garbage_collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = AAED89D7690E194EF3BA1132 /* garbage_collection_spec_test.json */; };
165167
1BF1F9A0CBB6B01654D3C2BE /* field_transform_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7515B47C92ABEEC66864B55C /* field_transform_test.cc */; };
166168
1C19D796DB6715368407387A /* annotations.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */; };
167169
1C4F88DDEFA6FA23E9E4DB4B /* mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3068AA9DFBBA86C1FE2A946E /* mutation_queue_test.cc */; };
@@ -178,6 +180,7 @@
178180
1D7919CD2A05C15803F5FE05 /* leveldb_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5C7942B6244F4C416B11B86C /* leveldb_mutation_queue_test.cc */; };
179181
1DB3013C5FC736B519CD65A3 /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D221C2DDC800EFB9CC /* common.pb.cc */; };
180182
1DCA68BB2EF7A9144B35411F /* leveldb_opener_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 75860CD13AF47EB1EA39EC2F /* leveldb_opener_test.cc */; };
183+
1E194F1CFDFE0265DF1CD5E6 /* garbage_collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = AAED89D7690E194EF3BA1132 /* garbage_collection_spec_test.json */; };
181184
1E2AE064CF32A604DC7BFD4D /* to_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B696858D2214B53900271095 /* to_string_test.cc */; };
182185
1E41BEEDB1F7F23D8A7C47E6 /* bundle_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6ECAF7DE28A19C69DF386D88 /* bundle_reader_test.cc */; };
183186
1E42CD0F60EB22A5D0C86D1F /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; };
@@ -371,6 +374,7 @@
371374
475FE2D34C6555A54D77A054 /* empty_credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8FA60B08D59FEA0D6751E87F /* empty_credentials_provider_test.cc */; };
372375
4781186C01D33E67E07F0D0D /* orderby_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A21F315EE100DD57A1 /* orderby_spec_test.json */; };
373376
479A392EAB42453D49435D28 /* memory_bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB4AB1388538CD3CB19EB028 /* memory_bundle_cache_test.cc */; };
377+
47B8ED6737A24EF96B1ED318 /* garbage_collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = AAED89D7690E194EF3BA1132 /* garbage_collection_spec_test.json */; };
374378
4809D7ACAA9414E3192F04FF /* FIRGeoPointTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */; };
375379
485CBA9F99771437BA1CB401 /* event_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6F57521E161450FAF89075ED /* event_manager_test.cc */; };
376380
489D672CAA09B9BC66798E9F /* status.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */; };
@@ -430,6 +434,7 @@
430434
5150E9F256E6E82D6F3CB3F1 /* bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F7FC06E0A47D393DE1759AE1 /* bundle_cache_test.cc */; };
431435
518BF03D57FBAD7C632D18F8 /* FIRQueryUnitTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = FF73B39D04D1760190E6B84A /* FIRQueryUnitTests.mm */; };
432436
52967C3DD7896BFA48840488 /* byte_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5342CDDB137B4E93E2E85CCA /* byte_string_test.cc */; };
437+
529AB59F636060FEA21BD4FF /* garbage_collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = AAED89D7690E194EF3BA1132 /* garbage_collection_spec_test.json */; };
433438
53AB47E44D897C81A94031F6 /* write.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D921C2DDC800EFB9CC /* write.pb.cc */; };
434439
53BBB5CDED453F923ADD08D2 /* stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5B5414D28802BC76FDADABD6 /* stream_test.cc */; };
435440
53F449F69DF8A3ABC711FD59 /* secure_random_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A531FC913E500713A1A /* secure_random_test.cc */; };
@@ -606,6 +611,7 @@
606611
5F19F66D8B01BA2B97579017 /* tree_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4D20A36DBB00BCEB75 /* tree_sorted_map_test.cc */; };
607612
5F6CE37B34C542704C5605A4 /* executor_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */; };
608613
5F6FD840AC2D729B50991CCB /* memory_document_overlay_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 29D9C76922DAC6F710BC1EF4 /* memory_document_overlay_cache_test.cc */; };
614+
5F9F1D9B397C4D7EA1E063D2 /* garbage_collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = AAED89D7690E194EF3BA1132 /* garbage_collection_spec_test.json */; };
609615
5FA3DB52A478B01384D3A2ED /* query.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D621C2DDC800EFB9CC /* query.pb.cc */; };
610616
5FC0157A03EF9820BCCCC4A3 /* FSTSyncEngineTestDriver.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02E20213FFC00B64F25 /* FSTSyncEngineTestDriver.mm */; };
611617
5FE047FE866758FD6A6A6478 /* FIRFieldPathTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04C202154AA00B64F25 /* FIRFieldPathTests.mm */; };
@@ -1719,6 +1725,7 @@
17191725
A5FA86650A18F3B7A8162287 /* Pods-Firestore_Benchmarks_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Benchmarks_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Benchmarks_iOS/Pods-Firestore_Benchmarks_iOS.release.xcconfig"; sourceTree = "<group>"; };
17201726
A70E82DD627B162BEF92B8ED /* Pods-Firestore_Example_tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_tvOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_tvOS/Pods-Firestore_Example_tvOS.debug.xcconfig"; sourceTree = "<group>"; };
17211727
A853C81A6A5A51C9D0389EDA /* bundle_loader_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = bundle_loader_test.cc; path = bundle/bundle_loader_test.cc; sourceTree = "<group>"; };
1728+
AAED89D7690E194EF3BA1132 /* garbage_collection_spec_test.json */ = {isa = PBXFileReference; includeInIndex = 1; path = garbage_collection_spec_test.json; sourceTree = "<group>"; };
17221729
AB323F9553050F4F6490F9FF /* pretty_printing_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = pretty_printing_test.cc; path = nanopb/pretty_printing_test.cc; sourceTree = "<group>"; };
17231730
AB380CF82019382300D97691 /* target_id_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = target_id_generator_test.cc; sourceTree = "<group>"; };
17241731
AB380CFC201A2EE200D97691 /* string_util_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_util_test.cc; sourceTree = "<group>"; };
@@ -2651,6 +2658,7 @@
26512658
79EAA9F7B1B9592B5F053923 /* bundle_spec_test.json */,
26522659
54DA129C1F315EE100DD57A1 /* collection_spec_test.json */,
26532660
54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */,
2661+
AAED89D7690E194EF3BA1132 /* garbage_collection_spec_test.json */,
26542662
8C7278B604B8799F074F4E8C /* index_spec_test.json */,
26552663
54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */,
26562664
54DA129F1F315EE100DD57A1 /* limit_spec_test.json */,
@@ -3034,6 +3042,7 @@
30343042
DEF4BF5FAA83C37100408F89 /* bundle_spec_test.json in Resources */,
30353043
546877D52248206A005E3DE0 /* collection_spec_test.json in Resources */,
30363044
546877D62248206A005E3DE0 /* existence_filter_spec_test.json in Resources */,
3045+
1E194F1CFDFE0265DF1CD5E6 /* garbage_collection_spec_test.json in Resources */,
30373046
BFBE4732E93E38317B110778 /* index_spec_test.json in Resources */,
30383047
546877D72248206A005E3DE0 /* limbo_spec_test.json in Resources */,
30393048
546877D82248206A005E3DE0 /* limit_spec_test.json in Resources */,
@@ -3067,6 +3076,7 @@
30673076
6141D3FDF5728FCE9CC1DBFA /* bundle_spec_test.json in Resources */,
30683077
54ACB6C9224C11F400172E69 /* collection_spec_test.json in Resources */,
30693078
54ACB6CA224C11F400172E69 /* existence_filter_spec_test.json in Resources */,
3079+
529AB59F636060FEA21BD4FF /* garbage_collection_spec_test.json in Resources */,
30703080
604B75044D6BEC2B7515EA1B /* index_spec_test.json in Resources */,
30713081
54ACB6CB224C11F400172E69 /* limbo_spec_test.json in Resources */,
30723082
54ACB6CC224C11F400172E69 /* limit_spec_test.json in Resources */,
@@ -3090,6 +3100,7 @@
30903100
4B5FA86D9568ECE20C6D3AD1 /* bundle_spec_test.json in Resources */,
30913101
08839E1CEAAC07E350257E9D /* collection_spec_test.json in Resources */,
30923102
9C1F25177DC5753B075DCF65 /* existence_filter_spec_test.json in Resources */,
3103+
0DDCAC7C7CA55CF10AE0E809 /* garbage_collection_spec_test.json in Resources */,
30933104
77C36312F8025EC73991D7DA /* index_spec_test.json in Resources */,
30943105
F08DA55D31E44CB5B9170CCE /* limbo_spec_test.json in Resources */,
30953106
15A5F95DA733FD89A1E4147D /* limit_spec_test.json in Resources */,
@@ -3113,6 +3124,7 @@
31133124
0B002E2E2012B32EB801C6D5 /* bundle_spec_test.json in Resources */,
31143125
009CDC6F03AC92F3E345085E /* collection_spec_test.json in Resources */,
31153126
7AD020FC27493FF8E659436C /* existence_filter_spec_test.json in Resources */,
3127+
1BB0C34B2E8D8BCC5882430A /* garbage_collection_spec_test.json in Resources */,
31163128
6156C6A837D78D49ED8B8812 /* index_spec_test.json in Resources */,
31173129
85BC2AB572A400114BF59255 /* limbo_spec_test.json in Resources */,
31183130
9F41D724D9947A89201495AD /* limit_spec_test.json in Resources */,
@@ -3155,6 +3167,7 @@
31553167
9C366448F9BA7A4AC0821AF7 /* bundle_spec_test.json in Resources */,
31563168
54DA12A61F315EE100DD57A1 /* collection_spec_test.json in Resources */,
31573169
54DA12A71F315EE100DD57A1 /* existence_filter_spec_test.json in Resources */,
3170+
5F9F1D9B397C4D7EA1E063D2 /* garbage_collection_spec_test.json in Resources */,
31583171
3783E25DFF9E5C0896D34FEF /* index_spec_test.json in Resources */,
31593172
54DA12A81F315EE100DD57A1 /* limbo_spec_test.json in Resources */,
31603173
54DA12A91F315EE100DD57A1 /* limit_spec_test.json in Resources */,
@@ -3196,6 +3209,7 @@
31963209
32030FA5B4BE6ABDFF2F974E /* bundle_spec_test.json in Resources */,
31973210
46B104DEE6014D881F7ED169 /* collection_spec_test.json in Resources */,
31983211
3887E1635B31DCD7BC0922BD /* existence_filter_spec_test.json in Resources */,
3212+
47B8ED6737A24EF96B1ED318 /* garbage_collection_spec_test.json in Resources */,
31993213
E04607A1E2964684184E8AEA /* index_spec_test.json in Resources */,
32003214
2AD8EE91928AE68DF268BEDA /* limbo_spec_test.json in Resources */,
32013215
BC5AC8890974E0821431267E /* limit_spec_test.json in Resources */,

Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ - (void)setUpForSpecWithConfig:(NSDictionary *)config {
4848
}
4949

5050
/** Overrides -[FSTSpecTests persistence] */
51-
- (std::unique_ptr<Persistence>)persistenceWithGCEnabled:(__unused BOOL)GCEnabled {
51+
- (std::unique_ptr<Persistence>)persistenceWithEagerGCForMemory:(__unused BOOL)eagerGC {
5252
return LevelDbPersistenceForTesting(_levelDbDir);
5353
}
5454

Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.mm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ @interface FSTMemorySpecTests : FSTSpecTests
3939
@implementation FSTMemorySpecTests
4040

4141
/** Overrides -[FSTSpecTests persistence] */
42-
- (std::unique_ptr<Persistence>)persistenceWithGCEnabled:(BOOL)GCEnabled {
43-
if (GCEnabled) {
42+
- (std::unique_ptr<Persistence>)persistenceWithEagerGCForMemory:(BOOL)eagerGC {
43+
if (eagerGC) {
4444
return MemoryPersistenceWithEagerGcForTesting();
4545
} else {
4646
return MemoryPersistenceWithLruGcForTesting();

Firestore/Example/Tests/SpecTests/FSTSpecTests.mm

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ @interface FSTSpecTests ()
204204
@end
205205

206206
@implementation FSTSpecTests {
207-
BOOL _gcEnabled;
207+
BOOL _useEagerGCForMemory;
208208
size_t _maxConcurrentLimboResolutions;
209209
BOOL _networkEnabled;
210210
FSTUserDataReader *_reader;
@@ -217,7 +217,7 @@ @implementation FSTSpecTests {
217217
__func__] \
218218
userInfo:nil];
219219

220-
- (std::unique_ptr<Persistence>)persistenceWithGCEnabled:(__unused BOOL)GCEnabled {
220+
- (std::unique_ptr<Persistence>)persistenceWithEagerGCForMemory:(__unused BOOL)eagerGC {
221221
@throw FSTAbstractMethodException(); // NOLINT
222222
}
223223

@@ -237,9 +237,9 @@ - (void)setUpForSpecWithConfig:(NSDictionary *)config {
237237
std::unique_ptr<Executor> user_executor = Executor::CreateSerial("user executor");
238238
user_executor_ = absl::ShareUniquePtr(std::move(user_executor));
239239

240-
// Store GCEnabled so we can re-use it in doRestart.
241-
NSNumber *GCEnabled = config[@"useGarbageCollection"];
242-
_gcEnabled = [GCEnabled boolValue];
240+
// Store eagerGCForMemory so we can re-use it in doRestart.
241+
NSNumber *eagerGCForMemory = config[@"useEagerGCForMemory"];
242+
_useEagerGCForMemory = [eagerGCForMemory boolValue];
243243
NSNumber *maxConcurrentLimboResolutions = config[@"maxConcurrentLimboResolutions"];
244244
_maxConcurrentLimboResolutions = (maxConcurrentLimboResolutions == nil)
245245
? std::numeric_limits<size_t>::max()
@@ -248,9 +248,11 @@ - (void)setUpForSpecWithConfig:(NSDictionary *)config {
248248
if (numClients) {
249249
XCTAssertEqualObjects(numClients, @1, @"The iOS client does not support multi-client tests");
250250
}
251-
std::unique_ptr<Persistence> persistence = [self persistenceWithGCEnabled:_gcEnabled];
251+
std::unique_ptr<Persistence> persistence =
252+
[self persistenceWithEagerGCForMemory:_useEagerGCForMemory];
252253
self.driver =
253254
[[FSTSyncEngineTestDriver alloc] initWithPersistence:std::move(persistence)
255+
eagerGC:_useEagerGCForMemory
254256
initialUser:User::Unauthenticated()
255257
outstandingWrites:{}
256258
maxConcurrentLimboResolutions:_maxConcurrentLimboResolutions];
@@ -558,6 +560,10 @@ - (void)doEnableNetwork {
558560
[self.driver enableNetwork];
559561
}
560562

563+
- (void)doTriggerLruGC:(NSNumber *)threshold {
564+
[self.driver triggerLruGC:threshold];
565+
}
566+
561567
- (void)doChangeUser:(nullable id)UID {
562568
if ([UID isEqual:[NSNull null]]) {
563569
UID = nil;
@@ -573,9 +579,11 @@ - (void)doRestart {
573579

574580
[self.driver shutdown];
575581

576-
std::unique_ptr<Persistence> persistence = [self persistenceWithGCEnabled:_gcEnabled];
582+
std::unique_ptr<Persistence> persistence =
583+
[self persistenceWithEagerGCForMemory:_useEagerGCForMemory];
577584
self.driver =
578585
[[FSTSyncEngineTestDriver alloc] initWithPersistence:std::move(persistence)
586+
eagerGC:_useEagerGCForMemory
579587
initialUser:currentUser
580588
outstandingWrites:outstandingWrites
581589
maxConcurrentLimboResolutions:_maxConcurrentLimboResolutions];
@@ -639,6 +647,8 @@ - (void)doStep:(NSDictionary *)step {
639647
}
640648
} else if (step[@"changeUser"]) {
641649
[self doChangeUser:step[@"changeUser"]];
650+
} else if (step[@"triggerLruGC"]) {
651+
[self doTriggerLruGC:step[@"triggerLruGC"]];
642652
} else if (step[@"restart"]) {
643653
[self doRestart];
644654
} else if (step[@"applyClientState"]) {

Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ typedef std::
123123
* mutation queues).
124124
*/
125125
- (instancetype)initWithPersistence:(std::unique_ptr<local::Persistence>)persistence
126+
eagerGC:(BOOL)eagerGC
126127
initialUser:(const credentials::User &)initialUser
127128
outstandingWrites:(const FSTOutstandingWriteQueues &)outstandingWrites
128129
maxConcurrentLimboResolutions:(size_t)maxConcurrentLimboResolutions NS_DESIGNATED_INITIALIZER;
@@ -266,6 +267,11 @@ typedef std::
266267
*/
267268
- (void)runTimer:(firebase::firestore::util::TimerId)timerID;
268269

270+
/**
271+
* Triggers a LRU GC run with given cache threshold.
272+
*/
273+
- (void)triggerLruGC:(NSNumber *)threshold;
274+
269275
/**
270276
* Switches the FSTSyncEngine to a new user. The test driver tracks the outstanding mutations for
271277
* each user, so future receiveWriteAck/Error operations will validate the write sent to the mock

0 commit comments

Comments
 (0)