ADR 006: Lock-Free Orderbook Cache¶
Status¶
Accepted
Context¶
The arbitrage detection engine must read orderbook state for multiple markets with minimal latency while market monitors continuously update this state from WebSocket feeds. The read path is critical for opportunity detection and cannot tolerate lock contention.
Key constraints: - WebSocket updates must not block arbitrage detection - Multiple readers (strategy engines) with single writer (market monitor) per market - Sub-microsecond read latency required - State must be consistent (no torn reads)
Critical Note: "Lock-free" here refers to memory access safety for the cache, NOT cross-market execution state. Execution atomicity is handled separately by ADR-007 (Saga Pattern).
Decision¶
Implement lock-free atomic orderbook storage using arc_swap::ArcSwap for single-writer, multi-reader access patterns.
Implementation Pattern¶
use std::sync::Arc;
use arc_swap::ArcSwap;
use dashmap::DashMap;
pub struct OrderbookCache {
// Lock-free read with atomic swap for updates
books: DashMap<MarketId, ArcSwap<Orderbook>>,
}
impl OrderbookCache {
pub fn update(&self, market: MarketId, book: Orderbook) {
self.books.entry(market)
.or_insert_with(|| ArcSwap::new(Arc::new(Orderbook::empty())))
.store(Arc::new(book));
}
pub fn get(&self, market: &MarketId) -> Option<Arc<Orderbook>> {
self.books.get(market).map(|e| e.load_full())
}
}
Performance Characteristics¶
- Reads: Lock-free, single atomic load operation
- Writes: Atomic swap, no blocking of readers
- Memory: Additional allocation per update (acceptable trade-off)
Alternatives Considered¶
| Approach | Pros | Cons | Verdict |
|---|---|---|---|
| ArcSwap | Lock-free reads, simple API | Extra allocation per write | Chosen |
| RwLock | Standard library, no deps | Read contention possible | Rejected |
| Crossbeam Epoch | True lock-free | Complex, overkill | Rejected |
| Copy-on-write Vec | Simple | Memory overhead for large books | Rejected |
Consequences¶
Positive¶
- Sub-microsecond read latency achievable
- WebSocket updates never block arbitrage detection
- No deadlock risk from concurrent access
- Simple, auditable implementation
Negative¶
- Memory allocation on every update
- Not suitable for very high-frequency updates (>100K/s per market)
- Old orderbook state briefly held in memory during GC
Neutral¶
- Readers may see slightly stale data during swap (acceptable for trading)
References¶
- arc-swap crate - ArcSwap documentation
- DashMap crate - Concurrent HashMap
- FRS Section 5.1 - Performance requirements
Linked Requirements¶
- NFR-ARCH-003: Orderbook cache must be lock-free for reads