It's mostly atomics, with a deliberate design choice to avoid traditional backpressure.
1. Lock-Free Parts (Atomics) The ring buffer relies on atomic.Uint64 for head/tail cursors. Also, hot-swapping the processor chain uses atomic.Pointer[T] to ensure the worker loop never blocks even during a config reload:
// Worker reads config without locks
currentChain := p.chain.Load()
currentChain.Process(data)
// Config updates swap the pointer atomically
p.chain.Store(newChain)
I intentionally avoided channels for the data path. Benchmarks showed that at >100k msgs/sec, the channel allocation/locking overhead was 2-3x costlier than the ring buffer approach.
Handling Backpressure: I Don't (Intentionally)
You're right that backpressure is tricky without locks. My approach was to eliminate the need for it:
Drop-on-full: If saturated, new logs are dropped (preserves recent history, non-blocking)
Fail-open circuit breaker: If buffer >80% full, bypass processing to drain faster
Philosophy: For observability, dropping overflow is better than blocking ingestion
The honest answer: This isn't "wait-free" in the academic sense—WaitGroup is used in output fanout. But the ingestion→buffering path has zero mutexes.
1. Lock-Free Parts (Atomics) The ring buffer relies on atomic.Uint64 for head/tail cursors. Also, hot-swapping the processor chain uses atomic.Pointer[T] to ensure the worker loop never blocks even during a config reload:
// Worker reads config without locks currentChain := p.chain.Load() currentChain.Process(data)
// Config updates swap the pointer atomically p.chain.Store(newChain)
I intentionally avoided channels for the data path. Benchmarks showed that at >100k msgs/sec, the channel allocation/locking overhead was 2-3x costlier than the ring buffer approach.
Handling Backpressure: I Don't (Intentionally) You're right that backpressure is tricky without locks. My approach was to eliminate the need for it:
Drop-on-full: If saturated, new logs are dropped (preserves recent history, non-blocking) Fail-open circuit breaker: If buffer >80% full, bypass processing to drain faster Philosophy: For observability, dropping overflow is better than blocking ingestion The honest answer: This isn't "wait-free" in the academic sense—WaitGroup is used in output fanout. But the ingestion→buffering path has zero mutexes.