-
Notifications
You must be signed in to change notification settings - Fork 420
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
Basic client RPC executor #1693
Conversation
Motivation: Clients can execute RPCs in a few different ways: straight up as a single attempt, with retries, or with hedging. This is done by the `ClientRPCExecutor`, the bones of which are added in this PR. At a high level, the executor takes a transport, request, serializer, deserializer and response handler and executes the request against a transport and executes the response handler if a response is received or synthesized locally. The executor only returns once the response has been handled. Modifications: - Add the `ClientRPCExecutor` and the one-shot implementation (hedging and retries will follow later) - The `ClientRPCExecutor` uses a `ClientStreamExecutor` which deals in serialized streams - Add a testing harness, which includes type erased transports (and a basic in process transport for testing) and different server behaviours. Result: Can execute one-shot requests.
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.
Looking very good! Left some small comments
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) | ||
extension ClientRPCExecutor.OneShotExecutor { | ||
@inlinable | ||
func execute<R>( |
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.
Nit: I believe we usually try to avoid using single letters for generic types.
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.
Hmm, the obvious name is "Result" but that becomes ambiguous in these instances with Swift.Result
. Any naming suggestions?
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.
Yeah Result
would probably be confusing. Is Response
equally bad?
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.
Yeah that's equally bad because it's not the response, it's whatever the user returns as a result of handling the response. It doesn't necessarily have to be related to the response at all. I think R
is fine given that this is internal, it isn't ambiguous with anything else and we can always change it when we come up with a better name.
switch result { | ||
case .streamExecutorCompleted(.success): | ||
// Stream finished; wait for the response to be handled. | ||
() |
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.
Style question: do we prefer/is there any difference/advantage to ()
versus continue
or break
in this case?
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 guess the difference is that ()
is "do nothing" whereas break
and continue
alter the flow control.
Using ()
if there was other code in the while loop beyond the switch then we'd go to that. I think break
would do the same (but I'd always doubt myself and wonder if it would break the while loop instead). continue
would skip any code after the switch so there are slight differences. I'd say ()
and break
are safer in that regard. I'd only reach for continue
if there was code afterwards that I explicitly wanted to skip.
I don't think we have a preferred style, but the style in NIO was always to use ()
over break
so I write it without thinking now.
Sources/GRPCCore/Call/Client/Internal/ClientRPCExecutor+OneShotExecutor.swift
Outdated
Show resolved
Hide resolved
Sources/GRPCCore/Streaming/Internal/RPCAsyncSequence+Buffered.swift
Outdated
Show resolved
Hide resolved
Sources/GRPCCore/Call/Client/Internal/ClientResponse+Convenience.swift
Outdated
Show resolved
Hide resolved
Sources/GRPCCore/Call/Client/Internal/ClientStreamExecutor.swift
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.
LGTM!
Motivation:
Clients can execute RPCs in a few different ways: straight up as a single attempt, with retries, or with hedging. This is done by the
ClientRPCExecutor
, the bones of which are added in this PR.At a high level, the executor takes a transport, request, serializer, deserializer and response handler and executes the request against a transport and executes the response handler if a response is received or synthesized locally. The executor only returns once the response has been handled.
Modifications:
ClientRPCExecutor
and the one-shot implementation (hedging and retries will follow later)ClientRPCExecutor
uses aClientStreamExecutor
which deals in serialized streamsResult:
Can execute one-shot requests.