Skip to content

Connection driven rpc backpressure - #15

Merged
kvc0 merged 10 commits into
kvc0:mainfrom
PDXKimani:connection-driven-rpc-backpressure
Jul 15, 2026
Merged

Connection driven rpc backpressure#15
kvc0 merged 10 commits into
kvc0:mainfrom
PDXKimani:connection-driven-rpc-backpressure

Conversation

@PDXKimani

@PDXKimani PDXKimani commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

BREAKING: notable semantic/API change to protosocket and protosocket-rpc API traits.

The main motivation of this PR is to reintroduce backpressure between a server's connection outbound write buffer and RPC processing, which was lost in the v1 refactor.

Essentially, this moves the "connection service polls a FuturesUnordered via poll" implementation that all server implementers I'm aware of were doing into the connection service itself. This is important, because it allows the connection service to conditionally advance the request futures only when there's somewhere for the responses to actually go: the serialization queue must have room, which is itself backpressured by the connection's write buffer.

RPC Server implementers now just return a future which will yield a response for each unary/streaming request, rather than being responsible for bookkeeping the production and return of responses themselves.

As before, they can always opt to decouple request processing from the connection via spawning.

Overall, I believe this should lighten the work for implementers and internalize concerns like message-id bookkeeping, as bonuses.

The underlying machinery is also available to any raw protosocket implementers who want to engage with backpressure.

@kvc0 kvc0 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the improvement! I have a few notes to consider

Comment thread example-proto-tls/src/server.rs
Comment thread protosocket-connection/src/connection.rs Outdated
Comment thread protosocket-connection/src/connection.rs Outdated
Comment thread protosocket-connection/src/connection.rs Outdated
Comment thread protosocket-connection/src/message_reactor.rs Outdated
Comment thread protosocket-rpc/src/server/rpc_submitter.rs Outdated
Comment thread protosocket-rpc/src/server/rpc_submitter.rs Outdated
Comment thread protosocket-rpc/src/server/rpc_submitter.rs Outdated
Comment thread protosocket-rpc/src/server/rpc_submitter.rs
Comment thread protosocket-rpc/src/server/rpc_submitter.rs Outdated
@PDXKimani
PDXKimani requested a review from kvc0 July 15, 2026 18:05
@PDXKimani

PDXKimani commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

I ended up adding Codec to MessageReactor so that the SendBudget could reference the type. My rationale there is that I can't see any practical reason you'd ever be using a MessageReactor in a context where there's not an associated Codec - Connection required its reactors to be associated with its Codec, we're just moving that inward a bit.

Then downstream that resulted in moving Codec from SocketService down to ConnectionService to make the generics less messy - again, I think ConnectionService was already de-facto associated with a codec and I'm just making that relationship explicit.

That adds a little bit to the surface of the breaking API changes here, but I think it's manageable based on the example crates and in service of what I'm trying to do here.

@kvc0 kvc0 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This looks great to me. I have a request, since it's a breaking change in the same neighborhood.


/// Create a new message codec for a connection.
fn codec(&self) -> Self::Codec;
fn codec(&self) -> <Self::ConnectionService as ConnectionService>::Codec;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

since we're doing a breaking change, let's give implementors more control: Now is a good time to make codec() and new_stream_service() take &mut self. &mut is available at the framework consumer sites, and giving &mut to users allows them more synchronization-free options.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done.

Comment on lines +165 to +169
produced += 1;
}
// Then poll ready RPCs until we either run out of room to send responses or run out of rpcs.
while let Some(permit) = outbound.reserve() {
match pin!(&mut me.rpcs).poll_next(context) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

this turned out nice.

@PDXKimani
PDXKimani requested a review from kvc0 July 15, 2026 23:15

@kvc0 kvc0 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

thank you!

@kvc0
kvc0 merged commit 2e27b4b into kvc0:main Jul 15, 2026
@PDXKimani
PDXKimani deleted the connection-driven-rpc-backpressure branch July 16, 2026 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants