Skip to content

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

Merged
merged 4 commits into from
Jul 2, 2018

Conversation

mikelehen
Copy link
Contributor

@mikelehen mikelehen commented Jun 29, 2018

No functional changes. Just renames, moves, added comments, etc.

  • pendingWrites => writePipeline
  • canWriteMutations() => canAddToWritePipeline().
  • 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.

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

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

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.

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

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.

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

* LocalStore via fillWritePipeline() and have or will send to the write
* stream.
*
* Whenever writePipeline.length > 0 (and the network is enabled) the write
Copy link
Contributor

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.

Copy link
Contributor Author

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

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.

Copy link
Contributor Author

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

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.

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'm torn on this.

  1. 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).
  2. It means we do redundant work to re-load the same set of mutations from LocalStore when you re-enable the network.
  3. 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.
Copy link
Contributor

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?

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.

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

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.

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

Copy link
Contributor Author

@mikelehen mikelehen left a 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
Copy link
Contributor Author

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

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
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'm torn on this.

  1. 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).
  2. It means we do redundant work to re-load the same set of mutations from LocalStore when you re-enable the network.
  3. 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.
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.

*/
canWriteMutations(): boolean {
isWritePipelineFull(): boolean {
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 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.
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 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
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

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 like the new symmetry between canAddToWritePipeline and addToWritePipeline. Thanks for updating the comments!

@mikelehen mikelehen merged commit 63e1414 into master Jul 2, 2018
@mikelehen mikelehen deleted the mikelehen/remove-lastBatchSeen branch July 2, 2018 16:42
mikelehen pushed a commit to firebase/firebase-ios-sdk that referenced this pull request Aug 15, 2018
[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.
mikelehen pushed a commit to firebase/firebase-ios-sdk that referenced this pull request Aug 15, 2018
[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.
mikelehen added a commit to firebase/firebase-ios-sdk that referenced this pull request Aug 16, 2018
[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.
@firebase firebase locked and limited conversation to collaborators Oct 18, 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