Skip to content

Conversation

@glbrntt
Copy link
Collaborator

@glbrntt glbrntt commented Nov 3, 2023

Motivation:

The ClientRPCExecutor currently ignores retry and hedging policies. This change adds support for retries.

Modifications:

  • Add a retry executor and wire it up to the client rpc executor
  • Add a few missing state transitions to the broadcasts sequence

Result:

RPC can be retried under certain conditions

Motivation:

The `ClientRPCExecutor` currently ignores retry and hedging policies.
This change adds support for retries.

Modifications:

- Add a retry executor and wire it up to the client rpc executor
- Add a few missing state transitions to the broadcasts sequence

Result:

RPC can be retried under certain conditions
@glbrntt glbrntt added the version/v2 Relates to v2 label Nov 3, 2023
@glbrntt glbrntt requested review from FranzBusch and gjcairo November 3, 2023 15:28
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.

LGTM - I think the comments make the logic pretty straightforward to follow. I wonder if it would make sense to break each task up in small methods to make it a bit more palatable, but I don't have strong opinions to be honest.

method: MethodDescriptor,
responseHandler: @Sendable @escaping (ClientResponse.Stream<Output>) async throws -> R
) async throws -> R {
// There's quite a lot going on here...
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for all the comments on this method, it makes things a lot easier to follow.

_ id: _BroadcastSequenceStateMachine<Element>.Subscriptions.ID
) -> OnCancelSubscription {
let (removed, continuation) = self.subscriptions.removeSubscriber(withID: id)
assert(removed)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are we removing this because it would sometimes fail or just because it seemed unnecessary to assert it?

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.

Overall looks good to me. Just two minor comments

if let timeout = self.timeout {
group.addTask {
let result = await Result {
try await Task.sleep(for: timeout, clock: .continuous)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thinking about iOS use-cases here this might not be what people expect. If you execute a request and the user backgrounds your app you might still be interested in the result later but it might have timedout on the continuous clocked whereas it would still be okay on the suspending one.
It all depends on how the underlying transport handles this of course

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In gRPC timeouts are propagated to the server (we don't do that here, yet) which will also cancel the RPC so I'm not it makes sense to make the change. We also can't do background networking anyway so if you background the app your RPC is toast anyway.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We also can't do background networking anyway so if you background the app your RPC is toast anyway.

Is that really true? If you were to build a transport on top of URLSession one could do background networking with the right entitlements.

let result = await Result {
// Check for cancellation; the RPC may have timed out in which case we should skip
// the response handler.
try Task.checkCancellation()
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems a bit odd to me. Why do we have to explicitly check here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Because nothing above is throwing: in the case of cancellation we will have a response of some kind (it may be failed already, and if it is failed the failure is constrained to RPCError). This is our last chance to jump into a CancellationError before reaching into user code.

@glbrntt glbrntt merged commit 017dc09 into grpc:main Nov 8, 2023
@glbrntt glbrntt deleted the gb-client-rpc-executor-retries branch November 8, 2023 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

version/v2 Relates to v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants