Skip to content

Port waitForPendingWrites from android #2081

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
Aug 15, 2019
Merged

Conversation

wu-hui
Copy link
Contributor

@wu-hui wu-hui commented Aug 13, 2019

  • Port waitForPendingWrites

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 looks like it does the trick, but I left some improvement suggestions. Thanks!

* Waits until all currently pending writes for the active user have been acknowledged by the
* backend.
*
* The returned Promise completes immediately if there are no outstanding writes. Otherwise, the
Copy link
Contributor

Choose a reason for hiding this comment

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

s/completes/resolves/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

* acknowledged by the backend.
*/
_waitForPendingWrites(): Promise<void> {
return this._firestoreClient!.waitForPendingWrites();
Copy link
Contributor

Choose a reason for hiding this comment

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

Please call ensureClientConfigured() to make sure this works even when it is the first operation that is scheduled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -522,6 +522,20 @@ export class FirestoreClient {
});
}

/**
* Returns a `Promise` resolves when all the pending writes at the time when this method is called
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 remove the backticks for Promise. It's just a normal term in JS, not a class or method name (also it should be "Promise that resolves");

Also: s/when all the pending writes at the time/when all writes that were pending at the time/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -602,6 +609,62 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
}
}

/**
* Takes a snapshot of current mutation queue, and register a user callback
Copy link
Contributor

Choose a reason for hiding this comment

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

s/register/registers

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -602,6 +609,62 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
}
}

/**
* Takes a snapshot of current mutation queue, and register a user callback
* which will be called when all those mutations in the snapshot are either
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you mean by "snapshot" here? Snapshot usually means something different in our code base.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed.


// pending writes can receive acknowledgements now.
await firestore.enableNetwork();
await expect(pendingWrites).to.be.eventually.fulfilled;
Copy link
Contributor

Choose a reason for hiding this comment

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

await expect(pendingWrites).to.be.eventually.fulfilled is the same as await pendingWrites.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


mockCredentialsProvider.changeUserTo(new User('user_1'));

await expect(awaitPendingWrite).to.be.eventually.rejected;
Copy link
Contributor

Choose a reason for hiding this comment

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

Please do test the error message as well. It will also make the test much clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

await firestore.disableNetwork();

const awaitPendingWrites = waitForPendingWrites(firestore);
await expect(awaitPendingWrites).to.be.eventually.fulfilled;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the same test case as above. In general, it is usually better to have smaller and more focused test cases. Maybe we should remove the "offline but no writes" case from above and only test it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


private listener: CredentialChangeListener | null = null;

changeUserTo(user: User): void {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think it makes sense to call this handleUserChange to match SyncEngine?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

handleUserChange implies it's handling something, which is not true. Changed to triggerUserChange instead.

): Promise<void> {
const mockCredentialsProvider = new MockCredentialsProvider();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const settings: any = { ...DEFAULT_SETTINGS };
Copy link
Contributor

Choose a reason for hiding this comment

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

You can potentially remove the any:

const settings = {... DEFAULT_SETTINGS, credentials: { client: mockCredentialsProvider, type: 'provider' }}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@wu-hui wu-hui changed the title waitForPendingWrites + allow-unlisten-after-shutdown. Port waitForPendingWrites from android Aug 14, 2019
@wu-hui wu-hui assigned schmidt-sebastian and unassigned wu-hui Aug 14, 2019
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.

LGTM with some comment nits. I do want you to make registerPendingWritesCallback non-async though :)

return Promise.resolve();
}

const callbacks = this.pendingWritesCallbacks.get(largestBatchId) || [];
Copy link
Contributor

Choose a reason for hiding this comment

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

You can invoke yarn build in "packages/firestore". It will spit out a couple different files into "packages/firestore/dist"

In packages/firestore/dist/index.esm.js, your current code looks as such:

callbacks = this.pendingWritesCallbacks.get(highestBatchId) || [];
callbacks.push(callback);
this.pendingWritesCallbacks.set(highestBatchId, callbacks);

It looks like we have to specify different compiler settings to make my suggestion work (--downlevelIterators)/ Since it is just an optional suggestion, let's keep it as is.

* backend.
*
* The returned Promise resolves immediately if there are no outstanding writes. Otherwise, the
* Task waits for all previously issued writes (including those written in a previous app
Copy link
Contributor

Choose a reason for hiding this comment

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

s/Task/Promise/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

*
* Any outstanding `waitForPendingWrites()` Promises are rejected during user changes.
*
* @return A `Promise` which resolves when all currently pending writes have been
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: remove backticks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -522,6 +522,21 @@ export class FirestoreClient {
});
}

/**
* Returns a Promise that resolves when when all writes that were pending at the time when this
Copy link
Contributor

Choose a reason for hiding this comment

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

s/when when/when/
s/at the time when/at the time/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -522,6 +522,21 @@ export class FirestoreClient {
});
}

/**
* Returns a Promise that resolves when when all writes that were pending at the time when this
* method is called received server acknowledgement. An acknowledgement can be either acceptance
Copy link
Contributor

Choose a reason for hiding this comment

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

s/is/was/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -450,6 +452,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
(this.isPrimary && source === OnlineStateSource.RemoteStore) ||
(!this.isPrimary && source === OnlineStateSource.SharedClientState)
) {
this.assertSubscribed('applyOnlineStateChange()');
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. This was missing but should be present. Otherwise this.syncEngineListener!.* might hit errors.

@@ -602,6 +609,59 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
}
}

/**
* Registers a user callback with all the pending mutations at the moment of calling.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you update this to:

Registers a user callback that resolves when all pending mutations at the moment of calling are acknowledged .

You can then remove the second sentence.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

* if there are any.
*/
private triggerPendingWritesCallbacks(batchId: BatchId): void {
(this.pendingWritesCallbacks.get(batchId) || []).forEach(callback => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I learned something today :)

// Prevent pending writes receiving acknowledgement.
await firestore.disableNetwork();

// `awaitsPendingWrites` is created when there is no pending writes, it will resolve
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like it needs reformatting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


// `awaitsPendingWrites` is created when there is no pending writes, it will resolve
// immediately even if we are offline.
const awaitPendingWrites = waitForPendingWrites(firestore);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest to remove the redundant variable here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@schmidt-sebastian
Copy link
Contributor

I do want you to make registerPendingWritesCallback non-async though :)

Looks like this is not possible as getHighestUnacknowledgedBatchId returns a Promise. Thanks for pointing this out.

@wu-hui wu-hui merged commit f18b0fb into master Aug 15, 2019
@firebase firebase locked and limited conversation to collaborators Oct 8, 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.

2 participants