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
Relevant source files
Purpose and Scope
This document explains the compile-time method ID generation mechanism in Muxio's RPC framework. Method IDs are unique 64-bit identifiers that enable type-safe RPC dispatch without runtime overhead. The system uses compile-time hashing of method names to generate these identifiers, ensuring that method routing is both efficient and collision-resistant.
For information about implementing RPC methods and the broader service definition patterns, see Creating Service Definitions. For details on how method IDs are used during RPC dispatch, see Service Endpoint Interface.
Overview
Method ID generation is a critical component of Muxio's type-safe RPC system. Each RPC method must have a unique identifier that both client and server use to route requests to the correct handler. Rather than using string-based method names at runtime (which would require string parsing and comparison), Muxio generates fixed 64-bit integer identifiers at compile-time by hashing method names.
This approach provides several key benefits:
| Benefit | Description |
|---|---|
| Zero Runtime Overhead | Method IDs are compile-time constants; no hashing occurs during request dispatch |
| Type Safety | Duplicate method names cause compile-time errors, preventing runtime routing conflicts |
| Efficient Comparison | Integer comparison is faster than string comparison for method dispatch |
| Network Efficiency | 8-byte integers are transmitted instead of variable-length strings |
| Cross-Platform Consistency | The same hash algorithm produces identical IDs on all platforms |
Sources:
- README.md49
- Diagram 4 from high-level architecture
The METHOD_ID Field
Every RPC method type in Muxio must implement the RpcMethodPrebuffered trait, which requires a METHOD_ID constant of type u64. This constant is the compile-time generated identifier for that method.
graph TB
subgraph "Client Side"
ClientCode["Application calls Add::call()"]
EncodeReq["Add::encode_request()"]
MethodID1["Attach Add::METHOD_ID"]
end
subgraph "Network Transport"
Frame["Binary frame with method_id field"]
end
subgraph "Server Side"
DecodeFrame["Extract method_id from frame"]
Dispatch["RpcDispatcher routes by method_id"]
Handler["Registered Add handler invoked"]
end
ClientCode --> EncodeReq
EncodeReq --> MethodID1
MethodID1 --> Frame
Frame --> DecodeFrame
DecodeFrame --> Dispatch
Dispatch --> Handler
MethodID1 -.METHOD_ID = 0xABCD1234.-> Dispatch
The METHOD_ID field serves as the primary routing key throughout the RPC system:
Diagram: Method ID Flow Through RPC Call
During an RPC call, the client includes the METHOD_ID in the request frame. The server's dispatcher extracts this ID and uses it to look up the appropriate handler in its registry. This integer-based routing is significantly faster than string-based method name matching.
Sources:
- README.md:101-117
- Diagram 3 from high-level architecture
Compile-Time Hash Generation
Method IDs are generated using the xxhash-rust crate, which provides fast, non-cryptographic hashing. The hash is computed at compile-time from the method's string name, producing a deterministic 64-bit identifier.
graph LR
subgraph "Compile Time"
MethodName["Method name string\n(e.g., 'Add')"]
HashFunc["xxhash algorithm"]
MethodID["METHOD_ID constant\n(u64)"]
end
subgraph "Runtime"
NoHash["No hashing occurs"]
DirectUse["METHOD_ID used directly"]
end
MethodName --> HashFunc
HashFunc --> MethodID
MethodID --> NoHash
NoHash --> DirectUse
style MethodName fill:#f9f9f9,stroke:#333
style MethodID fill:#f9f9f9,stroke:#333
Hash Generation Process
Diagram: Compile-Time vs Runtime Hash Computation
The hash generation function is typically implemented as a const function or macro that can be evaluated at compile-time. This ensures that the METHOD_ID is embedded directly in the compiled binary as a constant value.
Implementation Details
The implementation relies on xxhash's ability to produce consistent hashes across compilations and platforms. The hash is computed from the UTF-8 bytes of the method name string:
METHOD_ID = xxhash64(method_name.as_bytes())
Key characteristics of the hash generation:
| Characteristic | Value/Behavior |
|---|---|
| Hash Algorithm | xxHash (64-bit variant) |
| Input | UTF-8 encoded method name string |
| Output | u64 constant |
| Computation Time | Compile-time only |
| Determinism | Always produces same hash for same input |
| Collision Resistance | Very low probability with 64-bit space |
Sources:
- extensions/muxio-rpc-service/Cargo.toml16
- Diagram 4 from high-level architecture
Hash Algorithm: xxHash
Muxio uses the xxHash algorithm for method ID generation, as declared in the dependency specification at extensions/muxio-rpc-service/Cargo.toml16 xxHash is specifically chosen for its properties:
Why xxHash?
| Property | Benefit for Method IDs |
|---|---|
| Extremely Fast | Minimal compile-time overhead |
| Good Distribution | Low collision probability in typical method name sets |
| Deterministic | Same input always produces same output across platforms |
| Non-Cryptographic | No security requirements; speed is prioritized |
| 64-bit Output | Large enough space to avoid collisions in practical use |
Collision Probability
With 64-bit hashes, the probability of collision follows the birthday problem:
- For 100 methods: ~0.00027% chance of collision
- For 1,000 methods: ~0.027% chance of collision
- For 10,000 methods: ~2.7% chance of collision
In practice, most RPC services define far fewer than 1,000 methods, making collisions extremely unlikely. The system also provides compile-time collision detection (see next section).
Sources:
- extensions/muxio-rpc-service/Cargo.toml16
- Diagram 6 from high-level architecture
graph TB
subgraph "Service Definition Crate"
Add["Add struct\nMETHOD_ID = hash('Add')"]
Mult["Mult struct\nMETHOD_ID = hash('Mult')"]
Add2["AddNumbers struct\nMETHOD_ID = hash('Add')\n(hypothetical collision)"]
end
subgraph "Server Registration"
Registry["Method Handler Registry\nHashMap<u64, Handler>"]
end
subgraph "Compilation Result"
Success["✓ Compiles successfully"]
Error["✗ Duplicate key error\n'Add' and 'AddNumbers'\nboth hash to same ID"]
end
Add --> Registry
Mult --> Registry
Add2 -.collides.-> Registry
Add --> Success
Mult --> Success
Add2 --> Error
Collision Detection and Prevention
While xxHash provides good distribution, Muxio's type system ensures that duplicate method IDs cause compile-time errors rather than silent runtime failures.
Compile-Time Collision Detection
Diagram: Collision Detection at Compile-Time
The collision detection mechanism works through Rust's type system and const evaluation:
- Method Registration : When handlers are registered, each
METHOD_IDbecomes a key in a compile-time checked registry - Duplicate Detection : If two methods produce the same
METHOD_ID, the registration code will fail to compile - Clear Error Messages : The compiler reports which method names collided
Best Practices to Avoid Collisions
| Practice | Rationale |
|---|---|
| Use Descriptive Names | Longer, more specific names reduce collision probability |
| Namespace Methods | Prefix related methods (e.g., User_Create, User_Delete) |
| Avoid Abbreviations | Add is better than Ad or A |
| Test at Compile Time | Collisions are caught during build, not in production |
Sources:
- Diagram 4 from high-level architecture
- README.md49
sequenceDiagram
participant Client as "RpcClient"
participant Encoder as "RpcRequest encoder"
participant Network as "Binary protocol"
participant Decoder as "RpcRequest decoder"
participant Dispatcher as "RpcDispatcher"
participant Registry as "Handler registry"
participant Handler as "Method handler"
Client->>Encoder: METHOD_ID + params
Note over Encoder: METHOD_ID = 0xABCD1234
Encoder->>Network: Binary frame [id=0xABCD1234]
Network->>Decoder: Receive frame
Decoder->>Dispatcher: Extract METHOD_ID
Dispatcher->>Registry: lookup(0xABCD1234)
Registry->>Handler: Get registered handler
Handler->>Handler: Execute method
Handler->>Dispatcher: Return response
Note over Dispatcher,Registry: O(1) integer lookup\nvs O(n) string comparison
Method ID Usage in RPC Dispatch
Once generated, method IDs flow through the entire RPC pipeline to enable efficient request routing.
Request Dispatch Flow
Diagram: Method ID in Request Dispatch Sequence
The dispatcher maintains a HashMap<u64, Handler> mapping method IDs to their handler functions. When a request arrives:
- The binary frame is decoded to extract the
method_idfield - The dispatcher performs a hash map lookup using the 64-bit integer key
- If found, the corresponding handler is invoked
- If not found, an "method not found" error is returned
graph TB
subgraph "Dispatcher State"
Registry["Handler Registry\nHashMap<u64, Box<dyn Handler>>"]
end
subgraph "Registered Methods"
Add["0xABCD1234\n'Add' handler"]
Mult["0xDEF56789\n'Mult' handler"]
Echo["0x12345678\n'Echo' handler"]
end
subgraph "Incoming Request"
Request["RpcRequest\nmethod_id: 0xABCD1234"]
end
Add --> Registry
Mult --> Registry
Echo --> Registry
Request --> Registry
Registry -.lookup.-> Add
This integer-based lookup is significantly faster than string-based routing and consumes less memory in the dispatch table.
Handler Registry Structure
Diagram: Handler Registry Data Structure
The registry uses Rust's HashMap for O(1) average-case lookup performance. Each entry maps a METHOD_ID (u64) to a handler function or closure that can process requests for that method.
Sources:
- README.md:101-117
- Diagram 3 from high-level architecture
- extensions/muxio-rpc-service/Cargo.toml:1-18
graph TB
subgraph "Service Definition Crate"
Trait["RpcMethodPrebuffered"]
AddDef["Add implementation\nMETHOD_ID\nencode_request\ndecode_response"]
end
subgraph "Client Crate"
ClientCall["Add::call()\nuses Add::METHOD_ID\nuses Add::encode_request"]
end
subgraph "Server Crate"
ServerReg["register_prebuffered(Add::METHOD_ID)\nuses Add::decode_request"]
end
subgraph "Compile-Time Checks"
Check1["✓ Same METHOD_ID on client and server"]
Check2["✓ Same data structures in encode/decode"]
Check3["✓ Type mismatches = compile error"]
end
Trait --> AddDef
AddDef --> ClientCall
AddDef --> ServerReg
ClientCall --> Check1
ServerReg --> Check1
ClientCall --> Check2
ServerReg --> Check2
AddDef --> Check3
Type Safety Guarantees
The method ID generation system provides several compile-time guarantees that prevent entire classes of runtime errors.
Shared Definition Enforcement
Diagram: Type Safety Through Shared Definitions
Key guarantees provided by the system:
| Guarantee | Enforcement Mechanism |
|---|---|
| Method ID Consistency | Both client and server use Add::METHOD_ID from shared crate |
| Parameter Type Safety | Shared encode_request and decode_request functions |
| Response Type Safety | Shared encode_response and decode_response functions |
| API Contract Enforcement | Any change to method signature requires recompilation of both client and server |
Prevented Error Classes
By using compile-time method IDs and shared definitions, the following runtime errors become impossible:
- Method Name Typos : Cannot call a method that doesn't exist
- Parameter Type Mismatches : Wrong parameter types fail at compile-time
- Version Skew : Incompatible client/server versions won't compile against same service definition
- Routing Errors : Impossible to route to wrong handler due to integer-based dispatch
Sources:
- README.md49
- Diagram 4 from high-level architecture
Implementation Example
Here's how method ID generation integrates into a complete service definition, as demonstrated in the example application:
Service Definition Structure
Service Definition Crate
├── RpcMethodPrebuffered trait implementations
│ ├── Add::METHOD_ID = hash("Add")
│ ├── Add::encode_request
│ ├── Add::decode_request
│ ├── Add::encode_response
│ └── Add::decode_response
├── Mult::METHOD_ID = hash("Mult")
│ └── ... (similar structure)
└── Echo::METHOD_ID = hash("Echo")
└── ... (similar structure)
Client Usage
From README.md:144-151 the client invokes methods using the shared definitions:
Internally, this:
- Uses
Add::METHOD_IDto identify the method - Calls
Add::encode_requestto serialize parameters - Sends the request with the method ID
- Calls
Add::decode_responseto deserialize the result
Server Registration
From README.md:101-106 the server registers handlers using the same IDs:
The registration explicitly uses Add::METHOD_ID, ensuring that:
- Client calls to "Add" route to this handler
- The handler uses the correct decode/encode functions
- Any mismatch in type definitions causes a compile error
Sources:
graph TB
subgraph "Shared Service Definition"
Source["Add::METHOD_ID = hash('Add')"]
end
subgraph "Native Linux Build"
Linux["Compiled METHOD_ID\n0xABCD1234"]
end
subgraph "Native Windows Build"
Windows["Compiled METHOD_ID\n0xABCD1234"]
end
subgraph "WASM Build"
WASM["Compiled METHOD_ID\n0xABCD1234"]
end
Source --> Linux
Source --> Windows
Source --> WASM
Linux -.identical.-> Windows
Windows -.identical.-> WASM
Cross-Platform Consistency
Method IDs are generated identically across all platforms where Muxio runs, including native (x86, ARM), WebAssembly, and different operating systems.
Platform-Independent Hash Generation
Diagram: Cross-Platform Method ID Consistency
This consistency is critical for the "write once, deploy everywhere" architecture. The same service definition crate can be:
- Compiled into a native Tokio server
- Compiled into a native Tokio client
- Compiled into a WASM client for browsers
- Used in all three simultaneously
All instances will generate and use identical method IDs, ensuring interoperability.
Sources:
- README.md47
- extensions/muxio-wasm-rpc-client/Cargo.toml:1-30
- Diagram 2 from high-level architecture
Performance Characteristics
The compile-time method ID generation provides significant performance benefits compared to string-based method identification:
| Aspect | String-Based | Method ID (u64) | Improvement |
|---|---|---|---|
| Network Size | Variable (4-30 bytes) | Fixed (8 bytes) | ~50-75% reduction |
| Comparison Speed | O(n) string compare | O(1) integer compare | 10-100x faster |
| Memory Overhead | String allocation | Integer copy | Minimal |
| Hash Computation | At runtime | At compile-time | Zero runtime cost |
| Cache Efficiency | Poor (pointer chase) | Excellent (value type) | Better CPU utilization |
Benchmark Scenarios
In typical RPC scenarios:
- Method Dispatch : Integer-based lookup is 10-100x faster than string comparison
- Network Transmission : Fixed 8-byte ID saves bandwidth and reduces serialization time
- Memory Pressure : No string allocations or hash map overhead for method names
These optimizations are particularly valuable in high-throughput scenarios where thousands of RPC calls per second are processed.
Sources:
- README.md32
- README.md45
- Diagram 6 from high-level architecture
Summary
Method ID generation is a foundational element of Muxio's type-safe RPC system. By hashing method names at compile-time into 64-bit integers, the system achieves:
- Zero runtime overhead for method identification
- Compile-time collision detection preventing routing errors
- Efficient network transmission using fixed-size identifiers
- Type-safe APIs through shared service definitions
- Cross-platform consistency ensuring interoperability
The combination of compile-time hashing, shared trait implementations, and integer-based dispatch creates a robust foundation for building distributed systems where API contracts are enforced by the compiler rather than runtime validation.
Sources:
- README.md:1-166
- extensions/muxio-rpc-service/Cargo.toml:1-18
- All architecture diagrams
Dismiss
Refresh this wiki
Enter email to refresh