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.

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:

BenefitDescription
Zero Runtime OverheadMethod IDs are compile-time constants; no hashing occurs during request dispatch
Type SafetyDuplicate method names cause compile-time errors, preventing runtime routing conflicts
Efficient ComparisonInteger comparison is faster than string comparison for method dispatch
Network Efficiency8-byte integers are transmitted instead of variable-length strings
Cross-Platform ConsistencyThe same hash algorithm produces identical IDs on all platforms

Sources:


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:


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:

CharacteristicValue/Behavior
Hash AlgorithmxxHash (64-bit variant)
InputUTF-8 encoded method name string
Outputu64 constant
Computation TimeCompile-time only
DeterminismAlways produces same hash for same input
Collision ResistanceVery low probability with 64-bit space

Sources:


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?

PropertyBenefit for Method IDs
Extremely FastMinimal compile-time overhead
Good DistributionLow collision probability in typical method name sets
DeterministicSame input always produces same output across platforms
Non-CryptographicNo security requirements; speed is prioritized
64-bit OutputLarge 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:


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:

  1. Method Registration : When handlers are registered, each METHOD_ID becomes a key in a compile-time checked registry
  2. Duplicate Detection : If two methods produce the same METHOD_ID, the registration code will fail to compile
  3. Clear Error Messages : The compiler reports which method names collided

Best Practices to Avoid Collisions

PracticeRationale
Use Descriptive NamesLonger, more specific names reduce collision probability
Namespace MethodsPrefix related methods (e.g., User_Create, User_Delete)
Avoid AbbreviationsAdd is better than Ad or A
Test at Compile TimeCollisions are caught during build, not in production

Sources:


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:

  1. The binary frame is decoded to extract the method_id field
  2. The dispatcher performs a hash map lookup using the 64-bit integer key
  3. If found, the corresponding handler is invoked
  4. If not found, an "method not found" error is returned
graph TB
    subgraph "Dispatcher State"
        Registry["Handler Registry\nHashMap&lt;u64, Box&lt;dyn Handler&gt;&gt;"]
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:


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:

GuaranteeEnforcement Mechanism
Method ID ConsistencyBoth client and server use Add::METHOD_ID from shared crate
Parameter Type SafetyShared encode_request and decode_request functions
Response Type SafetyShared encode_response and decode_response functions
API Contract EnforcementAny 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:

  1. Method Name Typos : Cannot call a method that doesn't exist
  2. Parameter Type Mismatches : Wrong parameter types fail at compile-time
  3. Version Skew : Incompatible client/server versions won't compile against same service definition
  4. Routing Errors : Impossible to route to wrong handler due to integer-based dispatch

Sources:


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:

  1. Uses Add::METHOD_ID to identify the method
  2. Calls Add::encode_request to serialize parameters
  3. Sends the request with the method ID
  4. Calls Add::decode_response to 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:

  1. Compiled into a native Tokio server
  2. Compiled into a native Tokio client
  3. Compiled into a WASM client for browsers
  4. Used in all three simultaneously

All instances will generate and use identical method IDs, ensuring interoperability.

Sources:


Performance Characteristics

The compile-time method ID generation provides significant performance benefits compared to string-based method identification:

AspectString-BasedMethod ID (u64)Improvement
Network SizeVariable (4-30 bytes)Fixed (8 bytes)~50-75% reduction
Comparison SpeedO(n) string compareO(1) integer compare10-100x faster
Memory OverheadString allocationInteger copyMinimal
Hash ComputationAt runtimeAt compile-timeZero runtime cost
Cache EfficiencyPoor (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:


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:

  1. Zero runtime overhead for method identification
  2. Compile-time collision detection preventing routing errors
  3. Efficient network transmission using fixed-size identifiers
  4. Type-safe APIs through shared service definitions
  5. 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:

Dismiss

Refresh this wiki

Enter email to refresh