Valdr
Valkey, in safe Rust. Alpha.
Single-node Valkey workalike. Safe-Rust data engine, RESP parser, and
TLS stack. No fork(), no C module ABI. The Lua VM still
wraps the upstream C interpreter — see
MLUA_EXIT_PLAN.md
for the path to replace it.
Coverage
Counted assertions ██████████ 3,015 / 3,015
Single-node-core blocks █████████░ 2,466 / 2,541 · 97%
Full upstream Valkey suite ██████░░░░ 2,466 / 4,299 · 57%
The 1,758-block gap to 4,299 is out of scope — cluster, modules, sentinel, replication-integration, platform. Not failing. Verified 2026-05-28 against Valkey 9.1.0; bucketing in docs/TEST_AND_FEATURE_COVERAGE.md.
Scope
Strings, lists, hashes, sets, sorted sets ████████████ full
Streams + consumer groups ████████████ full
Pub/Sub (regular + sharded) ████████████ full
Transactions (MULTI/EXEC/WATCH) ████████████ full
Scripting (EVAL/EVALSHA/FCALL) ████████████ via mlua
ACL, AUTH, multi-DB ████████████ full
Expiration, maxmemory eviction ████████████ full
RDB persistence ████████████ bidirectional
TLS ████████████ rustls 1.2/1.3, mTLS
AOF persistence ███████░░░░░ alpha
Replication ██████░░░░░░ alpha · partial-resync unproven
Cluster mode ░░░░░░░░░░░░ not implemented
Sentinel HA ░░░░░░░░░░░░ not implemented
Loadable C module ABI ░░░░░░░░░░░░ not implemented
Performance
GET p=1 ░░░░░ 0.95× / 0.92×
GET p=16 ▒▒▒▒▒▒▒ 1.06× / 1.12×
GET p=100 █████████ 1.32× / 1.53×
Default-suite median across 23 commands 1.13× / 1.15×
Throughput ratios vs Valkey 8.1.7 / 9.1.0, same host (Apple M3 Max), warmed.
Full per-row data (9 pipeline depths + 23 commands)
| Workload | Valdr rps | Valkey rps | Ratio |
|---|---|---|---|
| Pipeline-depth · GET / PING_MBULK / SET · p = 1 / 16 / 100 | |||
| GET p=1 | 149,925 | 157,233 | 0.954× |
| GET p=16 | 2,202,643 | 2,083,333 | 1.057× |
| GET p=100 | 5,555,556 | 4,201,680 | 1.322× |
| PING p=1 | 152,905 | 160,514 | 0.953× |
| PING p=16 | 2,267,574 | 2,232,143 | 1.016× |
| PING p=100 | 7,246,377 | 5,102,041 | 1.420× |
| SET p=1 | 150,376 | 159,236 | 0.944× |
| SET p=16 | 1,941,748 | 1,757,469 | 1.105× |
| SET p=100 | 3,610,108 | 2,695,418 | 1.339× |
| Default suite · pipeline = 100 · payload = 64 bytes | |||
| ping_inline | 5,263,158 | 3,703,704 | 1.421× |
| ping_mbulk | 7,142,857 | 5,555,556 | 1.286× |
| set | 3,703,704 | 3,030,303 | 1.222× |
| get | 4,761,905 | 3,846,154 | 1.238× |
| incr | 4,166,667 | 4,000,000 | 1.042× |
| lpush | 2,777,778 | 2,439,024 | 1.139× |
| rpush | 2,702,703 | 2,702,703 | 1.000× |
| lpop | 2,564,102 | 2,272,727 | 1.128× |
| rpop | 2,380,952 | 2,500,000 | 0.952× |
| sadd | 2,380,952 | 3,225,806 | 0.738× |
| hset | 1,724,138 | 2,500,000 | 0.690× |
| spop | 2,857,143 | 4,000,000 | 0.714× |
| zadd | 1,923,077 | 2,439,024 | 0.788× |
| zpopmin | 2,857,143 | 4,166,667 | 0.686× |
| lrange_100 | 176,367 | 129,032 | 1.367× |
| lrange_300 | 56,180 | 38,971 | 1.442× |
| lrange_500 | 34,153 | 23,175 | 1.474× |
| lrange_600 | 27,778 | 18,685 | 1.487× |
| mset | 636,943 | 456,621 | 1.395× |
| mget | 1,063,829 | 847,457 | 1.255× |
| xadd | 1,123,596 | 1,388,889 | 0.809× |
| function_load | 578,035 | 58,893 | 9.815× |
| fcall | 847,458 | 1,428,571 | 0.593× |
| Workload | Valdr rps | Valkey rps | Ratio |
|---|---|---|---|
| Pipeline-depth · GET / PING_MBULK / SET · p = 1 / 16 / 100 | |||
| GET p=1 | 161,551 | 175,747 | 0.919× |
| GET p=16 | 2,590,674 | 2,304,148 | 1.124× |
| GET p=100 | 6,024,096 | 3,937,008 | 1.530× |
| PING p=1 | 179,533 | 177,305 | 1.013× |
| PING p=16 | 2,375,297 | 2,331,002 | 1.019× |
| PING p=100 | 7,407,407 | 4,950,495 | 1.496× |
| SET p=1 | 165,016 | 176,991 | 0.932× |
| SET p=16 | 2,028,397 | 1,607,717 | 1.262× |
| SET p=100 | 3,636,363 | 2,347,418 | 1.549× |
| Default suite · pipeline = 100 · payload = 64 bytes | |||
| ping_inline | 5,263,158 | 4,000,000 | 1.316× |
| ping_mbulk | 7,142,857 | 5,555,556 | 1.286× |
| set | 3,846,154 | 2,564,102 | 1.500× |
| get | 4,545,454 | 3,448,276 | 1.318× |
| incr | 3,846,154 | 3,571,428 | 1.077× |
| lpush | 2,564,102 | 2,380,952 | 1.077× |
| rpush | 2,500,000 | 2,777,778 | 0.900× |
| lpop | 2,500,000 | 2,173,913 | 1.150× |
| rpop | 2,380,952 | 2,439,024 | 0.976× |
| sadd | 2,325,581 | 3,125,000 | 0.744× |
| hset | 1,754,386 | 2,500,000 | 0.702× |
| spop | 2,857,143 | 3,703,704 | 0.771× |
| zadd | 1,851,852 | 2,222,222 | 0.833× |
| zpopmin | 2,702,703 | 3,703,704 | 0.730× |
| lrange_100 | 186,916 | 114,943 | 1.626× |
| lrange_300 | 62,461 | 37,722 | 1.656× |
| lrange_500 | 35,137 | 21,777 | 1.613× |
| lrange_600 | 29,308 | 18,123 | 1.617× |
| mset | 628,931 | 442,478 | 1.421× |
| mget | 1,030,928 | 740,741 | 1.392× |
| xadd | 1,098,901 | 1,408,451 | 0.780× |
| function_load | 588,235 | 56,593 | 10.394× |
| fcall | 869,565 | 1,351,351 | 0.643× |
Each adversary measured in its own run; Valdr-rps differs by ~3–8% between runs (normal variance on un-quantized rps numbers).
Try it
docker pull ghcr.io/ianm199/valdr:alpha
docker run -d --name valdr -p 6379:6379 ghcr.io/ianm199/valdr:alpha
printf 'PING\r\n' | nc 127.0.0.1 6379
# +PONG
docker run --rm --network host valkey/valkey:8-alpine \
valkey-benchmark -h 127.0.0.1 -p 6379 -n 100000 -c 50 -P 100 -t get,set