Skip to content
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

Merged
merged 13 commits into from
Nov 1, 2023
Merged

Conversation

glbrntt
Copy link
Collaborator

@glbrntt glbrntt commented Oct 30, 2023

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.

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

@gjcairo gjcairo left a 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>(
Copy link
Collaborator

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.

Copy link
Collaborator Author

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?

Copy link
Collaborator

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?

Copy link
Collaborator Author

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.
()
Copy link
Collaborator

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?

Copy link
Collaborator Author

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/Streaming/Internal/RPCWriter+Map.swift Outdated Show resolved Hide resolved
@glbrntt glbrntt requested a review from gjcairo October 31, 2023 15:56
Copy link
Collaborator

@FranzBusch FranzBusch left a comment

Choose a reason for hiding this comment

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

LGTM!

@glbrntt glbrntt enabled auto-merge (squash) November 1, 2023 12:51
@glbrntt glbrntt merged commit fe75fb1 into grpc:main Nov 1, 2023
12 of 14 checks passed
@glbrntt glbrntt deleted the gb-client-rpc-executor-one-shot branch November 1, 2023 12:58
@glbrntt glbrntt added semver/none No version change required. v2 A change for v2 labels Nov 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver/none No version change required. v2 A change for v2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants