-
Notifications
You must be signed in to change notification settings - Fork 412
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
Simplify client rpc execution #1993
Conversation
Motivation: The existing code for executing RPCs on the client is layered, the one-shot, retry and hedging executors serialize the messages of the requests they're processing into bytes. These 'raw' requests are processed by the stream executor which is structured in such a way as requiring a task group of events it needs to handled. These events are handled in a run method (which is in turn another task for the caller to run). This requires quite a bit of machinery (and allocations) for it to work. Modifications: - Push the serialization/deserialization of messages down into the stream executor. This lets a typed request be transformed directly to `RPCRequestPart`s and for `RPCResponsePart`s to be transformed directly to a typed response (rather than via an intermediary request/response typed to `[UInt8]`). - Remove the sendability requirement from the `next` function for client interceptors. This makes sense: interceptors should be straight-line code so shouldn't be shuffled off into a subtask. This unlocks the ability to remove the task group and event stream previously used by the stream executor as it can add child tasks to a provided task group (rather than needing a separate task group with an event stream). - Apply this change to the one-shot, hedging, and retry executors. Result: - ~35% reduction in allocations for unary RPCs on the client
next: @Sendable ( | ||
next: ( |
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.
you might want to adopt sending
here instead. So that you can call an interceptor on a child task if one wants to
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 I was thinking about that. I was going to go through this module separately and adopt sending
since we're still using v5 language mode and I think we need it in a few places.
@inlinable // would be private | ||
static func execute<Input: Sendable, Output: Sendable>( |
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: do we want to rename to _execute
?
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.
Ah thanks. We actually don't want it to be _execute
, it should be internal
, it's the comment that's wrong.
* Simplify client rpc execution Motivation: The existing code for executing RPCs on the client is layered, the one-shot, retry and hedging executors serialize the messages of the requests they're processing into bytes. These 'raw' requests are processed by the stream executor which is structured in such a way as requiring a task group of events it needs to handled. These events are handled in a run method (which is in turn another task for the caller to run). This requires quite a bit of machinery (and allocations) for it to work. Modifications: - Push the serialization/deserialization of messages down into the stream executor. This lets a typed request be transformed directly to `RPCRequestPart`s and for `RPCResponsePart`s to be transformed directly to a typed response (rather than via an intermediary request/response typed to `[UInt8]`). - Remove the sendability requirement from the `next` function for client interceptors. This makes sense: interceptors should be straight-line code so shouldn't be shuffled off into a subtask. This unlocks the ability to remove the task group and event stream previously used by the stream executor as it can add child tasks to a provided task group (rather than needing a separate task group with an event stream). - Apply this change to the one-shot, hedging, and retry executors. Result: - ~35% reduction in allocations for unary RPCs on the client * use task group void * fix comment
Motivation:
The existing code for executing RPCs on the client is layered, the one-shot, retry and hedging executors serialize the messages of the requests they're processing into bytes. These 'raw' requests are processed by the stream executor which is structured in such a way as requiring a task group of events it needs to handled. These events are handled in a run method (which is in turn another task for the caller to run). This requires quite a bit of machinery (and allocations) for it to work.
Modifications:
RPCRequestPart
s and forRPCResponsePart
s to be transformed directly to a typed response (rather than via an intermediary request/response typed to[UInt8]
).next
function for client interceptors. This makes sense: interceptors should be straight-line code so shouldn't be shuffled off into a subtask. This unlocks the ability to remove the task group and event stream previously used by the stream executor as it can add child tasks to a provided task group (rather than needing a separate task group with an event stream).Result: