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.

Service Definitions

Relevant source files

Purpose and Scope

This document explains how RPC service definitions are created and shared in the rust-muxio system. Service definitions are the contracts that define RPC methods, their inputs, outputs, and unique identifiers. By implementing the RpcMethodPrebuffered trait, both clients and servers depend on the same type-safe API contract, eliminating an entire class of distributed system bugs through compile-time verification.

For information about how clients invoke these definitions, see Service Caller Interface. For information about how servers handle these definitions, see Service Endpoint Interface.

Sources: README.md:16-50


The RpcMethodPrebuffered Trait

The RpcMethodPrebuffered trait is the foundational abstraction for defining RPC methods in muxio. Each RPC method is a type that implements this trait, specifying the method's unique identifier, input type, output type, and serialization logic.

classDiagram
    class RpcMethodPrebuffered {
        <<trait>>
        +METHOD_ID: u64\n+Input: type\n+Output: type
        +encode_request(input: Input) Result~Vec~u8~, io::Error~
        +decode_request(bytes: &[u8]) Result~Input, io::Error~
        +encode_response(output: Output) Result~Vec~u8~, io::Error~
        +decode_response(bytes: &[u8]) Result~Output, io::Error~
    }
    
    class Add {+METHOD_ID: u64\n+Input: Vec~f64~\n+Output: f64}
    
    class Mult {+METHOD_ID: u64\n+Input: Vec~f64~\n+Output: f64}
    
    class Echo {+METHOD_ID: u64\n+Input: Vec~u8~\n+Output: Vec~u8~}
    
    RpcMethodPrebuffered <|.. Add
    RpcMethodPrebuffered <|.. Mult
    RpcMethodPrebuffered <|.. Echo

Trait Structure

Each concrete implementation (like Add, Mult, Echo) must provide:

ComponentTypePurpose
METHOD_IDu64Compile-time generated unique identifier for the method
InputAssociated typeThe Rust type representing the method's parameters
OutputAssociated typeThe Rust type representing the method's return value
encode_requestFunctionSerializes Input to Vec<u8>
decode_requestFunctionDeserializes Vec<u8> to Input
encode_responseFunctionSerializes Output to Vec<u8>
decode_responseFunctionDeserializes Vec<u8> to Output

Sources: README.md49 extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:1-28


Method ID Generation

Each RPC method is assigned a unique METHOD_ID at compile time. This identifier is generated by hashing the method's name using the xxhash-rust library. The hash-based approach ensures that method IDs are deterministic, collision-resistant, and do not require manual coordination.

graph LR
    MethodName["Method Name\n(e.g., 'Add')"]
Hash["xxhash-rust\nHash Function"]
MethodID["METHOD_ID\n(u64 constant)"]
CompileTime["Compile Time\nConstant"]
MethodName --> Hash
 
   Hash --> MethodID
 
   MethodID --> CompileTime
    
    CompileTime -.enforces.-> UniqueID["Unique Identifier\nper Method"]
CompileTime -.enables.-> TypeSafety["Compile-time\nCollision Detection"]

Hash-Based Identification

The METHOD_ID is computed once at compile time and stored as a constant. This approach provides several benefits:

  1. Deterministic : The same method name always produces the same ID across all builds
  2. Collision-Resistant : The 64-bit hash space makes accidental collisions extremely unlikely
  3. Zero Runtime Overhead : No string comparisons or lookups needed during RPC dispatch
  4. Type-Safe : Method IDs are baked into the type system, preventing runtime mismatches

When a client makes an RPC call using Add::call(), it automatically includes Add::METHOD_ID in the request. When the server receives this request, it uses the method ID to route to the correct handler.

Sources: README.md49 extensions/muxio-rpc-service/Cargo.toml16 extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:35-36


Encoding and Decoding

Service definitions are responsible for converting between typed Rust structures and binary representations. The trait defines four serialization methods that operate on raw bytes:

graph LR
    subgraph "Client Side"
        Input1["Input\n(Typed Struct)"]
EncReq["encode_request()"]
ReqBytes["Request Bytes\n(Vec&lt;u8&gt;)"]
end
    
    subgraph "Transport"
        Network["Binary\nNetwork Frames"]
end
    
    subgraph "Server Side"
        ReqBytes2["Request Bytes\n(Vec&lt;u8&gt;)"]
DecReq["decode_request()"]
Input2["Input\n(Typed Struct)"]
end
    
 
   Input1 --> EncReq
 
   EncReq --> ReqBytes
 
   ReqBytes --> Network
 
   Network --> ReqBytes2
 
   ReqBytes2 --> DecReq
 
   DecReq --> Input2

Request Serialization Flow

Response Serialization Flow

Serialization Implementation

While service definitions can use any serialization format, the system commonly uses the bitcode library for efficient binary serialization. This provides:

  • Compact binary representation (smaller than JSON or MessagePack)
  • Fast encoding/decoding performance
  • Native Rust type support without manual schema definitions

The separation of serialization logic into the service definition ensures that both client and server use identical encoding/decoding logic, preventing deserialization mismatches.

Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:55-76 extensions/muxio-rpc-service/Cargo.toml17


graph TB
    subgraph "Shared Service Definition Crate"
        AddDef["Add Method\nimpl RpcMethodPrebuffered"]
MultDef["Mult Method\nimpl RpcMethodPrebuffered"]
EchoDef["Echo Method\nimpl RpcMethodPrebuffered"]
end
    
    subgraph "Client Implementation"
        TokioClient["muxio-tokio-rpc-client"]
WasmClient["muxio-wasm-rpc-client"]
AddCall["Add::call()"]
MultCall["Mult::call()"]
end
    
    subgraph "Server Implementation"
        TokioServer["muxio-tokio-rpc-server"]
AddHandler["Add Handler\nregister_prebuffered()"]
MultHandler["Mult Handler\nregister_prebuffered()"]
end
    
    AddDef -.depends on.-> TokioClient
    AddDef -.depends on.-> WasmClient
    AddDef -.depends on.-> TokioServer
    
    MultDef -.depends on.-> TokioClient
    MultDef -.depends on.-> WasmClient
    MultDef -.depends on.-> TokioServer
    
 
   AddDef --> AddCall
 
   AddDef --> AddHandler
 
   MultDef --> MultCall
 
   MultDef --> MultHandler

Shared Definitions Pattern

The key architectural principle is that service definitions are placed in a shared crate that both client and server implementations depend on. This shared dependency enforces API contracts at compile time.

Example Usage Pattern

The integration tests demonstrate this pattern in practice:

Client-side invocation:

Server-side handler registration:

Both client and server use:

  • Add::METHOD_ID for routing
  • Add::decode_request() for parsing inputs
  • Add::encode_response() for formatting outputs

Sources: README.md49 extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:1-96 extensions/muxio-wasm-rpc-client/tests/prebuffered_integration_tests.rs:21-142


Type Safety Guarantees

Service definitions provide compile-time guarantees that prevent an entire class of distributed system bugs. Any mismatch between client and server results in a compilation error rather than a runtime failure.

Compile-Time Error Scenarios

ScenarioError TypeDetection Point
Client and server use different Input typesType mismatchCompile time
Client and server use different Output typesType mismatchCompile time
Adding/removing fields from request/responseDeserialization errorCompile time (via shared definition)
Using wrong method ID for handlerMethod not foundCompile time (via shared constant)
Duplicate method namesHash collisionCompile time (deterministic hashing)

Example: Preventing Type Mismatches

If a developer attempts to change the Add method's input type on the server without updating the shared definition:

  1. The server code references Add::decode_request() from the shared definition
  2. The server handler expects the original Vec<f64> type
  3. Any type mismatch produces a compile error
  4. The server cannot be built until the shared definition is updated
  5. Once updated, all clients must also be rebuilt, automatically receiving the new type

This eliminates scenarios where:

  • A client sends the wrong data format
  • A server expects a different response structure
  • Method IDs collide between different methods
  • Serialization logic differs between client and server

Sources: README.md49 extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:82-95


graph TB
    App["Application Code"]
ServiceDef["Service Definition\n(RpcMethodPrebuffered)"]
CallTrait["RpcCallPrebuffered Trait"]
CallerInterface["RpcServiceCallerInterface"]
Transport["Transport\n(Tokio/WASM)"]
App -->|Add::call client, input| CallTrait
 
   CallTrait -->|uses| ServiceDef
 
   CallTrait -->|encode_request| ServiceDef
 
   CallTrait -->|decode_response| ServiceDef
 
   CallTrait -->|METHOD_ID| ServiceDef
 
   CallTrait -->|call_rpc_buffered| CallerInterface
 
   CallerInterface --> Transport

Integration with RPC Framework

Service definitions integrate with the broader RPC framework through the RpcCallPrebuffered trait, which provides the high-level call() method that applications use.

graph TB
    subgraph "Trait Definition"
        RpcCallPrebuffered["RpcCallPrebuffered\n(trait)"]
call_method["call()\n(async method)"]
end
    
    subgraph "Trait Bounds"
        RpcMethodPrebuffered["RpcMethodPrebuffered\n(provides encode/decode)"]
Send["Send + Sync\n(thread-safe)"]
sized["Sized\n(known size)"]
end
    
    subgraph "Blanket Implementation"
        blanket["impl&lt;T&gt; RpcCallPrebuffered for T\nwhere T: RpcMethodPrebuffered"]
end
    
    subgraph "Example Types"
        Add["Add\n(example service)"]
Mult["Mult\n(example service)"]
Echo["Echo\n(example service)"]
end
    
 
   RpcCallPrebuffered --> call_method
 
   blanket --> RpcCallPrebuffered
 
   RpcMethodPrebuffered --> blanket
 
   Send --> blanket
 
   sized --> blanket
    
    Add -.implements.-> RpcMethodPrebuffered
    Mult -.implements.-> RpcMethodPrebuffered
    Echo -.implements.-> RpcMethodPrebuffered
    
    Add -.gets.-> RpcCallPrebuffered
    Mult -.gets.-> RpcCallPrebuffered
    Echo -.gets.-> RpcCallPrebuffered

The RpcCallPrebuffered trait is automatically implemented for any type that implements RpcMethodPrebuffered. This blanket implementation:

  1. Encodes the input using encode_request()
  2. Creates an RpcRequest with the encoded bytes and METHOD_ID
  3. Handles large argument payloads by routing them to the prebuffered payload field when needed
  4. Invokes the transport-specific caller interface
  5. Decodes the response using decode_response()
  6. Returns the typed result to the application

Sources: extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:10-98 README.md:100-118


graph TB
    subgraph "example-muxio-rpc-service-definition"
        Add["Add\nInput: Vec&lt;f64&gt;\nOutput: f64\nMETHOD_ID"]
Mult["Mult\nInput: Vec&lt;f64&gt;\nOutput: f64\nMETHOD_ID"]
Echo["Echo\nInput: Vec&lt;u8&gt;\nOutput: Vec&lt;u8&gt;\nMETHOD_ID"]
end
    
    subgraph "Client Crates"
        TokioClient["muxio-tokio-rpc-client"]
WasmClient["muxio-wasm-rpc-client"]
end
    
    subgraph "Server Crate"
        TokioServer["muxio-tokio-rpc-server"]
end
    
    subgraph "Example Application"
        WsApp["example-muxio-ws-rpc-app"]
end
    
 
   Add --> TokioClient
 
   Add --> WasmClient
 
   Add --> TokioServer
 
   Add --> WsApp
    
 
   Mult --> TokioClient
 
   Mult --> WasmClient
 
   Mult --> TokioServer
 
   Mult --> WsApp
    
 
   Echo --> TokioClient
 
   Echo --> WasmClient
 
   Echo --> TokioServer
 
   Echo --> WsApp

Example Service Definition Crate

The example-muxio-rpc-service-definition crate demonstrates the shared definition pattern:

This crate exports the method definitions that are used across:

  • Native Tokio client tests
  • WASM client tests
  • Server implementations
  • Example applications

All consumers share the exact same type definitions, method IDs, and serialization logic.

Sources: extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:1-6 extensions/muxio-wasm-rpc-client/tests/prebuffered_integration_tests.rs:21-27 README.md:70-73

Dismiss

Refresh this wiki

Enter email to refresh