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

Introduction

What to use

One line per module: what it is for, and the call you make.

modulewhat it doeshow you use it
ergo-sbeschema XML → Rust SBE codecsergo_sbe::generate_to_out_dir(...) in build.rs; then plain include!, or ergo_sbe::sbe_mod!(name) in your crate, which also needs ergo-sbe in [dependencies] (chapter)
ergon-tunereads the host and names what will wreck your tail latency, and the fixcall at start-up; it changes nothing (chapter)
ergon-ioWebSocket and HTTP/TLS on one busy-spinning threadpoll() from your loop; epoll or io_uring is configuration (chapter)
ergo-aeron-clusterAeron Cluster clientonly if you need cluster; experimental, not a production substitute (chapter)
ergon-md-dpdkDPDK transport for the Aeron media driverLinux x86_64 and aarch64; proof of concept — use Aeron Premium for kernel bypass (chapter)

Kubernetes, if you deploy there. Tuning a node and pod for HFT: Kubernetes HFT tuning — start there.

modulewhat it doeshow you use it
k8s/nri-shared-cpusgives a pod its exclusive CPUs and the node’s shared housekeeping CPUs, and starts every thread on the shared ones so the busy-spin loop keeps its corehelm install ergon-shared-cpus oci://ghcr.io/mimran1980/charts/ergon-shared-cpus --version 0.1.0. Image ghcr.io/mimran1980/ergon-shared-cpus:0.1.0. Prepare the node first. Sample: k8s/nri-shared-cpus/examples/try (chapter)
k8s/nri-latency-podsnode tuning and the hot-pod admission policy (no exec probes, no always-restart sidecars). The grants live in the shared-cpus image. This directory is the YAML and the node unitsapply admission/priority-class.yaml and admission/validating-admission-policy.yaml after the chart. Pod authors set annotations and the ergon.io/latency: hot label (chapter)

Start with ergo-sbe. Add another crate only for a need you have: ergon-io for exchange I/O, ergon-tune to check a latency host’s settings. Which clock to read for timestamps and durations: Clocks.

ergo-sbe generates Rust codecs with no heap allocation on the encode and decode hot paths (allocation_count_test) from Simple Binary Encoding schemas. Wire layout matches official SBE for the published profile. Tail order is a type error, not a runtime surprise.

Quick start

[build-dependencies]
ergo-sbe = "0.1"

[dependencies]
ergo-sbe = "0.1"   # only for sbe_mod! / include_sbe!
#![allow(unused)]
fn main() {
let mut buf = [0u8; HeartbeatEncoder::compute_length_with_header()];
// Buffer is exact size from const compute_length_with_header — no bounds check needed.
let len = HeartbeatEncoder::try_wrap_and_apply_header(&mut buf, 0)?
    .fixed(&HeartbeatFixedFields { sequence: 7, timestamp: 0 })
    .encoded_length_with_header();
let dec = HeartbeatDecoder::try_from(&buf[..len])?;
assert_eq!(dec.sequence(), 7);
}

(From book/examples/heartbeat-encode.rs — compiled against the feature-tour codec.)

Set up → · Feature tour → · Coming from sbe-tool →

Wire orderNamed stages — asks before bids does not compile
Exact sizingcompute_length_with_header / *EncodedLength before you write; works with Aeron try_claim
GroupsClosures nest like the schema; no .parent()
Trust boundarytry_* → Result; bare wrap panics if short; bare decode panics if short and Err on wrong template/schema; unsafe *_unchecked skips the proof
Composites#[repr(transparent)] Engine([u8; N]) — the value is the on-wire bytes
Domain typesMap wire Decimal to rust_decimal::Decimal with one config line
Bulk groupsbulk_add(&[Entry]) / bulk_decode() for fixed-stride leaf groups
Runtime depsGenerated codecs embed sbe_rt — no ergo-sbe on the hot path

0.x experimental. Pin versions. Exit criteria: Road to 1.0.

It is not a line-for-line port of sbe-tool. The generated API is meant to be easier under Rust’s borrow checker, safer at untrusted boundaries, and still official-SBE on the wire. Nested groups use scoped closures so you do not hand encoder ownership back with .parent():

#![allow(unused)]
fn main() {
pub fn encode_sample_car(buf: &mut [u8]) -> Result<usize, sbe_rt::EncodeError> {
    let mut extras = OptionalExtras::default();
    extras.cruise_control(true).sports_pack(true);

    // Buffer pre-sized from EncodedLength; try_* still validates extent.
    let len = CarEncoder::try_wrap_and_apply_header(buf, 0)
        .unwrap()
        .fixed(&CarFixedFields {
            serial_number: 1234,
            model_year: 2013,
            available: true.into(),
            code: Model::A,
            some_numbers: [10, 20, 30, 40],
            vehicle_code: [b'A', b'B', b'C', b'D', b'E', b'F'],
            extras,
            engine: Engine::new(
                2000,
                4,
                [b'1', b'2', b'3'],
                0i8,
                false.into(),
                Booster::new(BoostType::TURBO, 210),
            ),
        })
        .fuel_figures(2, |g| {
            g.add(|mut e| {
                e.speed(30).mpg(35.9);
                e.usage_description_as_str("Urban")
            })?;
            g.add(|mut e| {
                e.speed(60).mpg(25.0);
                e.usage_description_as_str("Highway")
            })?;
            Ok(())
        })?
        .performance_figures(1, |g| {
            g.add(|mut e| {
                e.octane_rating(95);
                e.acceleration(2, |a| {
                    a.add(|x| {
                        x.mph(30).seconds(4.0);
                        Ok(())
                    })?;
                    a.add(|x| {
                        x.mph(60).seconds(7.5);
                        Ok(())
                    })
                })
            })?;
            Ok(())
        })?
        .manufacturer_as_str("Honda")?
        .model_as_str("Civic VTi")?
        .activation_code_as_str("abcdef")?
        .encoded_length_with_header();

    Ok(len)
}
}

(Compiled and run in CI.)

Wire parity is tested by the dual-encode suite (sbe_tool_wire_parity_test, sbe_tool_multi_schema_wire_parity_test). The benchmarks also assert byte parity before timing: Benchmarks.

Deployment

How to set up hosts and Kubernetes for these crates on AWS and Azure, x86 and Graviton: Deployment.

Rust 1.99+. Clone setup and checks: repository README.