This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Method ID Generation
Loading…
Method ID Generation
Relevant source files
This page explains how the muxio RPC framework generates unique identifiers for RPC methods at compile time using the xxhash algorithm. Method IDs enable efficient method dispatch without runtime string comparisons or schema files.
For information about defining RPC services using these method IDs, see Creating Service Definitions. For details about the serialization format that carries method IDs over the wire, see Serialization with Bitcode.
Overview
The muxio RPC framework generates a unique 64-bit method identifier for each RPC method by hashing the method’s fully-qualified name at compile time. This approach provides several key benefits:
| Aspect | Implementation |
|---|---|
| Hash Algorithm | xxhash (xxHash64 variant) |
| Input | Fully-qualified method name as UTF-8 string |
| Output | 64-bit unsigned integer (u64) |
| Generation Time | Compile time (zero runtime overhead) |
| Collision Handling | Deterministic; same name always produces same ID |
The generated method IDs are embedded directly into service definition constants, eliminating the need for runtime string hashing or lookup tables. Both client and server compile against the same service definitions, ensuring that method IDs match across platforms.
Sources:
- Cargo.lock:1886-1889 (xxhash-rust dependency)
- extensions/muxio-rpc-service/Cargo.toml:16 (xxhash-rust in dependencies)
The xxhash Algorithm
The muxio framework uses the xxhash-rust crate to generate method IDs. xxhash was selected for its specific characteristics:
graph LR
Input["Method Name String\n(UTF-8 bytes)"]
XXH64["xxHash64 Algorithm"]
Output["64-bit Method ID\n(u64)"]
Input --> XXH64
XXH64 --> Output
Props["Properties:\n- Deterministic\n- Fast (compile-time only)\n- Non-cryptographic\n- Low collision rate\n- Platform-independent"]
XXH64 -.-> Props
Algorithm Characteristics
Why xxhash?
| Property | Benefit |
|---|---|
| Deterministic | Same method name always produces the same ID across all platforms and compilations |
| Fast | Minimal compile-time overhead; speed matters less since generation is compile-time only |
| Non-cryptographic | No security requirements for method IDs; simpler algorithm reduces dependencies |
| Low collision rate | Statistical likelihood of two different method names producing the same ID is negligible |
| 64-bit output | Large enough keyspace (2^64 possible IDs) to avoid collisions in practice |
The framework does not perform collision detection because:
- The 64-bit keyspace makes collisions statistically improbable for any reasonable number of methods
- Methods are identified by fully-qualified names (including trait and module paths), further reducing collision likelihood
- If a collision does occur, it will manifest as a method routing error detectable during testing
Sources:
- Cargo.lock:1886-1889 (xxhash-rust package metadata)
- extensions/muxio-rpc-service/Cargo.toml:3 (core traits and method ID generation description)
Compile-Time Generation Mechanism
Method IDs are computed during compilation, not at runtime. The generation process integrates with Rust’s trait system and constant evaluation capabilities.
sequenceDiagram
participant Source as "Service Definition Source Code"
participant Compiler as "Rust Compiler"
participant XXHash as "xxhash-rust Crate"
participant Binary as "Compiled Binary"
Note over Source: RpcMethodPrebuffered trait\nwith method name
Source->>Compiler: Compile service definition
Compiler->>XXHash: Hash method name constant
XXHash-->>Compiler: Return u64 method ID
Compiler->>Compiler: Embed ID as constant (METHOD_ID)
Compiler->>Binary: Include ID in binary
Note over Binary: Method ID available at runtime\nas a simple constant
Generation Flow
Constant Evaluation
The method ID is generated using Rust’s const fn capabilities, making the hash computation part of compile-time constant evaluation:
- Service definition declares a method name as a string constant
- The xxhash function (or wrapper) is invoked at compile time
- The resulting
u64is stored as aconstassociated with the method - This constant is directly embedded in the compiled binary
This approach means:
- Zero runtime cost : No hashing occurs during program execution
- Type safety : Method IDs are compile-time constants that cannot be accidentally modified
- Cross-platform consistency : The same source code produces identical method IDs on all platforms
Sources:
- extensions/muxio-rpc-service/Cargo.toml:3 (compile-time method ID generation description)
- Cargo.lock:858-867 (muxio-rpc-service dependencies including xxhash-rust)
Integration with Service Definitions
Method IDs are tightly integrated with the RpcMethodPrebuffered trait, which defines the contract for RPC methods.
graph TB
Trait["RpcMethodPrebuffered Trait"]
MethodName["METHOD_NAME: &'static str\ne.g., 'MyService::calculate'"]
MethodID["METHOD_ID: u64\nxxhash64(METHOD_NAME)"]
Params["Params Type\n(Serializable)"]
Response["Response Type\n(Serializable)"]
Trait --> MethodName
Trait --> MethodID
Trait --> Params
Trait --> Response
MethodName -.generates.-> MethodID
RpcRequest["RpcRequest Structure"]
RpcRequest --> MethodIDField["method_id: u64"]
RpcRequest --> ParamsField["params: Vec<u8>\n(serialized)"]
MethodID -.copied into.-> MethodIDField
Params -.serialized into.-> ParamsField
Method ID in Service Definition Structure
Method ID Storage and Usage
Each service definition provides:
| Component | Type | Purpose |
|---|---|---|
METHOD_NAME | &'static str | Human-readable method identifier (used for debugging/logging) |
METHOD_ID | u64 | Hashed identifier used in binary protocol |
Params | Associated type | Request parameter structure |
Response | Associated type | Response result structure |
The METHOD_ID constant is used when:
- Encoding requests : Client includes method ID in
RpcRequestheader - Dispatching requests : Server uses method ID to route to the appropriate handler
- Validating responses : Client verifies the response corresponds to the correct method
Sources:
- extensions/muxio-rpc-service/Cargo.toml:3 (core traits and types description)
- Cargo.lock:858-867 (muxio-rpc-service package with xxhash dependency)
sequenceDiagram
participant Client as "RPC Client"
participant Network as "Binary Transport"
participant Dispatcher as "RpcDispatcher"
participant Endpoint as "RpcServiceEndpoint"
participant Handler as "Method Handler"
Note over Client: METHOD_ID = xxhash64("Add")
Client->>Client: Create RpcRequest with METHOD_ID
Client->>Network: Serialize and send request
Network->>Dispatcher: Receive binary frames
Dispatcher->>Dispatcher: Extract method_id from RpcRequest
Dispatcher->>Endpoint: Route by method_id (u64 comparison)
alt Method ID Registered
Endpoint->>Handler: Invoke handler for METHOD_ID
Handler-->>Endpoint: Return response
Endpoint-->>Dispatcher: Send RpcResponse
else Method ID Unknown
Endpoint-->>Dispatcher: Return MethodNotFound error
end
Dispatcher-->>Network: Serialize and send response
Network-->>Client: Deliver response
Method Dispatch Using IDs
At runtime, method IDs enable efficient dispatch without string comparisons or lookup tables.
Request Processing Flow
Dispatch Performance Characteristics
The use of 64-bit integer method IDs provides:
| Characteristic | Benefit |
|---|---|
| O(1) lookup | Hash map dispatch using HashMap<u64, Handler> |
| No string allocation | Method names never allocated at runtime |
| Cache-friendly | Integer comparison much faster than string comparison |
| Minimal memory | 8 bytes per method ID vs. variable-length strings |
The endpoint maintains a dispatch table:
HashMap<u64, Arc<dyn MethodHandler>>
key: METHOD_ID (e.g., 0x12ab34cd56ef7890)
value: Handler function for that method
When a request arrives with method_id = 0x12ab34cd56ef7890, the dispatcher performs a simple hash map lookup to find the handler.
Sources:
- extensions/muxio-rpc-service/Cargo.toml:3 (service traits and method dispatch)
- Cargo.lock:883-895 (muxio-rpc-service-endpoint package)
graph TB
subgraph "Development Time"
Define["Define Service:\ntrait MyMethod"]
Name["METHOD_NAME =\n'MyService::add'"]
Hash["Compile-time xxhash64"]
Constant["const METHOD_ID: u64 =\n0x3f8a4b2c1d9e7654"]
Define --> Name
Name --> Hash
Hash --> Constant
end
subgraph "Client Runtime"
CallSite["Call my_method(params)"]
CreateReq["Create RpcRequest:\nmethod_id: 0x3f8a4b2c1d9e7654\nparams: serialized"]
Encode["Encode to binary frames"]
CallSite --> CreateReq
Constant -.embedded in.-> CreateReq
CreateReq --> Encode
end
subgraph "Network"
Transport["WebSocket/TCP Transport\nBinary protocol"]
end
subgraph "Server Runtime"
Decode["Decode binary frames"]
ExtractID["Extract method_id:\n0x3f8a4b2c1d9e7654"]
Lookup["HashMap lookup:\nhandlers[0x3f8a4b2c1d9e7654]"]
Execute["Execute handler function"]
Decode --> ExtractID
ExtractID --> Lookup
Lookup --> Execute
Constant -.registered in.-> Lookup
end
Encode --> Transport
Transport --> Decode
style Constant fill:#f9f9f9
style CreateReq fill:#f9f9f9
style Lookup fill:#f9f9f9
Complete Method ID Lifecycle
This diagram traces a method ID from definition through compilation, serialization, and dispatch:
Sources:
- extensions/muxio-rpc-service/Cargo.toml:1-9 (service definition crate overview)
- Cargo.lock:858-867 (muxio-rpc-service dependencies)
- Cargo.lock:883-895 (muxio-rpc-service-endpoint for handler registration)
Benefits and Design Trade-offs
Advantages
| Benefit | Explanation |
|---|---|
| Zero Runtime Cost | Hash computation happens once at compile time; runtime operations use simple integer comparisons |
| Type Safety | Method IDs are compile-time constants; cannot be accidentally modified or corrupted |
| Platform Independence | Same method name produces identical ID on all platforms (Windows, Linux, macOS, WASM) |
| No Schema Files | Service definitions are Rust code; no external IDL or schema generation tools required |
| Fast Dispatch | Integer hash map lookup is faster than string comparison or reflection-based dispatch |
| Compact Wire Format | 8-byte method ID vs. variable-length method name string |
Design Considerations
| Consideration | Mitigation |
|---|---|
| Hash Collisions | 64-bit keyspace makes collisions statistically improbable; detected during testing if they occur |
| Method Versioning | Changing a method name produces a new ID; requires coordinated client/server updates |
| Debugging | Method names are logged alongside IDs for human readability during development |
| Binary Compatibility | Changing method names breaks wire compatibility; version management required at application level |
The framework prioritizes simplicity and performance over elaborate versioning mechanisms. Applications requiring complex API evolution strategies should implement versioning at a higher level (e.g., versioned service definitions or API paths).
Sources:
- extensions/muxio-rpc-service/Cargo.toml:3 (compile-time method ID generation)
- Cargo.lock:1886-1889 (xxhash-rust algorithm choice)
Example: Method ID Generation in Practice
Consider a simple service definition:
Method Definition Components
Step-by-Step Process
- Definition : Developer writes
struct AddMethodimplementingRpcMethodPrebuffered - Naming : Trait defines
const METHOD_NAME: &'static str = "calculator::Add" - Hashing : Compiler invokes
xxhash64("calculator::Add")at compile time - Constant : Result (e.g.,
0x9c5f3a2b8d7e4f61) stored asconst METHOD_ID: u64 - Client Usage : When calling
Add, client createsRpcRequest { method_id: 0x9c5f3a2b8d7e4f61, ... } - Server Registration : Server registers handler:
handlers.insert(0x9c5f3a2b8d7e4f61, add_handler) - Dispatch : Server receives request, extracts
method_id, performsHashMaplookup, invokes handler
This entire flow occurs with zero string operations at runtime, providing efficient method dispatch across client and server implementations.
Sources:
- extensions/muxio-rpc-service/Cargo.toml:1-18 (service definition crate structure)
- Cargo.lock:858-867 (muxio-rpc-service dependencies including xxhash-rust)