Ultra-high-performance, low-latency state computer for real-time analytics and event-driven systems. Roda lets you build deterministic streaming pipelines with cache-friendly dataflows, wait-free reads, and explicit memory bounds—ideal for IoT, telemetry, industrial automation, and any workload where microseconds matter.
Status: Early design and API preview. Examples and tests illustrate the intended DX. Expect rapid iteration and breaking changes.
....
fn main() {
// 1. Build a multistage pipeline
let engine = StageEngine::<Reading, Reading>::with_capacity(1_000_000)
.add_stage(pipe![ // Pipe is added to add_stage by static dispatch. It aims to better inlining instructions at compilation time
stateful(
|r| r.sensor_id, // Seperate Reading feed by sensor_id
|r| Summary { sensor_id: r.sensor_id, avg: r.value, count: 1 }, // Initialize summary each time seeing new sensor_id
|s, r| { // Update summary
s.avg = (s.avg * s.count as f64 + r.value) / (s.count + 1) as f64;
s.count += 1;
}
)
])
.add_stage(pipe![
delta(
|s: &Summary| s.sensor_id,
|curr, prev| {
if let Some(p) = prev && curr.avg > p.avg * 1.5 {
return Some(Alert { sensor_id: curr.sensor_id, severity: 1 });
}
None
}
)
]);
// 2. Ingest data
engine.send(&Reading { sensor_id: 1, value: 10.0, timestamp: 1 });
engine.send(&Reading { sensor_id: 1, value: 20.0, timestamp: 2 });
// 3. Receive processed alerts
std::thread::sleep(std::time::Duration::from_millis(10));
while let Some(alert) = engine.try_receive() {
println!("{:?}", alert);
}
}Roda utilizes a Memory-Mapped Journaling architecture. Unlike a Ring Buffer, Roda uses an append-only, immutable log (Journal) for stage-to-stage communication. Each stage operates in its own thread, polling the Atomic Write Index with Acquire/Release semantics. This design provides a persistent, zero-copy audit trail of all state transitions while maintaining nanosecond-level handoff latency.
Explore more detailed implementations in the examples folder:
- Service Health Monitoring: Demonstrates noise filtering, stateful aggregation, and alert suppression.
- Real-Time Sensor Data: Showcases statistical windowing and end-to-end latency tracking.
- High-Performance MBO Replay: A production-ready market data replay and alpha generation system with CPU pinning and zero-allocation hot paths.
Roda's design translates into real-world performance. Below are results from our latest benchmarks (see examples for logs):
| Example | Throughput | IPC | Branch Accuracy | L1 Cache Miss |
|---|---|---|---|---|
| Sensor Test | 56.1 MEPS | 1.00 | 94.5% | 3.16% |
| Service Health | 57.7 MEPS | 1.55 | 97.2% | 1.65% |
| MBO Replay | 4.05 MEPS | 0.74 | 97.3% | 1.94% |
*MEPS: Million Events Per Second.
- Deterministic performance: Explicit store sizes, preallocated buffers, back-pressure free write path.
- Low latency by construction: Reader APIs are designed for zero/constant allocations and predictable access patterns.
- Multistage Pipelines: Orchestrate processing stages in dedicated threads with
StageEngine. - Declarative Composition: Build complex logic using the
pipe!macro and reusable components. - Simple Concurrency: Single-writer/multi-reader patterns with lock-free coordination.
Roda is designed for microsecond-level latency by adhering to Mechanical Sympathy principles:
- Static Dispatch: Everything is resolved at compile time. The
pipe!macro and generic stages eliminate virtual function calls (dyn), allowing the compiler to inline and optimize the entire data flow across component boundaries. - Non-blocking Pipelining via
mmap: Stages communicate through shared memory-mapped regions. Data written by one stage is immediately visible to the next without kernel-level context switches, syscalls, or expensive memory copies. - Single-Writer Multi-Reader (SWMR): Only the write index is atomic and shared between threads. Each reader maintains its own local read index, eliminating write-side contention and minimizing cache coherence traffic across CPU cores.
- Wait-Free Reads: Readers poll the atomic write index using
Acquire/Releasememory ordering. They never block or wait for other readers or the writer, ensuring predictable, jitter-free processing even under heavy load. - Append-only Journal: Data is stored in pre-allocated, contiguous buffers. This ensures linear memory access patterns, which are highly efficient for CPU prefetchers and maximize cache hit rates.
- Zero-Copy Principles: Data is never moved or copied between stages. Consumers receive borrowed views (
&T) directly into the shared memory regions, eliminating allocation overhead and reducing memory bandwidth pressure.
The pipe! macro chains processing components into a single execution stage. Each component is executed sequentially for every incoming item.
Maintains per-key state for partitioned reduction or aggregation.
stateful(
|r| r.id, // Key selector: groups data by ID
|r| State::new(r), // Initializer: creates state for a new key
|s, r| s.update(r) // Mutator: updates existing state with new input
)Compares the current incoming item with the previous one for the same key. Useful for anomaly detection or calculating rates of change.
delta(
|s| s.id, // Key selector
|curr, prev| { // Comparison logic: receives Current and Option<Previous>
if let Some(p) = prev && curr.val > p.val * 2.0 {
return Some(Alert::new(curr));
}
None
}
)Standard functional primitives for transformation and conditional dropping.
pipe![
map(|x| x.value * 2),
filter(|x| *x > 100)
]Filters out redundant items if the calculated key matches the last seen key for that partition.
dedup_by(|r| r.id)- StageEngine: The primary entry point for building pipelines. It manages a sequence of stages, each running in its own thread.
- JournalStore: A bounded, cache-friendly append-only buffer. Data stays in memory-mapped regions; consumers receive borrowed views.
- SlotStore: A bounded store for state that needs to be updated by "slots" or addresses, rather than appended.
- Stage & Pipe:
- Stage: A unit of execution (thread) that processes items from an input store to an output store.
- Pipe: Composable logic that can be chained within a single stage.
Add roda-state to your Cargo.toml:
[dependencies]
roda-state = "0.1"For a deep dive into Roda's memory model, zero-copy internals, and execution patterns, see DESIGN.md.
Licensed under the Apache License, Version 2.0. See the LICENSE file for details.