grpc: implement new proto generated code API#2549
Conversation
Generated Protobuf APIThe primary goal of the gRPC library is to provide a natural and easy-to-use API for developers while also providing the highest possible performance through zero-cost abstractions, zero-copy design patterns, support for arenas, etc. The following API is designed to be ergonomic, using all native Design Goals
Target APIThe following shows the generated API for the four basic call types, simplified somewhat to allow the basic structure to be understood. pub fn unary(&self, req: impl AsView<Proxied = MyRequest>) -> UnaryCallBuilder<>
pub fn bidi(&self) -> BidiCallBuilder<>
pub fn server_stream(&self, req: impl AsView<Proxied = MyRequest>) -> ServerStreamBuilder<>
pub fn client_stream(&self) -> ClientStreamBuilder<>The Builder types implement * - Note that we will not initially be using Usage ExamplesBelow are examples of how application code will look when using our generated API. Inferred types are shown for completeness. Unary Calls// Simple usage with owned types
let res: Result<MyResponse, Status> =
client.unary_call(proto!(MyRequest{ query: 1 })).await;// Advanced usage: Arena-allocated request and response.
let mut request_view: MyRequestView<'_> = proto!(MyRequest{ }).as_view();
let mut response_view: MyResponseMut<'_> = MyResponse::new(); // Or obtain from an arena
let status: Status =
client.unary_call(request_view)
.with_timeout(Duration::from_secs(2))
.with_response_message(&mut response_view)
.await;Bidi Streaminglet (tx: GrpcStreamingRequest<MyRequest, _>, rx: GrpcStreamingResponse<MyResponse, _>) =
client.bidi_call(request_stream, response_sink).await;
tx.send_message(proto!(MyRequest { query: 1 })).await;
while let Some(msg) = rx.next().await {
println!("received response message {msg:?}");
}
let status: Status = rx.status().await; // note: consumes rx |
There was a problem hiding this comment.
IIUC, we're dropping support for generating tonic code. There may be some people using this plugin with Tonic in OSS. Do we want to keep Tonic code generation and add gRPC support with a flag to switch between the two?
There was a problem hiding this comment.
I was thinking this would be unlikely because we never published a crate with it?
I can add it back if you think it's necessary, but I was thinking that we'd eventually have:
- tonic + prost codegen (existing)
- grpc + google-protobuf codegen (this PR)
- tonic + prost codegen that wraps a grpc channel / server instead of tonic.
Where (3) is the migration path for users that are currently using tonic+prost but want the new channel implementation ASAP, and both (1) and (3) are left at v0 and maintained as best-effort while (2) is released in our 1.0.
There was a problem hiding this comment.
I'm aware of a few issues raised with the protobuf team by users of Tonic's protobuf codegen.
I don't have enough context on the current state of prost or the best path forward for existing Tonic users. Perhaps @LucioFranco can weigh in on whether we can safely drop support for Tonic + Google protobuf.
If we do decide to drop it, we should delete the tonic-protobuf crate as well.
|
cc @gu0keno0 @YutaoMa @ankurmittal as FYI - feel free to review if you have time, or just skim the design & look at the usage examples and the actual code in |
There was a problem hiding this comment.
I'm aware of a few issues raised with the protobuf team by users of Tonic's protobuf codegen.
I don't have enough context on the current state of prost or the best path forward for existing Tonic users. Perhaps @LucioFranco can weigh in on whether we can safely drop support for Tonic + Google protobuf.
If we do decide to drop it, we should delete the tonic-protobuf crate as well.
dfawley
left a comment
There was a problem hiding this comment.
Thanks for the review; all comments addressed
Aaaand immediately after hitting send, I realized I didn't add all of the copyrights. It should be good now though. |
|
@arjan-bal I updated to follow the sealed pattern from https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/#allowing-only-some-methods-to-be-called since the hidden trait doesn't prevent usage of methods. |
arjan-bal
left a comment
There was a problem hiding this comment.
Some minor comments, otherwise LGTM.
|
I had to do a little work to make the interop tests work -- they needed to override the authority, so I plumbed that around. And I couldn't see the error that would have let me fix it quickly because PF wasn't setting a failing picker (I did that in a later PR, too), so I added that in here too. PTAL |
| let runtime = self.runtime.clone(); | ||
| // TODO: Implement Drop that cancels this task. | ||
| self.runtime.spawn(Box::pin(async move { | ||
| runtime.sleep(Duration::from_millis(200)).await; |
There was a problem hiding this comment.
This call to sleep seems to be some kind of a hack. Maybe we should remove it?
There was a problem hiding this comment.
I'd rather not as part of this change. This is a hacky PF policy -- the real one is in #2340.
| sc: self.subchannel.as_ref().unwrap().clone(), | ||
| }), | ||
| }); | ||
| } else if state.connectivity_state == ConnectivityState::TransientFailure { |
There was a problem hiding this comment.
Can you please add a test for this change?
There was a problem hiding this comment.
There are currently no tests at all for this hack of the PF policy. I could add them but it would be short-lived and testing a known very-incomplete policy. Let's just keep limping like this until #2340 (or some form of it) has landed.
- Rename tonic-protobuf-build to grpc-protobuf-build - Change protoc-gen-rust-grpc to produce the new generated code API - Create grpc-protobuf crate for holding shared proto implementation - Fix handling of trailers in tonic transport implementation - Add test_util module for sharing interceptor testing code - Update interop's client_protobuf to use the new API
| self.rebuild(|c| c.with_interceptor(interceptor), Internal) | ||
| } | ||
|
|
||
| /// Attaches `interceptor` to the call. |
There was a problem hiding this comment.
Nit: same rustdoc as the above
There was a problem hiding this comment.
It's still accurate, despite being a copy. :)
I updated the comment to specify one is a multi-use interceptor and one is a single-use interceptor.
| use crate::client::Internal; | ||
|
|
||
| /// Configures a unary call for gRPC Protobuf. Implements `IntoFuture` which | ||
| /// perfors the call and resolves to the response as a `Result<Res, Status>`. |
And: