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.

Advanced Topics

Loading…

Advanced Topics

Relevant source files

Purpose and Scope

This section covers advanced usage patterns, optimization techniques, and extension points for the muxio framework. Topics include cross-platform deployment strategies, performance tuning, custom transport implementations, and deep integration with JavaScript environments via WASM.

For basic usage patterns, see Overview. For specific platform implementations, see Platform Implementations. For service definition basics, see Service Definitions.


Cross-Platform Deployment

Deployment Architecture

The muxio framework enables a single service definition to be deployed across multiple runtime environments without code duplication. The key enabler is the runtime-agnostic core design, which separates platform-specific concerns (transport, async runtime) from business logic.

Sources : README.md:48-52 README.md:66-84 extensions structure from Cargo.toml

graph TB
    subgraph "Shared_Service_Contract"
        ServiceDef["example-muxio-rpc-service-definition\nRpcMethodPrebuffered traits\nMETHOD_ID constants\nencode_request/decode_response"]
end
    
    subgraph "Native_Tokio_Deployment"
        NativeApp["Native Application\nRust Binary"]
RpcClient["RpcClient\nArc<TokioMutex<RpcDispatcher>>\ntokio-tungstenite WebSocket"]
RpcServer["RpcServer\nAxum HTTP Server\ntokio::spawn task pool"]
NativeTransport["TCP/IP Network\nNative OS Stack"]
NativeApp -->|implements| ServiceDef
 
       NativeApp -->|uses| RpcClient
 
       NativeApp -->|serves| RpcServer
 
       RpcClient <-->|WebSocket frames| NativeTransport
 
       RpcServer <-->|WebSocket frames| NativeTransport
    end
    
    subgraph "WASM_Browser_Deployment"
        WasmApp["Web Application\nwasm-bindgen Module"]
RpcWasmClient["RpcWasmClient\nMUXIO_STATIC_RPC_CLIENT_REF\nthread_local RefCell"]
JsBridge["static_muxio_write_bytes\nJavaScript Callback\nWebSocket.send"]
BrowserTransport["Browser WebSocket API\nJavaScript Runtime"]
WasmApp -->|implements| ServiceDef
 
       WasmApp -->|uses| RpcWasmClient
 
       RpcWasmClient -->|calls| JsBridge
 
       JsBridge -->|invokes| BrowserTransport
    end
    
 
   NativeTransport <-->|Binary Protocol| BrowserTransport
    
    BuildConfig["Build Configuration\ncargo build --target x86_64\ncargo build --target wasm32"]
BuildConfig -.->|native| NativeApp
 
   BuildConfig -.->|wasm32-unknown-unknown| WasmApp

Shared Service Definition Pattern

Both native and WASM clients use the same service definition crate, ensuring compile-time type safety. The service definition declares methods via the RpcMethodPrebuffered trait, which generates:

  • METHOD_ID - A compile-time u64 constant derived from the method name via xxhash
  • encode_request / decode_request - Serialization using bitcode
  • encode_response / decode_response - Deserialization using bitcode
ComponentNative ClientWASM ClientServer
Service DefinitionShared crate dependencyShared crate dependencyShared crate dependency
Caller InterfaceRpcClient implements RpcServiceCallerInterfaceRpcWasmClient implements RpcServiceCallerInterfaceN/A
Endpoint InterfaceOptional (bidirectional)Optional (bidirectional)RpcServer exposes RpcServiceEndpointInterface
Transporttokio-tungsteniteJavaScript WebSockettokio-tungstenite via Axum
Async RuntimeTokioBrowser event loopTokio

Sources : README.md50 README.md:72-74

Build Configuration Strategy

The workspace uses Cargo features and target-specific dependencies to enable cross-platform builds:

The muxio-wasm-rpc-client crate uses conditional compilation to ensure WASM-specific dependencies like wasm-bindgen, js-sys, and web-sys are only included for WASM targets.

Sources : README.md:39-40 workspace structure from Cargo.toml


Performance Considerations

Data Flow and Chunking Strategy

The muxio framework employs a multi-layered data transformation pipeline optimized for low latency and minimal memory overhead. Understanding this pipeline is critical for performance tuning.

Sources : README.md architecture overview, bitcode usage from Cargo.lock, chunking from src/rpc/rpc_internals/rpc_session.rs

graph LR
    subgraph "Application_Layer"
        AppData["Rust Struct\nVec&lt;f64&gt; or Custom Type"]
end
    
    subgraph "Serialization_Layer"
        BitcodeEncode["bitcode::encode\nCompact Binary\n~70% smaller than JSON"]
SerializedBytes["Vec&lt;u8&gt;\nSerialized Payload"]
end
    
    subgraph "RPC_Protocol_Layer"
        RpcRequest["RpcRequest struct\nmethod_id: u64\nparams: Vec&lt;u8&gt;"]
RpcHeader["RpcHeader\nmessage_type: MessageType\nflags: HeaderFlags"]
end
    
    subgraph "Chunking_Layer"
        ChunkLogic["DEFAULT_MAX_CHUNK_SIZE\n8KB chunks\nRpcStreamEncoder"]
Chunk1["Chunk 1\n8KB"]
Chunk2["Chunk 2\n8KB"]
ChunkN["Chunk N\nRemaining"]
end
    
    subgraph "Multiplexing_Layer"
        StreamId["stream_id assignment\nRpcSession allocator"]
FrameHeader["Frame Header\n4 bytes: stream_id + flags"]
Frame1["Frame 1"]
Frame2["Frame 2"]
end
    
    subgraph "Transport_Layer"
        WsFrame["WebSocket Binary Frame"]
Network["TCP/IP Network"]
end
    
 
   AppData -->|serialize| BitcodeEncode
 
   BitcodeEncode --> SerializedBytes
 
   SerializedBytes --> RpcRequest
 
   RpcRequest --> RpcHeader
 
   RpcHeader --> ChunkLogic
 
   ChunkLogic --> Chunk1
 
   ChunkLogic --> Chunk2
 
   ChunkLogic --> ChunkN
 
   Chunk1 --> StreamId
 
   Chunk2 --> StreamId
 
   ChunkN --> StreamId
 
   StreamId --> FrameHeader
 
   FrameHeader --> Frame1
 
   FrameHeader --> Frame2
 
   Frame1 --> WsFrame
 
   Frame2 --> WsFrame
 
   WsFrame --> Network

Chunking Configuration

The default chunk size is defined in the core library and affects memory usage, latency, and throughput trade-offs:

Chunk SizeMemory ImpactLatencyThroughputUse Case
4KBLower peak memoryHigher (more frames)LowerMemory-constrained environments
8KB (default)BalancedBalancedBalancedGeneral purpose
16KBHigher peak memoryLower (fewer frames)HigherHigh-bandwidth scenarios
32KB+High peak memoryLowestHighestLarge file transfers

The chunk size is currently a compile-time constant (DEFAULT_MAX_CHUNK_SIZE). Custom transports can override this by implementing custom encoding logic in their RpcStreamEncoder implementations.

Sources : src/rpc/rpc_internals/rpc_session.rs frame chunking logic

Prebuffering vs Streaming Trade-offs

The framework supports two RPC invocation patterns, each with distinct performance characteristics:

Prebuffered Pattern (RpcMethodPrebuffered):

  • Entire request payload buffered in memory before processing
  • Entire response payload buffered before returning
  • Lower latency for small payloads (< 8KB)
  • Simpler error handling (atomic success/failure)
  • Example: README.md:102-118 handler registrations

Streaming Pattern (dynamic channels):

  • Incremental processing via bounded_channel or unbounded_channel
  • Memory usage proportional to channel buffer size
  • Enables processing before full payload arrives
  • Supports backpressure via bounded channels
  • Required for payloads > available memory
ScenarioRecommended PatternRationale
Small JSON-like data (< 8KB)PrebufferedSingle allocation, minimal overhead
Medium data (8KB - 1MB)Prebuffered or StreamingDepends on memory constraints
Large data (> 1MB)StreamingPrevents OOM, enables backpressure
Real-time data feedsStreamingContinuous processing required
File uploads/downloadsStreamingPredictable memory usage

Sources : README.md:102-118 prebuffered examples, streaming concepts from architecture overview

Smart Transport Strategy for Large Payloads

For payloads exceeding several megabytes, consider implementing a hybrid approach:

  1. Send small metadata message via muxio RPC
  2. Transfer large payload via alternative channel (HTTP multipart, object storage presigned URL)
  3. Send completion notification via muxio RPC

This pattern avoids WebSocket frame size limitations and allows specialized optimization for bulk data transfer while maintaining RPC semantics for control flow.

Example Flow:

Client -> Server: UploadRequest { file_id: "abc", size: 500MB }
Server -> Client: UploadResponse { presigned_url: "https://..." }
Client -> Storage: PUT to presigned_url (outside muxio)
Client -> Server: UploadComplete { file_id: "abc" }
Server -> Client: ProcessingResult { ... }

Sources : Design patterns implied by README.md:46-47 low-latency focus


Extending the Framework

graph TB
    subgraph "Core_Traits"
        CallerInterface["RpcServiceCallerInterface\nasync fn call_prebuffered\nasync fn call_streaming"]
EndpointInterface["RpcServiceEndpointInterface\nasync fn register_prebuffered\nasync fn register_streaming"]
end
    
    subgraph "Transport_Abstraction"
        ReadBytes["read_bytes callback\nfn(&apos;static [u8])"]
WriteBytes["write_bytes implementation\nfn send(&self Vec&lt;u8&gt;)"]
end
    
    subgraph "Provided_Implementations"
        RpcClient["RpcClient\nTokio + WebSocket"]
RpcWasmClient["RpcWasmClient\nwasm-bindgen bridge"]
RpcServer["RpcServer\nAxum + WebSocket"]
end
    
    subgraph "Custom_Implementation_Example"
        CustomTransport["CustomRpcClient\nYour transport layer"]
CustomDispatcher["Arc&lt;Mutex&lt;RpcDispatcher&gt;&gt;\nRequest correlation"]
CustomSession["RpcSession\nStream multiplexing"]
CustomSend["Custom send_bytes\ne.g. UDP, IPC, gRPC"]
CustomTransport -->|owns| CustomDispatcher
 
       CustomDispatcher -->|owns| CustomSession
 
       CustomTransport -->|implements| CustomSend
    end
    
    RpcClient -.implements.-> CallerInterface
    RpcWasmClient -.implements.-> CallerInterface
    RpcServer -.implements.-> EndpointInterface
    
    CustomTransport -.implements.-> CallerInterface
 
   CustomTransport -->|uses| ReadBytes
 
   CustomTransport -->|uses| WriteBytes
    
    CallerInterface -.requires.-> ReadBytes
    CallerInterface -.requires.-> WriteBytes

Extension Points and Custom Transports

The muxio framework exposes several well-defined extension points for custom implementations:

Sources : README.md48 RpcServiceCallerInterface description, extensions/muxio-rpc-service-caller/src/caller_interface.rs extensions/muxio-rpc-service-endpoint/src/endpoint_interface.rs

Implementing a Custom Transport

To create a custom transport (e.g., for UDP, Unix domain sockets, or custom protocols), implement the following pattern:

Required Components:

  1. Transport Handler - Manages the underlying I/O
  2. RpcDispatcher - Handles request correlation (reuse from core)
  3. RpcSession - Handles stream multiplexing (reuse from core)
  4. Trait Implementation - Implements RpcServiceCallerInterface

Key Integration Points:

ComponentResponsibilityImplementation Required
read_bytes callbackFeed received bytes to dispatcherYes - transport-specific
write_bytes functionSend frames to networkYes - transport-specific
RpcDispatcher::call()Initiate RPC requestsNo - use core implementation
RpcDispatcher::read()Process incoming framesNo - use core implementation
State managementTrack connection lifecycleYes - transport-specific

Example Structure:

custom_transport/
├── src/
│   ├── lib.rs
│   ├── custom_client.rs       # Implements RpcServiceCallerInterface
│   ├── custom_transport.rs    # Transport-specific I/O
│   └── custom_framing.rs      # Adapts RpcSession to transport

Reference implementations: extensions/muxio-tokio-rpc-client/src/rpc_client.rs for async pattern, extensions/muxio-wasm-rpc-client/src/rpc_wasm_client.rs for callback-driven pattern.

Sources : README.md:35-36 runtime agnostic design, extensions/muxio-tokio-rpc-client/src/rpc_client.rs client structure

Runtime-Specific Adapters

The core library’s non-async, callback-driven design enables integration with diverse runtime environments:

Tokio Integration Pattern:

WASM Integration Pattern:

Custom Single-Threaded Runtime:

  • Wrap RpcDispatcher in Rc<RefCell<_>>
  • Process read_bytes on main thread
  • Use callback-based async pattern
  • No thread spawning required

Custom Multi-Threaded Runtime:

  • Wrap RpcDispatcher in Arc<StdMutex<_>>
  • Create thread pool for request handlers
  • Use channels for cross-thread communication
  • Example pattern from Tokio implementation

Sources : DRAFT.md:48-52 runtime model description, extensions/muxio-tokio-rpc-client/src/rpc_client.rs extensions/muxio-wasm-rpc-client/src/rpc_wasm_client.rs


graph TB
    subgraph "Rust_WASM_Module"
        WasmApp["Application Code\ncalls RPC methods"]
