Skip to content

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

Merged
merged 12 commits into from
Oct 4, 2018

Conversation

gsoltis
Copy link

@gsoltis gsoltis commented Sep 26, 2018

In this PR:

  • Implement MemoryEagerDelegate, implements eager gc for memory persistence
  • Remove GarbageCollector, GarbageSource and implementations / extensions
  • Fix up tests, mostly in local store.

Final portion of LRU port to webclient
Android Port: FirebasePrivate/firebase-android-sdk#190
iOS Port: firebase/firebase-ios-sdk#1600

Copy link
Contributor

@schmidt-sebastian schmidt-sebastian left a 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>(
Copy link
Contributor

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

Copy link
Author

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.

Copy link
Contributor

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.

Copy link
Author

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);
Copy link
Contributor

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?

Copy link
Author

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)
]);
Copy link
Contributor

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 :)

Copy link
Author

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?

Copy link
Contributor

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.

Copy link
Author

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(
Copy link
Contributor

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 :)

Copy link
Author

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) {
Copy link
Contributor

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.

Copy link
Author

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', () => {
Copy link
Contributor

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".

Copy link
Author

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.

Copy link
Contributor

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:

This can of course be done in a separate PR.

Copy link
Author

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) {
Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Author

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.

@gsoltis
Copy link
Author

gsoltis commented Oct 2, 2018

Whew, ok, I think it's straightened out now.

Copy link
Contributor

@schmidt-sebastian schmidt-sebastian left a 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!

@gsoltis
Copy link
Author

gsoltis commented Oct 3, 2018

Sorry, local_store.test.ts comments got hidden. Will address those shortly.

* Given an iterable, call the given function on each element in the
* collection and wait for all of the resulting concurrent
* PersistencePromises to resolve.
*/
Copy link
Contributor

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.
   */

Copy link
Author

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', () => {
Copy link
Contributor

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:

This can of course be done in a separate PR.

@gsoltis gsoltis merged commit cce601e into lru-master Oct 4, 2018
@gsoltis gsoltis deleted the gsoltis/eager_delegate branch October 4, 2018 17:19
@gsoltis gsoltis mentioned this pull request Oct 4, 2018
gsoltis pushed a commit that referenced this pull request Oct 4, 2018
* Implement IndexedDB LRU Reference Delegate, add LRU tests (#1224)
* Implement LRU Reference Delegate for Memory Persistence (#1237)
* Implement Eager Reference Delegate for Memory Persistence, drop old GC (#1256)
* Remove sequential forEach, replace with parallel forEach
@firebase firebase locked and limited conversation to collaborators Oct 16, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants