3 MIN READ · 12 MAR 2026 · systems

Lock-free SPSC ring buffers for tick ingestion

A back-pressure-free producer/consumer pattern that survived a 27k tick/sec burst without dropping a frame.

SB
Steven Battilana Quant · Zürich · ex-ETH

The Hook

The tick ingester started dropping frames at peak load. Single-producer, single-consumer bounded channels in Tokio were doing fine at 6k tick/sec — and then 27k arrived during the close-of-day burst and the producer started parking on send().await.

The Context

The standard advice is “increase the channel capacity”. That hides the problem until the next burst. The real lever is to remove the lock entirely from the hot path. A lock-free SPSC ring buffer is the right primitive for a single-producer-single-consumer queue: atomic head/tail indices, no kernel mediation, predictable latency.

The Code

use crossbeam::queue::ArrayQueue;
use std::sync::Arc;

pub fn make_ring<T>(capacity: usize) -> (Arc<ArrayQueue<T>>, Arc<ArrayQueue<T>>) {
    let q = Arc::new(ArrayQueue::new(capacity));
    (q.clone(), q)
}

The producer thread calls push; the consumer thread calls pop. No locking, no syscalls in the fast path.

The Takeaways

  1. Lock-free is not free — it shifts the cost to atomic ordering, which is a real CPU expense at MHz rates.
  2. Capacity sizing matters: too small and you back-pressure; too large and you hide a real producer/consumer imbalance.
  3. Always benchmark with a realistic burst pattern. The mean rate is the lie that hides the tail.
Posted 12 MAR 2026 · filed under Systems