Thread-per-core
Each worker thread binds to one CPU core and opens its ownSO_REUSEPORT listener socket. The kernel distributes incoming connections across workers automatically. Workers never share mutable state during request processing.
What each worker owns exclusively:
| Resource | Type |
|---|---|
| io_uring ring | Uring struct (arc-net) |
| Fixed buffer pool | FixedBuffers (arc-net) |
| Connection slab | indexed by ConnId (arc-gateway) |
| Upstream connection pool | UpstreamPool (arc-gateway) |
| Plugin instance pools | PluginCatalog per-pool (arc-plugins) |
| Rate limit L1 cache | WorkerLimiter (arc-global-rate-limit) |
| Metrics counters | WorkerMetrics (arc-observability) |
| Log ring buffer | SPSC ring (arc-logging) |
| Resource | Mechanism |
|---|---|
| Routes, upstreams, plugins, limiters | ArcSwap<Arc<SharedConfig>> — acquire load per request tick |
| Metrics arrays | AtomicU64 counters; admin thread reads with relaxed ordering |
| Global rate limit backend | GlobalRateLimiter with L2 Redis; workers contact it asynchronously for refills |
| XDP BPF maps | Kernel-managed; userspace XdpManager writes via libbpf |
io_uring data plane
Arc uses Linux’s io_uring interface rather than epoll or an async runtime like Tokio. This eliminates per-operation syscall overhead. The key io_uring features Arc uses:- SQPOLL — kernel polling thread that drains the submission queue without syscalls from userspace
- Fixed buffers — pre-registered buffer pool; read/write use buffer indices rather than pointers
- Fixed files — pre-registered file descriptor set; eliminates fd table lookup overhead
- Multishot accept — single
acceptSQE that re-arms itself after each connection; no re-submission per accept - Multishot timeout — single timeout SQE for the connection slab timer wheel
Shared configuration and hot reload
All workers share a singleArcSwap<Arc<SharedConfig>>. SharedConfig contains the compiled router, compiled upstreams, plugin catalog, per-route rate limiters, and TLS state. It is immutable after construction.
The hot reload flow:
restart_required_changes(). If any of those fields change, hot reload is rejected.
All config formats are normalized to canonical JSON internally. TOML and YAML files are parsed, converted to a serde_json::Value, keys are sorted, and the result is stored as raw_json: Arc<str> in SharedConfig. This ensures deterministic fingerprinting across cluster nodes.
Request flow
Security layers
Arc applies three independent security layers:Security details
See full detail.
Crate dependency graph
Crate sizes and roles
| Crate | Lines (approx.) | Role |
|---|---|---|
arc-gateway | 13,638 | Main proxy state machine; connection lifecycle; worker loop |
arc-config | 3,141 | Config schema, compilation, ArcSwap hot reload |
arc-core | 2,184 | Shared types (ArcConfig, NodeConfig, ListenerConfig, …) |
arc-net | 1,642 | Raw io_uring syscall wrappers; buffer pool; socket ops |
arc-router | 1,089 | Compressed radix tree; route compilation; O(log n) matching |
arc-acme | 1,089 | ACME challenge lifecycle; TLS-ALPN-01; HTTP-01 |
arc-global-rate-limit | 904 | Two-tier rate limiter; Redis Lua backend; circuit breaker |
arc-proto-http1 | 591 | HTTP/1.x request/response parser; chunked decoder |
arc-proto-h2 | 542 | HTTP/2 frame parser; SETTINGS; flow control; stream state |
arc-logging | 480 | NDJSON log encoding; SPSC ring buffers; io_uring batch writes; file rotation |
arc-xdp-userspace | 420 | XDP/eBPF userspace manager; BPF map writes; dynamic threshold computation |
arc-plugins | 347 | Wasmtime integration; instance pool; on_request ABI |
arc-observability | 327 | WorkerMetrics atomic counters; admin server; /metrics |
arc-cli | 126 | arc logs tail/query CLI |
arc-rate-limit | 104 | Single-tier GCRA atomic rate limiter |
arc-common | 100 | ArcError, Result |

