This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Prebuffered RPC Calls
Loading…
Prebuffered RPC Calls
Relevant source files
- extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs
- extensions/muxio-rpc-service-caller/tests/prebuffered_caller_tests.rs
- extensions/muxio-wasm-rpc-client/tests/prebuffered_integration_tests.rs
Purpose and Scope
This document describes the prebuffered RPC call pattern in muxio, where the entire request payload is encoded and buffered before transmission, and the entire response payload is accumulated before being decoded and returned to the caller. This is in contrast to streaming RPC calls, which process data incrementally using channels (see Streaming RPC Calls).
Prebuffered calls are the simplest and most common RPC pattern, suitable for request/response operations where the entire input and output fit comfortably in memory. For service definition details, see Service Definitions. For caller interface abstractions, see Service Caller Interface.
Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:10-21
Overview
A prebuffered RPC call is a synchronous-style remote procedure invocation where:
- The caller encodes the entire request payload using
bitcodeserialization - The request is transmitted as a complete unit to the server
- The server processes the request and generates a complete response
- The response is transmitted back as a complete unit
- The caller decodes the response and returns the result
The key characteristic is that both request and response are treated as atomic, indivisible units from the application’s perspective, even though the underlying transport may chunk them into multiple frames for transmission.
Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:30-48
The RpcCallPrebuffered Trait
The RpcCallPrebuffered trait defines the interface for making prebuffered RPC calls. It is automatically implemented for any type that implements RpcMethodPrebuffered from the muxio-rpc-service crate.
Trait Definition
The trait is generic over any client type C that implements RpcServiceCallerInterface, making it runtime-agnostic and usable with both native Tokio clients and WASM clients.
Automatic Implementation
The blanket implementation at extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:24-98 automatically provides the call method for any service that defines request/response types via the RpcMethodPrebuffered trait.
Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:10-28
Request/Response Flow
The following diagram illustrates the complete flow of a prebuffered RPC call from the caller’s perspective:
sequenceDiagram
participant App as "Application Code"
participant Trait as "RpcCallPrebuffered::call"
participant Encode as "encode_request"
participant Strategy as "Transport Strategy"
participant Client as "RpcServiceCallerInterface"
participant Buffered as "call_rpc_buffered"
participant Decode as "decode_response"
App->>Trait: Echo::call(client, input)
Trait->>Encode: Self::encode_request(input)
Encode-->>Trait: Vec<u8>
Trait->>Strategy: Check encoded_args.len()
alt "len < DEFAULT_SERVICE_MAX_CHUNK_SIZE"
Strategy->>Strategy: Use rpc_param_bytes
note over Strategy: Small payload: inline in header
else "len >= DEFAULT_SERVICE_MAX_CHUNK_SIZE"
Strategy->>Strategy: Use rpc_prebuffered_payload_bytes
note over Strategy: Large payload: stream as chunks
end
Strategy->>Trait: RpcRequest struct
Trait->>Client: call_rpc_buffered(request, decode_fn)
Client->>Client: Transmit request frames
Client->>Client: Await response frames
Client->>Client: Accumulate response bytes
Client-->>Trait: (encoder, Result<Vec<u8>, Error>)
alt "Response is Ok(bytes)"
Trait->>Decode: decode_response(bytes)
Decode-->>Trait: Self::Output
Trait-->>App: Ok(output)
else "Response is Err"
Trait-->>App: Err(RpcServiceError)
end
Prebuffered RPC Call Sequence
Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:49-97
Smart Transport Strategy for Large Payloads
The prebuffered implementation uses a “smart transport strategy” to handle arguments of any size, automatically selecting the most efficient encoding method based on payload size.
Strategy Logic
| Condition | Field Used | Transmission Method |
|---|---|---|
encoded_args.len() < DEFAULT_SERVICE_MAX_CHUNK_SIZE | rpc_param_bytes | Inline in initial header frame |
encoded_args.len() >= DEFAULT_SERVICE_MAX_CHUNK_SIZE | rpc_prebuffered_payload_bytes | Chunked and streamed after header |
RpcRequest Structure
RpcRequest {
rpc_method_id: u64,
rpc_param_bytes: Option<Vec<u8>>, // Used for small payloads
rpc_prebuffered_payload_bytes: Option<Vec<u8>>, // Used for large payloads
is_finalized: bool, // Always true for prebuffered
}
Implementation Details
The decision logic is implemented at extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:58-65:
This strategy ensures that:
- Small requests avoid unnecessary chunking overhead
- Large requests don’t fail due to header size limits (typically ~64KB)
- The underlying
RpcDispatcherautomatically handles chunking for large payloads - Server-side endpoint logic transparently locates arguments in either field
Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:30-72
graph TB
Input["Application Input\n(Self::Input)"]
Encode["encode_request()\nbitcode serialization"]
EncodedBytes["Vec<u8>\nencoded_args"]
SizeCheck{"encoded_args.len() >=\nDEFAULT_SERVICE_MAX_CHUNK_SIZE?"}
SmallPath["rpc_param_bytes:\nSome(encoded_args)"]
SmallNote["Inline in header frame\nSingle transmission"]
LargePath["rpc_prebuffered_payload_bytes:\nSome(encoded_args)"]
LargeNote["Chunked by RpcDispatcher\nMultiple frames"]
Request["RpcRequest struct\nis_finalized: true"]
Dispatcher["RpcDispatcher\nEncodes and transmits"]
Network["WebSocket Transport\nBinary frames"]
Input-->Encode
Encode-->EncodedBytes
EncodedBytes-->SizeCheck
SizeCheck-->|No| SmallPath
SmallPath-->SmallNote
SmallNote-->Request
SizeCheck-->|Yes| LargePath
LargePath-->LargeNote
LargeNote-->Request
Request-->Dispatcher
Dispatcher-->Network
Transport Strategy Data Flow
The following diagram shows how data flows through the different encoding paths:
Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:55-72 README.md:32-34
Error Propagation
Prebuffered RPC calls propagate errors through a nested Result structure to distinguish between transport errors and application-level errors.
graph TB
Call["RpcCallPrebuffered::call"]
BufferedCall["call_rpc_buffered"]
OuterResult["Result<(encoder, InnerResult), RpcServiceError>"]
InnerResult["Result<Self::Output, RpcServiceError>"]
TransportErr["Transport Error\n(Connection failed, timeout, etc.)"]
RemoteErr["Remote Service Error\n(Handler returned Err)"]
DecodeErr["Decode Error\n(Malformed response)"]
Success["Successful Response\nSelf::Output"]
Call-->BufferedCall
BufferedCall-->OuterResult
OuterResult-->|Outer Err| TransportErr
OuterResult-->|Outer Ok| InnerResult
InnerResult-->|Inner Err RpcServiceError::Rpc| RemoteErr
InnerResult-->|Inner Ok, decode fails| DecodeErr
InnerResult-->|Inner Ok, decode succeeds| Success
Error Flow Diagram
Error Handling Code
The error unwrapping logic at extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:87-96:
Error Types
| Error Type | Cause | Example |
|---|---|---|
RpcServiceError::Transport | Network failure, framing error, decode failure | Connection closed, malformed frame |
RpcServiceError::Rpc | Remote handler returned error | “item does not exist”, “Addition failed” |
RpcServiceError::NotConnected | Client not connected when call initiated | WebSocket not established |
Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:79-96 extensions/muxio-rpc-service-caller/tests/prebuffered_caller_tests.rs:135-177
Usage Example
The following test demonstrates typical usage of prebuffered RPC calls:
Basic Success Case
From extensions/muxio-rpc-service-caller/tests/prebuffered_caller_tests.rs:98-133:
Error Handling Example
From extensions/muxio-rpc-service-caller/tests/prebuffered_caller_tests.rs:179-212:
Large Payload Test
From extensions/muxio-wasm-rpc-client/tests/prebuffered_integration_tests.rs:296-312:
Sources: extensions/muxio-rpc-service-caller/tests/prebuffered_caller_tests.rs:98-212 extensions/muxio-wasm-rpc-client/tests/prebuffered_integration_tests.rs:296-312
Integration with Service Definitions
Prebuffered RPC calls rely on service definitions that implement the RpcMethodPrebuffered trait. Each service method must provide:
Required Trait Methods
| Method | Purpose | Return Type |
|---|---|---|
METHOD_ID | Compile-time constant identifying the method | u64 |
encode_request() | Serialize input parameters | Result<Vec<u8>, io::Error> |
decode_request() | Deserialize input parameters | Result<Self::Input, io::Error> |
encode_response() | Serialize output result | Result<Vec<u8>, io::Error> |
decode_response() | Deserialize output result | Result<Self::Output, io::Error> |
Example Service Usage
The RpcCallPrebuffered trait automatically implements the call method for any type implementing RpcMethodPrebuffered, providing compile-time type safety and zero-cost abstractions.
Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:1-11 extensions/muxio-wasm-rpc-client/tests/prebuffered_integration_tests.rs:21-27
Comparison with Streaming Calls
| Aspect | Prebuffered Calls | Streaming Calls |
|---|---|---|
| Memory Usage | Entire payload in memory | Incremental processing |
| Latency | Higher (wait for complete payload) | Lower (process as data arrives) |
| Complexity | Simple request/response | Requires channel management |
| Use Cases | Small to medium payloads, simple operations | Large datasets, incremental results, progress tracking |
| Request Field | is_finalized: true | is_finalized: false initially |
| Response Handling | Single accumulated buffer | Stream of chunks via channels |
When to Use Prebuffered
- Request and response fit comfortably in memory
- Simple request/response semantics
- No need for progress tracking or cancellation
- Examples: database queries, RPC calculations, file uploads < 10MB
When to Use Streaming
- Large payloads that don’t fit in memory
- Need to process results incrementally
- Progress tracking or cancellation required
- Examples: video streaming, large file transfers, real-time data feeds
For detailed information on streaming RPC patterns, see Streaming RPC Calls.
Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs71 Table of Contents (4.4 vs 4.5)
graph TB
subgraph "Application Layer"
AppCode["Application Code\nBusiness Logic"]
end
subgraph "RPC Service Layer"
ServiceDef["Service Definition\nRpcMethodPrebuffered trait\nEcho, Add, Mult"]
MethodID["METHOD_ID constant\nxxhash generated"]
EncDec["encode_request()\ndecode_response()\nbitcode serialization"]
end
subgraph "Caller Layer"
CallTrait["RpcCallPrebuffered trait\ncall()
method"]
CallerIface["RpcServiceCallerInterface\ncall_rpc_buffered()"]
end
subgraph "Dispatcher Layer"
Dispatcher["RpcDispatcher\nRequest correlation\nResponse routing"]
Request["RpcRequest struct\nmethod_id\nparam_bytes or payload_bytes\nis_finalized: true"]
end
subgraph "Transport Layer"
Session["RpcSession\nStream multiplexing\nFrame encoding"]
Framing["Binary frames\nChunking strategy"]
end
AppCode-->|Echo::call client, input| CallTrait
CallTrait-->|implements for| ServiceDef
CallTrait-->|uses| MethodID
CallTrait-->|calls| EncDec
CallTrait-->|invokes| CallerIface
CallerIface-->|creates| Request
CallerIface-->|sends to| Dispatcher
Dispatcher-->|initializes stream in| Session
Session-->|chunks and encodes| Framing
ServiceDef-.defines.->MethodID
ServiceDef-.implements.->EncDec
Component Relationships
The following diagram shows how prebuffered RPC components relate to each other:
Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:1-98 extensions/muxio-rpc-service-caller/tests/prebuffered_caller_tests.rs:1-18
Testing Strategy
The prebuffered RPC implementation includes comprehensive unit and integration tests:
Unit Tests
Located in extensions/muxio-rpc-service-caller/tests/prebuffered_caller_tests.rs:20-213:
- MockRpcClient : Test implementation of
RpcServiceCallerInterface - test_buffered_call_success : Verifies successful request/response roundtrip
- test_buffered_call_remote_error : Tests error propagation from server
- test_prebuffered_trait_converts_error : Validates error type conversion
Integration Tests
Located in extensions/muxio-wasm-rpc-client/tests/prebuffered_integration_tests.rs:1-313:
- Real server instance : Uses actual
RpcServerfrommuxio-tokio-rpc-server - WebSocket bridge : Connects WASM client to real server over network
- Large payload test : Validates chunking for payloads 200x chunk size
- Cross-platform validation : Same test logic for both Tokio and WASM clients
Test Architecture
Sources: extensions/muxio-rpc-service-caller/tests/prebuffered_caller_tests.rs:1-213 extensions/muxio-wasm-rpc-client/tests/prebuffered_integration_tests.rs:1-313