-
Notifications
You must be signed in to change notification settings - Fork 929
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,12 +28,12 @@ import { | |
import { MaybeDocument, NoDocument } from '../model/document'; | ||
import { DocumentKey } from '../model/document_key'; | ||
import { Mutation } from '../model/mutation'; | ||
import { MutationBatchResult } from '../model/mutation_batch'; | ||
import { MutationBatchResult, BATCHID_UNKNOWN } from '../model/mutation_batch'; | ||
import { RemoteEvent, TargetChange } from '../remote/remote_event'; | ||
import { RemoteStore } from '../remote/remote_store'; | ||
import { RemoteSyncer } from '../remote/remote_syncer'; | ||
import { assert, fail } from '../util/assert'; | ||
import { FirestoreError } from '../util/error'; | ||
import { Code, FirestoreError } from '../util/error'; | ||
import * as log from '../util/log'; | ||
import { primitiveComparator } from '../util/misc'; | ||
import { ObjectMap } from '../util/obj_map'; | ||
|
@@ -160,6 +160,8 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer { | |
private mutationUserCallbacks = {} as { | ||
[uidKey: string]: SortedMap<BatchId, Deferred<void>>; | ||
}; | ||
/** Stores user callbacks waiting for all pending writes to be acknowledged. */ | ||
private pendingWritesCallbacks = new Map<BatchId, Array<Deferred<void>>>(); | ||
private limboTargetIdGenerator = TargetIdGenerator.forSyncEngine(); | ||
|
||
// The primary state is set to `true` or `false` immediately after Firestore | ||
|
@@ -450,6 +452,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer { | |
(this.isPrimary && source === OnlineStateSource.RemoteStore) || | ||
(!this.isPrimary && source === OnlineStateSource.SharedClientState) | ||
) { | ||
this.assertSubscribed('applyOnlineStateChange()'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this intended? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. This was missing but should be present. Otherwise |
||
const newViewSnapshots = [] as ViewSnapshot[]; | ||
this.queryViewsByQuery.forEach((query, queryView) => { | ||
const viewChange = queryView.view.applyOnlineStateChange(onlineState); | ||
|
@@ -570,6 +573,8 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer { | |
// before listen events. | ||
this.processUserCallback(batchId, /*error=*/ null); | ||
|
||
this.triggerPendingWritesCallbacks(batchId); | ||
|
||
try { | ||
const changes = await this.localStore.acknowledgeBatch( | ||
mutationBatchResult | ||
|
@@ -593,6 +598,8 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer { | |
// listen events. | ||
this.processUserCallback(batchId, error); | ||
|
||
this.triggerPendingWritesCallbacks(batchId); | ||
|
||
try { | ||
const changes = await this.localStore.rejectBatch(batchId); | ||
this.sharedClientState.updateMutationState(batchId, 'rejected', error); | ||
|
@@ -602,6 +609,58 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer { | |
} | ||
} | ||
|
||
/** | ||
* Registers a user callback that resolves when all pending mutations at the moment of calling | ||
* are acknowledged . | ||
*/ | ||
async registerPendingWritesCallback(callback: Deferred<void>): Promise<void> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method doesn't seem to have to be async. You can just return void. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is executed on async queue, the enqueue method demands it to return a promise. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please follow the pattern that we use here: https://github.com/firebase/firebase-js-sdk/pull/2063/files#diff-3bab365c04d7fc7ae24002035afdd01fR614 It makes the interactions in the client simpler if we limit async methods to their smallest possible scope. Right now, registerPendingWritesCallback only has one consumer, but once it has two or more this would really make a difference (and even with just one consumer, you can already remove two Promise.resolve() invocations that the transpiler adds for you. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (!this.remoteStore.canUseNetwork()) { | ||
log.debug( | ||
LOG_TAG, | ||
'The network is disabled. The task returned by ' + | ||
"'awaitPendingWrites()' will not complete until the network is enabled." | ||
); | ||
} | ||
|
||
const highestBatchId = await this.localStore.getHighestUnacknowledgedBatchId(); | ||
if (highestBatchId === BATCHID_UNKNOWN) { | ||
// Trigger the callback right away if there is no pending writes at the moment. | ||
callback.resolve(); | ||
return; | ||
} | ||
|
||
const callbacks = this.pendingWritesCallbacks.get(highestBatchId) || []; | ||
callbacks.push(callback); | ||
this.pendingWritesCallbacks.set(highestBatchId, callbacks); | ||
} | ||
|
||
/** | ||
* Triggers the callbacks that are waiting for this batch id to get acknowledged by server, | ||
* if there are any. | ||
*/ | ||
private triggerPendingWritesCallbacks(batchId: BatchId): void { | ||
(this.pendingWritesCallbacks.get(batchId) || []).forEach(callback => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you use a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, that does not work..compiler still unhappy about it could be potentially There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I learned something today :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :) |
||
callback.resolve(); | ||
}); | ||
|
||
this.pendingWritesCallbacks.delete(batchId); | ||
} | ||
|
||
/** Reject all outstanding callbacks waiting for pending writes to complete. */ | ||
private rejectOutstandingPendingWritesCallbacks(errorMessage: string): void { | ||
this.pendingWritesCallbacks.forEach(callbacks => { | ||
callbacks.forEach(callback => { | ||
callback.reject( | ||
new FirestoreError( | ||
Code.CANCELLED, errorMessage | ||
) | ||
); | ||
}); | ||
}); | ||
|
||
this.pendingWritesCallbacks.clear(); | ||
} | ||
|
||
private addMutationCallback( | ||
batchId: BatchId, | ||
callback: Deferred<void> | ||
|
@@ -799,6 +858,11 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer { | |
this.currentUser = user; | ||
|
||
if (userChanged) { | ||
// Fails tasks waiting for pending writes requested by previous user. | ||
this.rejectOutstandingPendingWritesCallbacks( | ||
"'waitForPendingWrites' promise is rejected due to a user change." | ||
); | ||
|
||
const result = await this.localStore.handleUserChange(user); | ||
// TODO(b/114226417): Consider calling this only in the primary tab. | ||
this.sharedClientState.handleUserChange( | ||
|
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.
Please call
ensureClientConfigured()
to make sure this works even when it is the first operation that is scheduled.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.
Done.