-
Notifications
You must be signed in to change notification settings - Fork 934
Refactor pendingWrites / write pipeline. #972
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
No functional changes. Just renames, moves, added comments, etc. * pendingWrites => writePipeline * canWriteMutations() inverted and renamed isWritePipelineFull(). * commit() => addToWritePipeline() * lastBatchSeen removed since we can compute it on demand from writePipeline. * Removed cleanUpWriteStreamState() and instead inlined it in disableNetworkInternal() since I didn't like the non-symmetry with cleanUpWatchStreamState() which is called every time the stream closes. * Lots of comment cleanup.
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.
Comments are 10/10. Naming is 9.5/10 :)
I feel that Gil should look at the names of the variables as well (if he has time before his vacation). I am not super sold on isWritePipelineFull
. If you have more naming suggestions, please do reach out before submitting. Thanks!
*/ | ||
canWriteMutations(): boolean { | ||
isWritePipelineFull(): boolean { |
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 older name did a better job conveying the dual purpose of this function. The new name might create confusion if the function returns true but the pipeline is empty. I'm gonna throw canAcceptMutations
out there and see if you like that better.
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 reworked into canAddToWritePipeline() for now, which is hopefully abstract enough to encompass isNetworkEnabled
, but still concretely ties it to the writePipeline.
I'd actually like to remove the isNetworkEnabled
restriction and allow writes in the pipeline when the network is not enabled. But that may be contentious (see Gil's comment on my TODO about that). So I'll propose that in a separate PR and see what we want to do.
private pendingWrites: MutationBatch[] = []; | ||
private lastBatchSeen: BatchId = BATCHID_UNKNOWN; | ||
/** | ||
* A set of up to MAX_PENDING_WRITES writes that we have fetched from the |
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.
Since order matters, better to call this a "list" of writes.
Not sure, but you could also highlight that the protocol correlates responses with requests implicitly, so it's the order within this list that maintains the correlation.
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
* LocalStore via fillWritePipeline() and have or will send to the write | ||
* stream. | ||
* | ||
* Whenever writePipeline.length > 0 (and the network is enabled) the write |
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.
If the network is disabled can the writePipeline have any writes in it? This phrasing makes it seem as if it's possible for there to be writes even when the network is disabled (and suggests the question of how to consider those).
If we can't have writes in here with the network disabled it's probably better to just get that out of the way early.
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.
As written we go out of our way to clear the the pipeline when the network is disabled and avoid adding writes. But I think the code would be simpler (and still work) if we didn't do that. So I'm tempted to change this behavior in a subsequent change (I added a TODO to disableNetworkInternal already).
But for now I've adjusted the comment to match the implemented behavior. Thanks.
* | ||
* Whenever writePipeline.length > 0 (and the network is enabled) the write | ||
* stream is considered to be necessary and all the writes will be sent to the | ||
* backend if/when the stream is established. |
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.
"if/when" can just be "when".
You may also consider rephrasing this into active voice. For example, rather than "considered to be necessary":
Whenever writePipeline.length > 0 the RemoteStore will attempt to start or restart the write stream. When the stream is established the writes in the pipeline will be sent in order.
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.
Better, thanks!
this.writePipeline.length + | ||
' pending writes' | ||
); | ||
// TODO(mikelehen): We only actually need to clear the write pipeline if |
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.
It seems reasonable to me that we only attempt to fill the write pipeline if the network is enabled. I think you can drop this todo.
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'm torn on this.
- We're currently going out of our way to avoid filling writePipeline when the network is disabled (I could remove
this.writePipeline = []
here and the networkEnabled check in isWritePipelineFull() if we allowed writePipeline to persist when the network was disabled). - It means we do redundant work to re-load the same set of mutations from LocalStore when you re-enable the network.
- I think it may be conceptually simpler to just consider writePipeline to be the "pre-fetched" mutations from the LocalStore, regardless of the network state... since they already persist across stream close / restarts.
So I'm inclined to leave it for now.
/** | ||
* Notifies that there are new mutations to process in the queue. This is | ||
* typically called by SyncEngine after it has sent mutations to LocalStore. | ||
* Attempts to fill our write pipeline with mutations from the LocalStore. |
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.
Let's avoid mixing writes and mutations. Since this is a write pipeline we fill it with writes, no?
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.
// DOSing ourselves then those limits won't be exceeded here and | ||
// we'll continue to make progress. | ||
for (const batch of this.pendingWrites) { | ||
// Drain the write pipeline now that the stream is established. |
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.
"Drain" is incorrect because we're not removing anything. At this point we're merely sending all the queued writes but they don't get removed until there's a response from the server.
In this sense "pending writes" was a better name because it didn't suggest direct sequential processing.
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.
Changed to "Send." I agree "pending writes" was a better name once they're sent. But since they may not be sent, I think it was overall a poor name.
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.
Thanks for the feedback. I tweaked comments and reworked isWritePipelineFull() to canAddToWritePipeline() in response. Take another look if you like.
* LocalStore via fillWritePipeline() and have or will send to the write | ||
* stream. | ||
* | ||
* Whenever writePipeline.length > 0 (and the network is enabled) the write |
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.
As written we go out of our way to clear the the pipeline when the network is disabled and avoid adding writes. But I think the code would be simpler (and still work) if we didn't do that. So I'm tempted to change this behavior in a subsequent change (I added a TODO to disableNetworkInternal already).
But for now I've adjusted the comment to match the implemented behavior. Thanks.
* | ||
* Whenever writePipeline.length > 0 (and the network is enabled) the write | ||
* stream is considered to be necessary and all the writes will be sent to the | ||
* backend if/when the stream is established. |
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.
Better, thanks!
this.writePipeline.length + | ||
' pending writes' | ||
); | ||
// TODO(mikelehen): We only actually need to clear the write pipeline if |
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'm torn on this.
- We're currently going out of our way to avoid filling writePipeline when the network is disabled (I could remove
this.writePipeline = []
here and the networkEnabled check in isWritePipelineFull() if we allowed writePipeline to persist when the network was disabled). - It means we do redundant work to re-load the same set of mutations from LocalStore when you re-enable the network.
- I think it may be conceptually simpler to just consider writePipeline to be the "pre-fetched" mutations from the LocalStore, regardless of the network state... since they already persist across stream close / restarts.
So I'm inclined to leave it for now.
/** | ||
* Notifies that there are new mutations to process in the queue. This is | ||
* typically called by SyncEngine after it has sent mutations to LocalStore. | ||
* Attempts to fill our write pipeline with mutations from the LocalStore. |
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.
*/ | ||
canWriteMutations(): boolean { | ||
isWritePipelineFull(): boolean { |
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 reworked into canAddToWritePipeline() for now, which is hopefully abstract enough to encompass isNetworkEnabled
, but still concretely ties it to the writePipeline.
I'd actually like to remove the isNetworkEnabled
restriction and allow writes in the pipeline when the network is not enabled. But that may be contentious (see Gil's comment on my TODO about that). So I'll propose that in a separate PR and see what we want to do.
// DOSing ourselves then those limits won't be exceeded here and | ||
// we'll continue to make progress. | ||
for (const batch of this.pendingWrites) { | ||
// Drain the write pipeline now that the stream is established. |
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.
Changed to "Send." I agree "pending writes" was a better name once they're sent. But since they may not be sent, I think it was overall a poor name.
private pendingWrites: MutationBatch[] = []; | ||
private lastBatchSeen: BatchId = BATCHID_UNKNOWN; | ||
/** | ||
* A set of up to MAX_PENDING_WRITES writes that we have fetched from the |
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
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 like the new symmetry between canAddToWritePipeline
and addToWritePipeline
. Thanks for updating the comments!
[Port of firebase/firebase-js-sdk#972] No functional changes. Just renames, moves, added comments, etc. * pendingWrites => writePipeline * canWriteMutations renamed canAddToWritePipeline * commitBatch => addBatchToWritePipeline * lastBatchSeen removed since we can compute it on demand from writePipeline. * Removed cleanUpWriteStreamState and instead inlined it in disableNetworkInternal since I didn't like the non-symmetry with cleanUpWatchStreamState which is called every time the stream closes. * Misc. comment cleanup.
[Port of firebase/firebase-js-sdk#972] No functional changes. Just renames, moves, added comments, etc. * pendingWrites => writePipeline * canWriteMutations renamed canAddToWritePipeline * commitBatch => addBatchToWritePipeline * lastBatchSeen removed since we can compute it on demand from writePipeline. * Removed cleanUpWriteStreamState and instead inlined it in disableNetworkInternal since I didn't like the non-symmetry with cleanUpWatchStreamState which is called every time the stream closes. * Misc. comment cleanup.
[Port of firebase/firebase-js-sdk#972] No functional changes. Just renames, moves, added comments, etc. * pendingWrites => writePipeline * canWriteMutations renamed canAddToWritePipeline * commitBatch => addBatchToWritePipeline * lastBatchSeen removed since we can compute it on demand from writePipeline. * Removed cleanUpWriteStreamState and instead inlined it in disableNetworkInternal since I didn't like the non-symmetry with cleanUpWatchStreamState which is called every time the stream closes. * Misc. comment cleanup.
No functional changes. Just renames, moves, added comments, etc.
writePipeline.
disableNetworkInternal() since I didn't like the non-symmetry with
cleanUpWatchStreamState() which is called every time the stream closes.