One million statements, four concurrent clients, one isolated sqld database on a single-core virtual machine — and not one failure. Every statement is a live network round trip over loopback Hrana: 1,000,000 total, 0 failures, p95 6 ms.
Before the million, a sweep: 1, 4, 8, then 16 simultaneous clients, each running an identical mixed workload against the same seeded database. The headline is a wall, not a curve — sqld has a single writer thread, so aggregate throughput is flat at roughly 2,250 statements per second from one client to four. Concurrency buys queueing, not speed: p95 climbs ~5× from four clients to sixteen (6→28 ms) and ~28× from one (1→28 ms), and the box stops cooperating past that point (32 and 64 client runs collapsed — not captured in the evidence file; the separate wedge finding documented load ~56). Zero errors at every level — the server said no politely, by returning SQLITE_BUSY, and the client retried with backoff exactly as a production driver would.
| Clients | Ops / 35 s | Rate | p50 | p95 | p99 | BUSY retries | Failures |
|---|---|---|---|---|---|---|---|
| 1 | 78,018 | 2,229 /s | 0 ms | 1 ms | 3 ms | 0 | 0 |
| 4 | 78,882 | 2,254 /s | 1 ms | 6 ms | 8 ms | 7 | 0 |
| 8 | 68,790 | 1,965 /s | 2 ms | 14 ms | 22 ms | 27 | 0 |
| 16 | 47,408 | 1,354 /s | 4 ms | 28 ms | 80 ms | 44 | 0 |
| ≥ 32 | collapse — thrash, not captured in the evidence file | — | |||||
A brand-new database, seeded with 100,000 rows, then four clients hammering it with a realistic serverless OLTP mix — 12% single-row inserts, 8% point updates, 35% indexed point reads, 25% range counts, 20% top-N reads — until exactly 1,000,000 statements had been served. The wall from the sweep was the write-heavy part; at this mix the same wall stands far away. Throughput held a mean of 1,500 /s for eleven minutes, and p99 held at 11 ms for the entire run.
870 – 1,872 /s per second; flat until the very end.
99% of a million statements answered in 11 ms or less.
26 BUSY retries absorbed by the client; none surfaced.
RSS at peak; host never above 357 MB of 969 MB.
Statements completed per second across the 666-second run — 4 clients, one sqld process, one core. The dashed amber line is the mean, 1,500/s.
| Latency | p50 | p95 | p99 | max |
|---|---|---|---|---|
| milliseconds | 2 | 6 | 11 | 269 |
sqld serializes writes through one writer thread — exactly as SQLite itself does, only across a socket. The sweep’s flat ceiling (~2,250/s at 1–4 clients) is that serialization on one core. The practical law it implies: per-statement round trips are the currency, so batch your writes. In the companion speedtest replication, 1,000-row batches cut serverless overhead from 20× to 3.4×.
A sustained write-heavy run (30% writes, retry storms, growing table) left the benchmark sqld process busy-spinning at 100% CPU with zero clients attached — load 56 on one core, machine otherwise idle. Killing the process ended it instantly; the read-heavy million ran eleven minutes without a trace of it. This is a sqld behavior worth knowing before you run heavy write concurrency against a solo node: monitor per-database CPU and batch aggressively.
The sweep and the million used one sqlitend-created database each, one minted full-access token, @libsql/client 0.18.0 over Hrana WebSocket to ws://127.0.0.1 — loopback, so no real network in the numbers. Both databases were seeded with 100,000 rows. The sweep mix was write-heavy (30%); the million mix read-heavy (20% writes) and is labeled as such — rates across the two are not directly comparable. Latency was bucketed at 50µs resolution; client-visible latency includes retry backoff. Each run was a fixed-quota batch, so the million is exactly 1,000,000 statements — every one counted.
One core, one gigabyte, loopback — absolute rates are box-relative; the shape (flat writer ceiling, calm read tail) is the portable lesson. The write-heavy sweep thrashes at ≥32 clients and is reported as such, not hidden. The sqld busy-spin was observed once and mitigated; it deserves a dedicated investigation before claiming write-heavy HA. A real network, TLS, and more cores would all move these numbers and are the obvious follow-up. The control plane has no login in v1: firewall or reverse-proxy it in production.