-
Notifications
You must be signed in to change notification settings - Fork 934
Implement Eager Reference Delegate for Memory Persistence, drop old GC #1256
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty much good. Some suggestions, and I wonder if waitForAll
could be simplified (see #1269)
* each element in the collection and wait for all of the resulting | ||
* concurrent PersistencePromises to resolve. | ||
*/ | ||
static waitForEach<K>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just revived one of my older PRs that would allow you to merge this with forEach
above. Let me know if you think this could work for you: #1269
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The two methods are still different. forEach
runs the callbacks serially, while waitForEach
runs all of the callbacks and then waits for all of them.
However, I rewrote waitForEach
to take an Iterable
, which I think is more generally useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like my comment on this got lost somehow. If you do think that the parallelism improves things, can we make the difference between forEach
/waitFor
/waitForEach
more explicit? We could maybe rename waitForEach
to forEachParalellel
or something similar (sorry for the horrible name suggestion) or pass in a flag that allows parallelization. For what it's worth, there are currently other calls to forEach
that could be parallelized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am removing the old forEach
and renaming waitForEach
to forEach
. Everything will be parallel.
} | ||
} | ||
return PersistencePromise.waitForEach(removedDocuments, key => { | ||
return this.referenceDelegate.removeMutationReference(transaction, key); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line seems to be missing from Android (at least in master). Is that still coming?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I have a few changes that I need to make to Android as a result of porting. This is one of them.
return PersistencePromise.waitFor([ | ||
store.delete([targetId, path]), | ||
this.referenceDelegate.removeReference(txn, key) | ||
]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could just be:
return store.delete(..).next(() => this.referenceDelegate.removeReference(txn, key))
Unless you have data that the waitFor
parallelism meaningfully improves performance :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we default to concurrent? We're potentially building up a big list of operations here, is there a reason to sequence any of them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are only two promises here, and one of them is a delete for a single key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it's in a loop. We can have a bunch of concurrent promises, or half as many two-step promises. I'd prefer to default to concurrent, especially for things that hit disk.
@@ -244,14 +235,28 @@ class LocalStoreTester { | |||
return this; | |||
} | |||
|
|||
toNotContainIfEager( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! I would maybe flip this to toContainIfLru
, since that feels like the stricter assert. But that's just my personal opinion :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually think it's stronger this way. It's asserting that eager gc will remove the document. LRU won't, no-op won't, and potentially other GC algorithms wouldn't either.
gcIsEager: boolean, | ||
doc: MaybeDocument | ||
): LocalStoreTester { | ||
if (gcIsEager) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we pass gcIsEager
to LocalStoreTester? That way, the tests would not need to know what GC mode they are running under.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and that simplifies .toNotContainIfEager()
calls. But, I think some tests will still need to check? Unless those get moved to the appropriate subclass.
@@ -643,22 +639,25 @@ function genericLocalStoreTests( | |||
}); | |||
|
|||
it('handles SetMutation -> Ack -> PatchMutation -> Reject', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test could be moved to the LocalStore w/ Memory Persistence
"subclass".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started implementing this, but I think it gets a little too ugly with sharing the test infrastructure with the generic tests.
If you feel strongly we can refactor, but I'd like to do it in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was hoping that you could just move the tests method to the respective class, similar to what we do here:
firebase-js-sdk/packages/firestore/test/unit/local/remote_document_cache.test.ts
Line 168 in 3223572
genericRemoteDocumentCacheTests(); |
This can of course be done in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I filed b/117272361 to do it across all clients
.finish(); | ||
}); | ||
|
||
it('collects garbage after ChangeBatch with no target ids', () => { | ||
if (!gcIsEager) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment about moving this to LocalStore w/ Memory Persistence
.
garbageCollector | ||
this.persistence = await this.initPersistence( | ||
this.serializer, | ||
this.useGarbageCollection |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we always "use" garbage collection - we just switch between modes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kinda. But for the purposes of spec tests, lru garbage collection is no garbage collection, since there is, at least currently, no hook to collect any garbage.
Whew, ok, I think it's straightened out now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still feel we can improve our waitFor/forEach/waitForEach story.
Can you also comment on the nits I left on the tests? I just want to make sure you saw them. Thanks!
Sorry, |
* Given an iterable, call the given function on each element in the | ||
* collection and wait for all of the resulting concurrent | ||
* PersistencePromises to resolve. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would make it more explicit that it runs in parallel:
/**
* Given an iterable, call the given function on each element in
* parallel and wait for all of the resulting concurrent
* PersistencePromises to resolve.
*/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your update is actually subtly wrong. The callback is called on each element in sequence, not in parallel. The resulting promises run in parallel.
@@ -643,22 +639,25 @@ function genericLocalStoreTests( | |||
}); | |||
|
|||
it('handles SetMutation -> Ack -> PatchMutation -> Reject', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was hoping that you could just move the tests method to the respective class, similar to what we do here:
firebase-js-sdk/packages/firestore/test/unit/local/remote_document_cache.test.ts
Line 168 in 3223572
genericRemoteDocumentCacheTests(); |
This can of course be done in a separate PR.
In this PR:
MemoryEagerDelegate
, implements eager gc for memory persistenceGarbageCollector
,GarbageSource
and implementations / extensionsFinal portion of LRU port to webclient
Android Port: FirebasePrivate/firebase-android-sdk#190
iOS Port: firebase/firebase-ios-sdk#1600