Add codec layer for server side handlers#2540
Open
sauravzg wants to merge 7 commits intohyperium:masterfrom
Open
Add codec layer for server side handlers#2540sauravzg wants to merge 7 commits intohyperium:masterfrom
sauravzg wants to merge 7 commits intohyperium:masterfrom
Conversation
These traits are supposed to provide abstractions over standard references and protobuf views and muts to eliminate hard dependency on protobuf The biggest challenge in the message module is to solve the long-standing difficulty of reborrowing mutable message views. The Problem: Reborrowing GATs When working with Generic Associated Types (GATs) for mutable views (e.g., type Mut<'a>), we frequently need to "reborrow" a view to pass it to a child function without consuming the original. However, traditional approaches fail here: 1. Foreign Types: We cannot add a .reborrow() method directly to protobuf::Mut as it is a foreign type. 2. HRTB Hell: Defining a separate Reborrow trait and requiring for<'a> T::Mut<'a>: Reborrow leads to complex Higher-Ranked Trait Bound errors (e.g., "implementation is not general enough") that are extremely difficult to satisfy in Rust's current type system. The Solution: Static Operator Pattern We bypass these issues by moving the reborrowing logic into the provider trait itself as a static operator. * Introduced AsMut::reborrow_view<'a, 'b>(view: &'b mut Self::Mut<'a>) -> Self::Mut<'b>. * This bundles the reborrowing logic with the type definition, avoiding the need for extra traits or complex HRTB bounds on the view type itself. Changes: * as_mut.rs: Defined AsMut with the reborrow_view static operator. Implemented it for protobuf::Message and added tests verifying that views can be reborrowed and mutated without consuming the original. * as_view.rs: Defined AsView for immutable views.
This introduces a push-based stream API (`PushStream`) for gRPC server responses, modeled after internal C++ patterns to address limitations in the current pull-based model. Key architectural decisions: * **Push vs. Pull:** Standard streams yield `Result<Message, Error>`, but gRPC strictly requires messages followed by a terminal error. A unified async execution context also prevents partial consumption bugs and eliminates the performance overhead of constantly wrapping/unwrapping items in a `Result`. * **Generics over Dynamic Dispatch:** Generics are used instead of `dyn` APIs (like `RecvStream`) to enable nightly specialization. This provides power users with a workaround for Rust's limited native RTTI without requiring custom vtables. * **Struct Wrappers over Raw Traits:** The underlying traits are encapsulated in struct wrappers to future-proof the API. This gives the gRPC layer the flexibility to transparently manage features like task locals later without breaking public stability. Known Pitfalls: * **Stateless Consumer API:** The consumer API is completely stateless and is never explicitly made aware of the end of the stream. Consequently, there is currently no zero-cost or trivial way to implement asynchronous cleanups when the stream concludes.
Introduce the public API traits for server-side gRPC methods (`UnaryMethod`, `ClientStreamingMethod`, `ServerStreamingMethod`, `BidiStreamingMethod`). These traits are designed to be the primary interface for codegen. Currently, the API models a handler as an async function with a (requeststream, responsesink) interface. Note on design: - The public API is scoped to access request and response messages only. - Modifications to headers and trailers are intended to be handled via "interceptor" APIs, following the Java gRPC model. - Contextual information is expected to be accessed via a read-only local context. Known pitfalls: - Unary inputs are references, implying that they will not trivially work with spawns. While responses can be shallow copied due to them being mut refs, requests will have to be copied if they need to be sent to a different task.
…c API This change introduces data objects for the internal generic API. The primary objects are - RequestStream: which is essentially metadata + input stream - Lazy: It's a delayed computation struct that is responsible for holding a set of lazy transformations to be performed on a mutable ref to an object. This is needed to allow laze deserialization for cases like arena - ResponseWriter: A compile time state machine which guarantees the following potential states. "Trailer only" or "Metadata , Message* , Trailers" . This is achieved via compile time state transitions ensuring that's it's impossible to generate an incorrect order. The pain of having to implement these state transitions is simplified via extension API to to trivial maps. - MessageWrappers: Outgoing and Incoming wrappers to allow tweaking per read(input) and write options(output). - Known Pitfalls - Since, `Lazy` transformations are evaluated lazily, it's difficult to model complex transformations that need shared state without additional cost.
…r handling all types of gRPC methods on the server side, along with adapters for specific method types. Key changes: - Introduce `StreamingMethodHandler` trait to provide a common interface for method execution. - Add `UnaryMethodAdapter`, `ServerStreamingAdapter`, `ClientStreamingAdapter`, and `BidiStreamingAdapter` to bridge specific method traits to the unified handler. - This generic API has intentionally been kept private due to the pitfalls below. Known pitfalls: - Lazy and ResponseHolder are not very great abstractions to workaround the fact that we may not always have ownership of the underlying message. - Asymmetry between the API: Request is lazy while response is in a holder. - The API is very generic heavy, but this should be acceptable at least until we talk about interceptors. C++ has a similar setup with Generic handler until being type erased in the codec layer.
…the codec module. Supports Protobuf and Prost, alongside Gzip, Deflate, and Zstd compression via a global lock-free registry. The serialization API is designed to allow supporting zero copy improvements using grpc's Buf and BufMut extensions to model chain of non contiguos buffers.
…r adapter This commit introduces the `GenericByteStreamMethodHandler` trait to provide a standard interface for handling raw gRPC byte streams (`ReqB: Buf`). Additionally, it adds the `CodecMessageStreamHandler`, which bridges the gap between raw byte streams and strongly-typed gRPC messages. It acts as an adapter that wraps an underlying `MessageStreamHandler` and implements `GenericByteStreamMethodHandler`, transparently managing the encoding and decoding of message streams using standard Codecs. Finally, the `method_handler` module is updated to export these new components and introduces a `CodecRespB` type alias for the default codec response body type.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implement Core Serialization Infrastructure and Generic Byte Stream Handlers
Description
This sits on top of #2539 and only the last two commits should be in scope of review.
This pull request establishes the core serialization and compression infrastructure within the
codecmodule and introduces the generic byte stream handler abstractions required to seamlessly bridge raw network buffers with strongly-typed message handlers.Key Architectural Additions
BufandBufMutextensions. This API is explicitly designed to support and optimize zero-copy operations over chains of non-contiguous memory buffers.GenericByteStreamMethodHandlerTrait: Defines a standardized interface for server-side processing of raw gRPC byte streams (ReqB: Buf), creating a distinct abstraction layer for transport-level buffering.CodecMessageStreamHandlerAdapter: Introduces an adapter that implementsGenericByteStreamMethodHandlerwhile wrapping an underlying, strongly-typedMessageStreamHandler. This component bridges the transport and application layers by transparently managing the encoding and decoding of message streams utilizing the registered codecs.Known gaps
BytesMuttovec[BytesMut]enumto avoid dynamic dispatch which can be eliminated but needs codec API modifications or additional trait constraints on the ByteStreamhandler