Skip to main content

Thread-per-core

Each worker thread binds to one CPU core and opens its own SO_REUSEPORT listener socket. The kernel distributes incoming connections across workers automatically. Workers never share mutable state during request processing. What each worker owns exclusively: What workers share (read-only or atomic):

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 accept SQE 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 single ArcSwap<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:
Fields that require a process restart (listener addresses, worker count, io_uring ring sizing, control plane binding) are gated by 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