-
Notifications
You must be signed in to change notification settings - Fork 790
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
Managed gRPC client #212
Managed gRPC client #212
Conversation
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.
Initial comments. Still reviewing.
|
||
namespace Grpc.NetCore.HttpClient.Internal | ||
{ | ||
internal static class GrpcProtocolConstants |
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.
We need a better way to share these constants between the projects.
|
||
namespace Grpc.NetCore.HttpClient.Internal | ||
{ | ||
internal static class GrpcProtocolHelpers |
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.
Same here.
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 world is a better place when you just drop the word "helpers" from these apis. 😆
src/Grpc.NetCore.HttpClient/Internal/HttpContentClientStreamWriter.cs
Outdated
Show resolved
Hide resolved
src/Grpc.NetCore.HttpClient/Internal/HttpContextClientStreamReader.cs
Outdated
Show resolved
Hide resolved
src/Grpc.NetCore.HttpClient/Internal/HttpContextClientStreamReader.cs
Outdated
Show resolved
Hide resolved
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.
Looks good overall.
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 think this is mostly looking good, but I'd still like to give it a closer look next week. If this PR is blocking your other work though, feel free to merge after addressing comments.
src/Grpc.NetCore.HttpClient/Internal/HttpContentClientStreamWriter.cs
Outdated
Show resolved
Hide resolved
cbdfac4
to
547a77c
Compare
Big update. @jtattermusch Can you review the interop test client? I'd like to merge this and get the HttpClient gRPC client running in your interop environment ASAP. If there are failures caused by other servers, and the problem is HttpClient then we need to get issues raised to the HttpClient team quickly. Interop test status:
|
@@ -1,5 +1,5 @@ | |||
{ | |||
"sdk": { | |||
"version": "3.0.100-preview4-011108" | |||
"version": "3.0.100-preview6-011568" |
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.
Updated the SDK so HttpClient supports making HTTP/2 requests without TLS.
src/Grpc.NetCore.HttpClient/Internal/HttpContextClientStreamReader.cs
Outdated
Show resolved
Hide resolved
src/Grpc.NetCore.HttpClient/Internal/HttpContextClientStreamReader.cs
Outdated
Show resolved
Hide resolved
if (_timeout != null) | ||
{ | ||
// Deadline timer will cancel the call CTS | ||
_deadlineTimer = new Timer(DeadlineExceeded, null, _timeout.Value, Timeout.InfiniteTimeSpan); |
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.
What's the reason to use this instead of .CancelAfter
?
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.
A different status is returned depending on whether the call was cancelled via a token or via the deadline being exceeded. A CTS doesn't provide a reason why it was cancelled (Cancel vs CancelAfter)
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 might be better to construct a new CTS with the specified timeout and register DeadlineExceeded with that instead of using a timer. That way when you dispose the registration, you wait for DeadlineExceeded to complete (if it's currently executing) automatically.
|
||
public void Dispose() | ||
{ | ||
if (!Disposed) |
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's not super clear to me what the threading model is for this (since you've got cancellation and timeouts). How does this work without locking?
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 concerned about this too.
As long as the GrpcCall object itself is accessed from a single logical thread and then disposed after it's used, _ctsRegistration?.Dispose()
should block until any possibly-already-triggered calls to CancelCall
complete. This effectively locks on our behalf.
I don't know if _deadlineTimer?.Dispose()
will similarly wait for DeadlineExceeded
to complete. I don't think it does. It would be tempting to use a lock in Dispose and DeadlineExceeded to protect against ODE's, but you risk deadlocking if you're not extremely careful.
After a lot of effort trying to properly dispose the CTS backing the RequestAborted token without ODEs or deadlocking, @jkotalik and I found the best way to avoid deadlocking is to simply not even try disposing CTS that's already been canceled. dotnet/aspnetcore#9333
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 don't think you need to do anything as complicated as what we did for the RequestAborted token since you don't try to lazily allocate the CTS nor do you expose the CancellationToken to the end user.
I do think that using a CTS instead of a Timer for DeadlineExceeded might help you avoid an ODE in DeadlineExceeded though.
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.
Dispose, cancellation and error handling is immature at the moment.
Dispose can happen 3 ways:
- Response headers are invalid (bad HttpStatusCode or bad content type)
- Response is received and we finish reading the body
- User explicitly calls dispose on the public API, which gets routed here
Then there are additional ways _callCts can be canceled:
- The deadline could be exceeded
- The user's passed in cancellation token is cancelled
There definitely needs to be some changes here to avoid cancelling an already disposed _callCts.
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.
Implementation looks good, going to look through the tests before signing off.
return new RpcException(new Status(statusCode, string.Empty)); | ||
} | ||
|
||
public void FinishResponse() |
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.
Is this threadsafe?
Merging so this can be setup in the interop tests. Still work to do in follow up PRs to get thread-safety perfect but that can be handled in follow up work. Thanks for everyone's feedback. |
Addresses #43
To do:
Validating content typeDeadline (client timeout)Deadline (copying C-core HTTP request header formatting)CallOptions.CancellationTokenFuture: