Durable acks, Measured.
How fast can each system ack a write that survives power loss? 5 systems, 2 disks, 7 runs each.
Method and setup
- Every client keeps 1 request in flight. Every system acks only after fsync.
- Closed loop: each writer sends its next append as soon as the last one is acked.
- Agent-shaped: each writer sleeps 0 to 2x the think time before every append, seeded by writer index.
- Run on 2026-10-24 on an Azure Standard_L8s_v3 (8 vCPU) with Linux 7.0 and ext4.
- Two disks: the VM's local NVMe, and a 128 GiB Premium SSD v2 at 3,000 IOPS and 125 MB/s.
- Closed loop runs 5,000 appends of 256 bytes per writer at 1, 4 and 16 writers.
- Agent-shaped load runs 64 writers with 5 ms think, and 256 writers with 50 ms think.
- Each cell is the median of 7 interleaved reps. Every run starts on a fresh data directory.
- AtticDB commits groups and acks after fdatasync. Redis 8.10.2, Valkey 9.1.2 and DiceDB 1.0.0 use appendfsync always.
- SQLite 3.45.1 runs in process with WAL and synchronous=FULL, 1 row per transaction.
- Dragonfly has no append-only file, so it cannot ack a write after fsync.
Premium SSD v2
| Writers | System | Acks/s | p50 µs | p99 µs | p99 range µs | p99.9 µs | max µs |
|---|---|---|---|---|---|---|---|
| 1 | atticdb | 1,167 | 817 | 1,514 | 1,509 to 1,636 | 2,097 | 2,590 |
| 1 | redis | 466 | 2,136 | 3,231 | 3,079 to 3,253 | 4,246 | 11,650 |
| 1 | valkey | 472 | 2,131 | 3,214 | 3,057 to 3,278 | 4,215 | 8,883 |
| 1 | dicedb | 473 | 2,128 | 3,204 | 3,133 to 3,250 | 4,222 | 7,982 |
| 1 | sqlite | 1,143 | 735 | 2,365 | 2,253 to 2,479 | 3,029 | 4,763 |
| 4 | atticdb | 2,619 | 1,473 | 2,366 | 2,246 to 2,404 | 2,875 | 4,026 |
| 4 | redis | 1,022 | 3,972 | 5,568 | 5,436 to 5,646 | 6,945 | 13,518 |
| 4 | valkey | 936 | 4,255 | 5,622 | 5,467 to 5,738 | 7,943 | 15,345 |
| 4 | dicedb | 932 | 4,273 | 5,691 | 5,418 to 5,810 | 8,424 | 16,121 |
| 4 | sqlite | 1,342 | 704 | 1,960 | 1,807 to 1,993 | 2,716 | 9,944,485 |
| 16 | atticdb | 9,328 | 1,618 | 2,734 | 2,394 to 2,937 | 3,323 | 4,173 |
| 16 | redis | 5,151 | 3,292 | 5,195 | 5,016 to 5,582 | 7,465 | 16,279 |
| 16 | valkey | 5,096 | 3,185 | 5,161 | 5,038 to 5,634 | 6,327 | 25,764 |
| 16 | dicedb | 5,086 | 3,513 | 5,063 | 4,988 to 5,785 | 6,072 | 11,252 |
| 16 | sqlite | 1,421 | 704 | 1,696 | 1,639 to 1,839 | 3,098 | 46,787,988 |
| Load | System | Acks/s | p50 µs | p99 µs | p99 range µs | p99.9 µs | max µs |
|---|---|---|---|---|---|---|---|
| 64 writers, 5 ms | atticdb | 9,664 | 1,286 | 2,246 | 2,194 to 2,380 | 2,764 | 6,213 |
| 64 writers, 5 ms | redis | 7,779 | 2,916 | 4,693 | 4,526 to 5,059 | 6,545 | 13,334 |
| 64 writers, 5 ms | valkey | 7,796 | 2,901 | 4,597 | 4,442 to 5,139 | 6,992 | 11,483 |
| 64 writers, 5 ms | dicedb | 7,840 | 2,807 | 4,592 | 4,424 to 4,968 | 6,998 | 11,928 |
| 64 writers, 5 ms | sqlite | 1,105 | 3,860 | 830,031 | 829,880 to 830,422 | 1,730,934 | 3,731,498 |
| 256 writers, 50 ms | atticdb | 4,270 | 1,284 | 2,259 | 2,184 to 2,392 | 2,827 | 5,302 |
| 256 writers, 50 ms | redis | 4,151 | 3,070 | 4,955 | 4,700 to 5,433 | 12,560 | 33,249 |
| 256 writers, 50 ms | valkey | 4,154 | 3,072 | 4,860 | 4,694 to 9,744 | 8,071 | 28,153 |
| 256 writers, 50 ms | dicedb | 4,149 | 3,065 | 4,985 | 4,590 to 5,365 | 9,384 | 27,769 |
| 256 writers, 50 ms | sqlite | 1,038 | 9,405 | 2,130,899 | 1,830,926 to 2,131,252 | 3,732,598 | 6,534,968 |
AtticDB beats Redis, Valkey and DiceDB in every cell on this disk. It acks 1.8x to 2.8x as many writes in closed loop.
In every run its worst p99 rep beats their best rep. Under agent load its p50 is 1.3 ms against 2.8 to 3.1 ms.
In closed loop SQLite has a lower p50 at every writer count. Its p99 and p99.9 are lower at 4 and 16 writers.
Local NVMe
| Writers | System | Acks/s | p50 µs | p99 µs | p99 range µs | p99.9 µs | max µs |
|---|---|---|---|---|---|---|---|
| 1 | atticdb | 9,799 | 94 | 128 | 113 to 320 | 145 | 300 |
| 1 | redis | 8,110 | 116 | 184 | 171 to 708 | 216 | 354 |
| 1 | valkey | 8,434 | 108 | 172 | 166 to 602 | 212 | 330 |
| 1 | dicedb | 8,436 | 110 | 179 | 166 to 186 | 214 | 344 |
| 1 | sqlite | 19,410 | 32 | 157 | 152 to 170 | 411 | 736 |
| 4 | atticdb | 24,516 | 146 | 214 | 213 to 218 | 251 | 438 |
| 4 | redis | 17,504 | 221 | 304 | 296 to 317 | 348 | 532 |
| 4 | valkey | 18,444 | 202 | 298 | 286 to 324 | 344 | 474 |
| 4 | dicedb | 18,149 | 208 | 310 | 298 to 327 | 361 | 467 |
| 4 | sqlite | 22,162 | 32 | 143 | 138 to 160 | 622 | 729,400 |
| 16 | atticdb | 48,611 | 305 | 502 | 493 to 519 | 674 | 1,210 |
| 16 | redis | 44,572 | 299 | 549 | 543 to 564 | 604 | 932 |
| 16 | valkey | 42,928 | 311 | 573 | 546 to 580 | 624 | 810 |
| 16 | dicedb | 44,693 | 300 | 547 | 537 to 572 | 609 | 947 |
| 16 | sqlite | 24,936 | 32 | 124 | 123 to 128 | 648 | 2,831,276 |
| Load | System | Acks/s | p50 µs | p99 µs | p99 range µs | p99.9 µs | max µs |
|---|---|---|---|---|---|---|---|
| 64 writers, 5 ms | atticdb | 11,687 | 148 | 416 | 273 to 521 | 786 | 4,871 |
| 64 writers, 5 ms | redis | 11,648 | 183 | 382 | 336 to 567 | 964 | 5,317 |
| 64 writers, 5 ms | valkey | 11,641 | 184 | 506 | 341 to 564 | 1,053 | 6,766 |
| 64 writers, 5 ms | dicedb | 11,644 | 182 | 504 | 334 to 547 | 1,029 | 5,292 |
| 64 writers, 5 ms | sqlite | 8,531 | 57 | 33,381 | 33,367 to 33,414 | 103,539 | 428,896 |
| 256 writers, 50 ms | atticdb | 4,351 | 138 | 558 | 225 to 636 | 736 | 8,698 |
| 256 writers, 50 ms | redis | 4,351 | 145 | 612 | 292 to 752 | 2,185 | 7,625 |
| 256 writers, 50 ms | valkey | 4,352 | 142 | 496 | 293 to 1,318 | 2,758 | 8,039 |
| 256 writers, 50 ms | dicedb | 4,352 | 140 | 348 | 289 to 1,078 | 2,103 | 7,392 |
| 256 writers, 50 ms | sqlite | 4,242 | 68 | 18,489 | 18,354 to 18,732 | 103,782 | 329,060 |
In closed loop AtticDB beats Redis, Valkey and DiceDB on every median at 1 and 4 writers.
At 16 writers it beats them on acks/s and p99 only: 48,611/s and 502 µs. Redis and DiceDB have a lower p50: 299 and 300 against 305 µs.
AtticDB's p99.9 and max at 16 writers are 674 and 1,210 µs against 604 to 947 µs.
Under agent load AtticDB has the best p50 and p99.9 of the 4 servers. Its p99 median trails Redis at 64 writers, and trails DiceDB and Valkey at 256 writers.
Where we lose
- SQLite has a lower p50 in closed loop at every writer count on both disks.
- SQLite runs in the writer's process with no socket. On NVMe with 1 writer SQLite gets 19,410 acks/s and a 32 µs p50 against AtticDB's 9,799 and 94 µs.
- On Premium SSD v2 in closed loop, SQLite has a lower p99 and p99.9 at 4 and 16 writers.
- On NVMe in closed loop, SQLite keeps a lower p50 and p99 at 4 and 16 writers.
- On NVMe its p99.9 is lower at 16 writers, and its agent-load p50 is 57 and 68 µs.
- On NVMe at 16 closed-loop writers, Redis and DiceDB beat AtticDB on p50, p99.9 and max latency.
- On NVMe under agent load, Redis has a lower p99 at 64 writers, and DiceDB and Valkey at 256.