Skip to content

Conversation

@cjihrig
Copy link
Contributor

@cjihrig cjihrig commented May 7, 2019

This commit adds client streaming RPC support.

This commit adds client streaming RPC support.
this.cancelled = false;
this.decoder = new StreamDecoder();

const http2Stream = this.call._getHttp2Stream();
Copy link
Member

Choose a reason for hiding this comment

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

This really feels like an abstraction layering violation. The Http2ServerCallStream owns the http2 stream object, and all interaction with the http2 stream should go through it.

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 open to suggestions. I can think of a few other ways of doing this, but I'd like to avoid guessing wrong and rewriting it multiple times.

Copy link
Contributor

Choose a reason for hiding this comment

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

So, I don't think that @murgatroid99 was hinting at a particular way of writing this. The gist of his remark is that if we look at the current structure of the code, you have this class you wrote a little while back named Http2ServerCallStream with a comment on it that says it's wrapping the HTTP2 request.

But now, in this new pull request, you're bypassing this wrapper, and you are accessing the ServerHttp2Stream object directly. So, from an overall design point of view, this seems inconsistent. On one hand, you're wrapping the features of ServerHttp2Stream, mutating some of this class' behavior such as the write function, and on the other hand, you're bypassing these mutations. Thinking about the future maintainer of this code, one needs precise rules and contracts about what's being wrapped and why.

If you're willing to try and bypass the wrapping you introduced yourself, would you mind sharing your thoughts on what the contracts here should be?

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 was just trying to keep the overhead low since it's an internal class. I can have the ServerHttp2Stream instance listen for 'data' and 'end' events from the http2 stream and re-emit them. Similarly, I can wrap the pause() and resume() methods. Would you prefer that, or do you have other ideas (which is what I meant by my original comment)?

Copy link
Member

Choose a reason for hiding this comment

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

Directly listening to the 'data' event won't work well because the stream classes have special handling around that event: they will start reading as soon as you add that listener.

The strategy used on the client side is for the internal call stream class to be a duplex stream of Buffer messages, and when the surface call needs to send or receive a unary message it just writes or reads only one message. I think it would be reasonable to do that here too.

Another option I can see is to have all of the Server*Impl classes directly own and interact with the ServerHttp2Stream, with helper functions or a helper class for de-duplicating some code.

This commit adds a setupReadable() method to
Http2ServerCallStream. This is used to set up the plumbing
between the HTTP2 stream and the surface readable/bidi
calls.
Copy link
Member

@murgatroid99 murgatroid99 left a comment

Choose a reason for hiding this comment

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

Other than one simple change this looks good to me.

}

deserialize(input: Buffer): RequestType|null {
if (input === null || input === undefined) {
Copy link
Member

Choose a reason for hiding this comment

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

The function type declaration says this will never be null or undefined. And this is intended to only be called by our own internal code. And the code that calls it explicitly checks for null first. So this whole condition should be unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK. I've removed it.

This commit removes null and undefined checks from deserialize().
@murgatroid99 murgatroid99 merged commit 3bebc22 into grpc:master May 15, 2019
@cjihrig cjihrig deleted the client-stream branch May 15, 2019 17:25
@lock lock bot locked as resolved and limited conversation to collaborators Aug 13, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants