nexus-id 2.0.0

High-performance ID generators for low-latency systems
Documentation
# Patterns

Realistic recipes for common ID needs.

## Client order IDs on a trading hot path

One generator per writer thread. Worker bits encode the thread index (or the
matching engine shard). Sequence bits sized so you never run out within a
millisecond.

```rust
use nexus_id::{Snowflake64, SnowflakeId64};

const WORKER_ID: u32 = 5;

// 42 timestamp bits, 6 worker bits, 16 sequence bits.
// 65,536 orders per ms per worker = 65M orders/sec/worker peak.
pub type OrderGen = Snowflake64<42, 6, 16>;
pub type OrderId = SnowflakeId64<42, 6, 16>;

pub struct OrderIssuer {
    gen: OrderGen,
    epoch_ms: u64,
}

impl OrderIssuer {
    pub fn new(epoch_ms: u64) -> Self {
        Self { gen: OrderGen::new(WORKER_ID), epoch_ms }
    }

    pub fn next(&mut self, now_ms: u64) -> OrderId {
        let rel_ms = now_ms.saturating_sub(self.epoch_ms);
        // Sequence exhaustion at 65k/ms is effectively impossible; crash if it happens.
        self.gen.next_id(rel_ms).expect("order sequence exhausted within 1 ms")
    }
}
```

Store these directly in protocol messages. At the hash-map boundary, use
`MixedId64` + `nohash_hasher` — see [mixed-id.md](mixed-id.md).

## Request trace IDs

Use `UuidV7` for distributed traces: time-sortable makes log aggregation
trivial, and the V7 format is standard enough that OpenTelemetry and
friends understand it out of the box.

```rust
use nexus_id::{UuidV7, Uuid};

pub struct TraceSource {
    gen: UuidV7,
}

impl TraceSource {
    pub fn new() -> Self { Self { gen: UuidV7::from_entropy() } }
    pub fn span(&mut self) -> Uuid { self.gen.generate() }
}
```

When the same log line needs to appear in both structured logs and a
human-facing UI, use `TypeId<8>` with a `trace` prefix:

```rust
use nexus_id::{TypeId, UuidV7};

let uuid = UuidV7::from_entropy().generate();
let compact = u64::from_le_bytes(uuid.as_bytes()[0..8].try_into().unwrap());
let trace: TypeId<8> = TypeId::from_parts("trace", compact).unwrap();
```

## Content hashes

For cache keys or deduplication, you don't want a monotonic ID — you want
a hash of the content. `nexus-id` doesn't hash for you (use `xxhash-rust`,
`ahash`, or `blake3` depending on your needs), but `HexId64` gives you a
cheap string form of the result:

```rust
use nexus_id::HexId64;

let content_hash: u64 = xxhash_rust::xxh3::xxh3_64(payload);
let cache_key = HexId64::from_u64(content_hash);
// cache_key.as_str() is a 16-char hex string, zero-alloc.
```

## HashMap-heavy workloads

If your hot loop is dominated by map lookups keyed by order ID:

```rust
use std::collections::HashMap;
use nohash_hasher::BuildNoHashHasher;
use nexus_id::{MixedId64, SnowflakeId64};

type OrderMap<V> =
    HashMap<MixedId64<42, 6, 16>, V, BuildNoHashHasher<u64>>;

// Key conversion is ~1 cycle — do it at the call site.
fn lookup(map: &OrderMap<Order>, id: SnowflakeId64<42, 6, 16>) -> Option<&Order> {
    map.get(&id.mixed())
}
```

## Public-facing API IDs

Stripe-style `prefix_<base32>` IDs make your API surface self-documenting.
Generate the value from `UuidV7` so resources are still time-sortable in
your database:

```rust
use nexus_id::{TypeId, UuidV7};

pub fn new_order_id(gen: &mut UuidV7) -> TypeId<4> {
    let u = gen.generate();
    let hi = u64::from_be_bytes(u.as_bytes()[0..8].try_into().unwrap());
    TypeId::from_parts("ord", hi).unwrap()
}
```

Users see `ord_01J2MB6KFF2TMXYZ...`. Your index still gets sequential
insertions. Everyone wins.