VeltrixDB Docs
v1.1.0 GitHub FAQ Quickstart

Architecture Overview

How a write and a read actually move through VeltrixDB, from the TCP listener down to NVMe.

In one sentence VeltrixDB separates keys from values (WiscKey KV-separation): a small in-memory index points at values stored in an append-only log on NVMe, so writes never rewrite old data and reads are either a DRAM lookup or a single disk seek.

System diagram

Client (any SDK or nc)
        │  TCP — binary or text protocol
        ▼
┌─────────────────────────────────────────┐
│  TCP Server (cmd/server)                │
│  1 goroutine/conn · pipeline coalescing │
└──────────────────┬──────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────┐
│  Storage Engine (storage/engine.go)     │
│                                         │
│  In-Memory Index   LIRS Cache           │
│  8192 shards       hot values in RAM    │
│                                         │
│  WAL (per disk)    VLog (per disk)      │
│  group-commit      append-only values   │
└──────────────────┬──────────────────────┘
                   │ (Linux production only)
                   ▼
┌─────────────────────────────────────────┐
│  C++ Layer                              │
│  ART index · io_uring · NUMA pinning    │
└─────────────────────────────────────────┘

Sharding

All data is split across 8192 shards. Each shard has its own lock, so a read on shard 5 never blocks a read on shard 42.

key → FNV-1a hash → bottom 13 bits (& 0x1FFF) → shard ID (0–8191)
shard ID % numDisks → which disk (WAL + VLog + compaction)

With 8 disks, 1024 shards land on each disk. A failure on one disk doesn't affect the other 7. The Go numShards constant and the C++ kNumShards constant must stay equal — they are a hard cross-language invariant.

Key components

ComponentRole
In-memory indexA hash map per shard (Go) or an Adaptive Radix Tree (C++). Each entry stores disk offset, disk index, value size, flags, and a monotone version counter — about 64 bytes per key.
LIRS cacheScan-resistant eviction. Small values (≤256 B) get eviction priority 2 vs. priority 1 for larger values, so a big sequential scan can't push your hot working set out of RAM. Sized with -cache <MB>.
WAL (Write-Ahead Log)Group-commit: many concurrent writers share one fdatasync call per flush window (default 10 ms). One WAL file per disk.
VLog (Value Log)Append-only file per disk. Values live here; the index holds only metadata (this is the WiscKey KV-separation). Appends are lock-free via atomic offset reservation.
Block packingBatched writes (MultiPut) pack up to 26 records per 4 KB VLog block. For 128 B values that's 152 bytes of overhead per record instead of 4096 — a 27× density improvement. A single Put stays on the unpacked fast path.

Write path

What happens on PUT user:42 value:

  1. Hash the key → shard 307 → disk 2 (307 % 8)
  2. Atomically reserve a VLog offset and write the value
  3. Write a WAL entry (key + VLog offset)
  4. WAL and VLog fdatasync race concurrently — the client waits for the slower of the two
  5. Update the shard 307 index entry (disk offset, version++)
  6. Insert the value into the LIRS cache
  7. Return OK
If the process crashes after step 2 but before step 3, the value is orphaned on disk but was never made visible to any reader — that's safe by construction, not an edge case that's merely tolerated.

Read path

What happens on GET user:42:

  1. Hash the key → shard 307
  2. LIRS cache hit → return the value with no disk I/O (~220 ns)
  3. Cache miss → take a read lock on shard 307, look up the index entry
  4. Not in the index → NOT_FOUND
  5. In the index → read the value from the VLog at the recorded disk offset (~400 µs on NVMe)
  6. Insert the value into the cache
  7. Return the value

Admission control

Background garbage collection competes with foreground reads for NVMe bandwidth. VeltrixDB tracks an EWMA of read latency and throttles GC before reads degrade:

Read EWMAAction
< 3 msNormal — GC runs at full speed
≥ 3 msGC bandwidth capped at 60 MB/s
≥ 4 msGC paused entirely; each PUT sleeps 2 ms to shed load
< 2 ms (recovery)Everything resumes at full speed
No reads for 4 minutesEWMA is treated as stale and GC resumes regardless

See Admission Control & GC for the full escalation ladder, including the emergency GC tier that bypasses admission pauses to prevent a garbage death-spiral.

C++ layer (Linux only)

On Linux, VeltrixDB loads an optional C++ acceleration layer for production throughput. The Go layer is fully functional on its own and is what runs on macOS and in development.

ComponentReplacesWhy it's faster
ART indexGo hash mapFaster range scans, prefix compression
io_uring schedulerBlocking pwrite3-tier I/O priority — foreground reads always beat background compaction
SQPOLL VLog readerBlocking preadZero-syscall NVMe reads
NUMA thread pinningDefault OS schedulerEliminates cross-socket memory latency

The C++ layer shaves roughly 25 µs off the NVMe read P99 on production hardware.

Cluster layer at a glance

cluster/      partition_map.go     consistent-hash ring (FNV-1a, 64 vnodes/node), epoch fencing
              failure_detection.go heartbeat SUSPECT → FAILED state machine
              gossip.go            TCP gossip listener + digest exchange

replication/  async / quorum / strong replication modes
              vector clocks, anti-entropy, tombstone watermarks

consensus/    Raft — leader election + log replication + snapshots

Full detail in Cluster & Consensus, including which deployment mode (standalone / raft / replicated) gives you which consistency guarantee.

Next steps