RpcWasmClient["RpcWasmClient\nimplements RpcServiceCallerInterface"]
StaticClient["MUXIO_STATIC_RPC_CLIENT_REF\nthread_local RefCell"]
Dispatcher["RpcDispatcher\nrequest correlation"]
Session["RpcSession\nstream multiplexing"]
WasmApp -->|uses| RpcWasmClient
 
       RpcWasmClient -->|stores in| StaticClient
 
       RpcWasmClient -->|owns| Dispatcher
 
       Dispatcher -->|owns| Session
    end
    
    subgraph "FFI_Boundary"
        WriteBytes["#[wasm_bindgen]\nstatic_muxio_write_bytes\nfn(Vec&lt;u8&gt;)"]
JsCallback["JavaScript Callback\nwindow.muxioWriteBytes\n= function(bytes)"]
WriteBytes -->|invokes| JsCallback
    end
    
    subgraph "JavaScript_Runtime"
        WebSocket["WebSocket Instance\nws.send(bytes)"]
EventLoop["Browser Event Loop"]
OnMessage["ws.onmessage handler"]
ReadBytes["Read Path\ncalls Rust static_muxio_read_bytes"]
JsCallback -->|calls| WebSocket
 
       WebSocket -->|send| EventLoop
 
       EventLoop -->|receive| OnMessage
 
       OnMessage -->|invokes| ReadBytes
    end
    
    subgraph "Rust_Read_Path"
        StaticReadFn["#[wasm_bindgen]\nstatic_muxio_read_bytes\nfn(&[u8])"]
DispatcherRead["dispatcher.read()\nframe decoding"]
ReadBytes -->|calls| StaticReadFn
 
       StaticReadFn -->|delegates to| DispatcherRead
    end
    
 
   Session -->|generates frames| WriteBytes
 
   DispatcherRead -->|delivers responses| WasmApp

JavaScript and WASM Integration

WASM Bridge Architecture

The WASM client integrates with JavaScript through a minimal FFI bridge that passes byte arrays between Rust and JavaScript:

Sources : extensions/muxio-wasm-rpc-client/src/rpc_wasm_client.rs extensions/muxio-wasm-rpc-client/src/static_lib/static_client.rs README.md52 FFI description

Static Client Pattern

The WASM client uses a static singleton pattern due to WASM’s limitations with owned callbacks:

Problem : JavaScript callbacks cannot capture owned Rust data (no 'static lifetime guarantees)

Solution : Store client in thread-local static storage

Key Characteristics:

  • thread_local! ensures single-threaded access (WASM is single-threaded)
  • RefCell provides interior mutability
  • Option allows initialization after module load
  • All public API functions access the static client

Sources : extensions/muxio-wasm-rpc-client/src/static_lib/static_client.rs:10-12 extensions/muxio-wasm-rpc-client/src/rpc_wasm_client.rs

JavaScript Interop Patterns

The JavaScript side must implement a minimal bridge to complete the integration:

Required JavaScript Setup:

FunctionPurposeImplementation
window.muxioWriteBytesRust -> JS data transferCallback that sends bytes via WebSocket
static_muxio_read_bytes()JS -> Rust data transferWASM export invoked from onmessage
static_muxio_create_client()Initialize WASM clientWASM export called on WebSocket open
static_muxio_handle_state_change()Connection lifecycleWASM export called on WebSocket state changes

Example JavaScript Integration:

Sources : extensions/muxio-wasm-rpc-client/src/rpc_wasm_client.rs architecture, README.md52 wasm-bindgen bridge description

Memory Management Across FFI Boundary

The WASM FFI boundary requires careful memory management to prevent leaks and use-after-free issues:

Rust - > JavaScript (Write Path):

  • Vec<u8> ownership transferred to JavaScript via wasm-bindgen
  • JavaScript holds Uint8Array view into WASM linear memory
  • Critical : JavaScript must not retain references after async operations
  • Data copied by WebSocket.send(), safe to release

JavaScript - > Rust (Read Path):

  • JavaScript creates Uint8Array from WebSocket data
  • Passes slice reference to Rust via wasm-bindgen
  • Rust copies data into owned structures (Vec<u8>)
  • JavaScript can release buffer after function returns

Best Practices:

  • Always copy data across FFI boundary, never share references
  • Use wasm-bindgen type conversions (Vec<u8>, &[u8])
  • Avoid storing JavaScript arrays in Rust (lifetime issues)
  • Avoid storing Rust pointers in JavaScript (invalidation risk)

Sources : extensions/muxio-wasm-rpc-client/src/rpc_wasm_client.rs implementation, wasm-bindgen safety patterns

WASM Build and Deployment

Building and deploying the WASM client requires specific toolchain configuration:

Build Steps:

Integration into Web Application:

Sources : extensions/muxio-wasm-rpc-client/ structure, WASM build patterns from Cargo.toml target configuration