Skip to content

Make notifyLocalViewChanges idempotent #3044

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 3 commits into from
May 12, 2020

Conversation

schmidt-sebastian
Copy link
Contributor

@schmidt-sebastian schmidt-sebastian commented May 10, 2020

This changes notifyLocalViewChanges so that it has not side effects if the transaction fails. The main change is that I removed the indirection of the reference delegate to ask the LocalStore what documents are "pinned". Instead, the Local Store directly tells the reference delegate about every document that is added to a View.

Adresses #2755

@google-oss-bot
Copy link
Contributor

google-oss-bot commented May 10, 2020

Binary Size Report

Affected SDKs

  • @firebase/firestore

    Type Base (a57dac5) Head (c7d168b) Diff
    browser 249 kB 249 kB -152 B (-0.1%)
    esm2017 194 kB 194 kB -148 B (-0.1%)
    main 490 kB 490 kB -452 B (-0.1%)
    module 247 kB 247 kB -152 B (-0.1%)
  • @firebase/firestore/memory

    Type Base (a57dac5) Head (c7d168b) Diff
    browser 190 kB 190 kB -80 B (-0.0%)
    esm2017 148 kB 148 kB -98 B (-0.1%)
    main 367 kB 366 kB -277 B (-0.1%)
    module 188 kB 188 kB -80 B (-0.0%)
  • firebase

    Type Base (a57dac5) Head (c7d168b) Diff
    firebase-firestore.js 288 kB 288 kB -158 B (-0.1%)
    firebase-firestore.memory.js 230 kB 230 kB -83 B (-0.0%)
    firebase.js 821 kB 821 kB -158 B (-0.0%)

Test Logs

@@ -682,8 +682,7 @@ export class SyncEngine implements RemoteSyncer {
this.queriesByTarget.delete(targetId);

if (this.isPrimaryClient) {
const limboKeys = this.limboDocumentRefs.referencesForId(targetId);
this.limboDocumentRefs.removeReferencesForId(targetId);
const limboKeys = this.limboDocumentRefs.removeReferencesForId(targetId);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combined these lines since removeReferencesForId already returns the references for the target.

@@ -1141,7 +1137,7 @@ export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
.removeTargets(txn, upperBound, activeTargetIds);
}

removeMutationReference(
markPotentiallyOrphaned(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed this method as I need to use it to trigger key to be considered potentially orphaned. Only then with Eager GC pick it up during its GC run. Other names welcome, including removeMutationReference, which reveals less implementation details.

@@ -1070,8 +1070,6 @@ function clientMetadataStore(

/** Provides LRU functionality for IndexedDB persistence. */
export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
private inMemoryPins: ReferenceSet | null = null;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the inMemoryPins redirection to the LocalStore. This was possible because LocalStore now directly pins all documents in a View via addReference.

targetId
);

if (!viewChange.fromCache) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this part of the loop is just moved to below the transaction. The goal is that if the transaction fails, this code doesn't get executed.

(key: DocumentKey) =>
this.persistence.referenceDelegate.removeReference(txn, key)
this.persistence.referenceDelegate.addReference(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the call that makes it possible to remove the local pins.

).next(() => {
this.persistence.referenceDelegate.removeTarget(txn, targetData!);
});
return this.persistence.referenceDelegate.removeTarget(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reference delegate now knows internally what documents are orphaned in the view.

const promises: Array<PersistencePromise<void>> = [];
if (referenceDelegate) {
keys.forEach(key => {
promises.push(referenceDelegate.addReference(txn, key));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could keep this call... but all this does now is mark a document as non-orphaned if it was previously orphaned. The keys now also added to the target as part of notifyLocalViewChanges .

private inMemoryPins: ReferenceSet | null = null;
/** Manages all documents that are active in Query views. */
private localViewReferences: ReferenceSet = new ReferenceSet();
/** The list of documents that are potentially GCed after each transaction. */
private _orphanedDocuments: Set<DocumentKey> | null = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: why is this variable prefixed with an underscore, while localViewReferences isn't?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, we don't use underscore prefixes for classes that are not part of the private API. This is an exception as orphanedDocuments has a getter that verifies that is is set correctly. orphanedDocuments is cleared between each transaction run.

@@ -184,7 +184,9 @@ export interface MemoryReferenceDelegate extends ReferenceDelegate {
}

export class MemoryEagerDelegate implements MemoryReferenceDelegate {
private inMemoryPins: ReferenceSet | null = null;
/** Manages all documents that are active in Query views. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I always find that manage is one of the words that can usually be replaced with something more specific. Even keeps track of, to me, seems more specific.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope this is not a general stance on management :) Updated to use "track".

@schmidt-sebastian schmidt-sebastian merged commit 74621b7 into master May 12, 2020
@firebase firebase locked and limited conversation to collaborators Jun 12, 2020
@schmidt-sebastian schmidt-sebastian deleted the mrschmidt/idempotentrefsv2 branch November 9, 2020 22:39
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.

3 participants