Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GitHub

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

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:

  1. The caller encodes the entire request payload using bitcode serialization
  2. The request is transmitted as a complete unit to the server
  3. The server processes the request and generates a complete response
  4. The response is transmitted back as a complete unit
  5. 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

ConditionField UsedTransmission Method
encoded_args.len() < DEFAULT_SERVICE_MAX_CHUNK_SIZErpc_param_bytesInline in initial header frame
encoded_args.len() >= DEFAULT_SERVICE_MAX_CHUNK_SIZErpc_prebuffered_payload_bytesChunked 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 RpcDispatcher automatically 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&lt;u8&gt;\nencoded_args"]
SizeCheck{"encoded_args.len() &gt;=\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&lt;(encoder, InnerResult), RpcServiceError&gt;"]
InnerResult["Result&lt;Self::Output, RpcServiceError&gt;"]
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 TypeCauseExample
RpcServiceError::TransportNetwork failure, framing error, decode failureConnection closed, malformed frame
RpcServiceError::RpcRemote handler returned error“item does not exist”, “Addition failed”
RpcServiceError::NotConnectedClient not connected when call initiatedWebSocket 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

MethodPurposeReturn Type
METHOD_IDCompile-time constant identifying the methodu64
encode_request()Serialize input parametersResult<Vec<u8>, io::Error>
decode_request()Deserialize input parametersResult<Self::Input, io::Error>
encode_response()Serialize output resultResult<Vec<u8>, io::Error>
decode_response()Deserialize output resultResult<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

AspectPrebuffered CallsStreaming Calls
Memory UsageEntire payload in memoryIncremental processing
LatencyHigher (wait for complete payload)Lower (process as data arrives)
ComplexitySimple request/responseRequires channel management
Use CasesSmall to medium payloads, simple operationsLarge datasets, incremental results, progress tracking
Request Fieldis_finalized: trueis_finalized: false initially
Response HandlingSingle accumulated bufferStream 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 RpcServer from muxio-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