GitHub

Single-Node Benchmark Evidence — August 2026

This document records a measured, reproducible single-node comparison of ArcherDB against Valkey and PostGIS on identical hardware, using the repo’s own harnesses. Raw outputs live in reports/benchmarks/evidence-20260803/.

Headline (single node, all ArcherDB writes durable + checksummed):

System Configuration Insert throughput
ArcherDB (native client, max batches) durable, consensus-committed 831,000 events/s
ArcherDB (Python SDK, pipelined) durable, consensus-committed 659,000 events/s
Valkey 8.1 GEOADD (best of any pipelining) no persistence 174,000–202,000 ops/s
Valkey 8.1 GEOADD (appendfsync always, pipelined) durable per op 109,000 ops/s
Valkey 8.1 GEOADD (appendfsync always, sequential) durable per op 1,474 ops/s
PostGIS 16 (GIST, batched inserts) durable, no consensus 29,000–31,000 rows/s

ArcherDB’s durable, replicable write path is 4–4.8× faster than Valkey’s volatile GEOADD ceiling, 7.6× faster than Valkey configured for comparable durability, and ~27× faster than PostGIS — while also serving spatial queries with sub-millisecond p99 latency.

Test environment

  • AMD EPYC (KVM guest), 8 vCPUs, 24 GB RAM, Linux 6.8.0-124-generic.
  • Virtio disk: ~571 µs per 4 KiB O_DIRECT+O_DSYNC write (7.2 MB/s), 901 MB/s for 1 MiB durable writes. Datacenter NVMe with power-loss protection has 10–20× lower sync-write latency, which disproportionately improves ArcherDB’s small-batch and Valkey’s fsync-always numbers.
  • ArcherDB: -Drelease -Dconfig=lite, single replica, O_DIRECT + O_DSYNC WAL (two durable writes per request), Aegis-128L checksums on every message. The lite tier shares the 10 MiB message envelope with all production tiers.
  • Valkey 8.1.9: official docker image, loopback inside the container netns. Default configuration (--save '' --appendonly no, io-threads=1) unless noted; the fsync rows use appendonly yes + appendfsync always.
  • PostGIS 16-3.4: official docker image, schema + GIST index from scripts/competitor-benchmarks/setup-postgis.sh, psycopg2.execute_values batched inserts.
  • Runs are serial; each measurement waits for system load < 2.0.

ArcherDB: insert throughput vs. request batch size

Native load generator (archerdb benchmark, in-process VSR client, one request in flight per client). Every request is consensus-committed and durable on disk before its reply.

Events per request Clients Events Throughput
240 1 200K 182,586 events/s
1,000 1 500K 344,232 events/s
8,000 1 1M 620,143 events/s
8,000 8 1M 637,246 events/s
65,535 1 1M 661,747 events/s
81,916 (max) 1 1M 901,823 events/s
81,916 (max) 1 2M 830,677 events/s

Reading the curve:

  • Small requests are bounded by the durable-write floor: each request pays two serialized O_DIRECT+O_DSYNC writes (~1.2 ms on this disk) regardless of size. Batching amortizes that cost; this is the design point, not a trick — one 10 MiB request carries up to 81,916 128-byte events.
  • At large batches the single replica core becomes execute-bound (~1 µs/event: S2 cell computation, cuckoo RAM-index upsert, LSM insert).
  • Concurrent clients neither help nor hurt materially at equal event totals (8,000-event batches: 620K/s at 1 client vs 637K/s at 8) — single-node writes are intentionally sequential in the WAL for crash-recovery correctness. Sustained multi-million-event runs shed ~8% (max batches) to ~25% (8K batches) to LSM compaction pacing; reducing per-commit compaction overhead at high commit rates is tracked as follow-up work.

ArcherDB: query latency (single node, 2M events loaded)

Measured in the same run as the 2M-event insert (N5-bmax-c1.txt):

Query p50 p99
UUID point lookup 92 µs 323 µs
UUID batch (per entity) 1 µs 3 µs
Radius (1 km) 91 µs 191 µs
Polygon 87 µs 327 µs

PostGIS on the same box and dataset shape: UUID lookup 3,461/s (~289 µs each, best case, no concurrency), radius 1,508/s (~663 µs), polygon 5.6/s (~178 ms).

Python SDK

All runs: fresh single-node server, 393K–400K events, zero errors.

Shape Throughput
benchmark_geo.py defaults (8,000-event requests, sequential) 421,282 events/s
65,535-event requests, sequential 475,540 events/s
insert_events_pipelined (8,000-event requests, window 8) 659,389 events/s
same shape, strictly synchronous 316,514 events/s

Pipelining (GeoClientSync.insert_events_pipelined / NativeClient.insert_events_pipelined) overlaps client-side packing with server round trips for a 2.1× gain over synchronous submission at the same batch size.

Before the August 2026 fixes the same harness measured 59–71K events/s: it capped requests at 240 events (a stale 32 KiB assumption) and the SDK built events one ctypes field at a time. If you have older numbers on file, they measured the harness, not the server.

Valkey comparison notes

  • GEOADD is Valkey’s closest analog to an ArcherDB event insert. Its ceiling on this box is ~174–202K ops/s regardless of pipelining depth or connection count (single-threaded server, ~5 µs/op); plain SET reaches 752K ops/s, which bounds any Valkey-based design from above.
  • Default Valkey acknowledges writes from memory only. A crash loses every write since the last RDB snapshot — with snapshots disabled (the default --save '' in many deployments), the entire dataset. ArcherDB replies only after the write is fsync’d and consensus-committed.
  • With appendfsync always (the durability-comparable configuration), Valkey drops to 109K ops/s pipelined — and 1,474 ops/s for sequential clients, because each op pays the same ~0.6 ms sync-write cost that ArcherDB amortizes across up to 81,916 events per request.

Reproduction

# ArcherDB native curve (fresh temp server per run):
./zig/zig build -Drelease -Dconfig=lite
./zig-out/bin/archerdb benchmark --event-count=1000000 --event-batch-size=8000
./zig-out/bin/archerdb benchmark --event-count=2000000   # max batches

# Python SDK (server on :3001):
./zig-out/bin/archerdb format --cluster=0 --replica=0 --replica-count=1 0_0.archerdb
./zig-out/bin/archerdb start --addresses=127.0.0.1:3001 0_0.archerdb &
python3 benchmark_geo.py --events 400000 --batch-size 8000 --addresses 127.0.0.1:3001

# Valkey (docker):
docker run -d --name valkey valkey/valkey:8.1 valkey-server --save '' --appendonly no
docker exec valkey valkey-benchmark -n 2000000 -c 8 -P 64 -r 10000000 -q \
  GEOADD fleet 13.361389 38.115556 m:__rand_int__

# PostGIS (docker):
docker run -d --name postgis -e POSTGRES_USER=bench -e POSTGRES_PASSWORD=bench \
  -e POSTGRES_DB=geobench -p 127.0.0.1:5433:5432 postgis/postgis:16-3.4
# schema: scripts/competitor-benchmarks/setup-postgis.sh
python3 scripts/competitor-benchmarks/benchmark-postgis.py --port 5433 \
  --event-count 200000 --batch-size 8000 --json

Caveats: single-node lite tier on shared virtualized hardware; a background load gate (< 2.0) was enforced but the host is not dedicated. Numbers on dedicated hardware with power-loss-protected NVMe will be higher, especially for small batches. Multi-node cluster benchmarks are tracked separately (docs/BENCHMARKS.md targets: ≥770K events/s on 3 nodes).

Edit this page