DEV Community

Alex Georgiev
Alex Georgiev

Posted on AI-assisted

Valkey 9.2's new rdbcompression lz4 setting cuts a 1M-row RDB from 178MB to 35MB

I set rdbcompression lz4 on a Valkey 9.2.0-rc1 primary, watched a replica do a full sync, and measured exactly the same number of bytes going over the wire as with compression off. For a few minutes I believed the feature didn't touch replication at all. It does. I just had the wrong setting.

Valkey 9.2.0-rc1, released as a release candidate on 16 September with GA planned for mid-November, adds rdbcompression lz4: whole-stream LZ4 compression for RDB files. Until now, RDB persistence has compressed each string value separately with LZF, a scheme the project's own issue tracker describes as "quite inefficient" because it can only find repetition inside one value, never across the file. I ran both schemes against three kinds of data and then went looking for where the new setting falls over.

The headline number

I loaded a million keys shaped like a typical user-profile hash, each a small JSON document with a handful of repeated field names and a handful of unique values, and ran SAVE three times under each compression setting.

rdbcompression file size SAVE time (best of 3)
no 180.3 MB 0.80s
yes (default, per-string LZF) 177.6 MB 1.10s
lz4 35.4 MB 1.16s

The default LZF setting barely moves the needle: 180.3MB down to 177.6MB, a 1.5% saving, for the CPU cost of running compression at all. lz4 takes the same file down to 35.4MB, an 80.1% cut against the default and 80.4% against no compression whatsoever, for about 6% more CPU time than LZF already costs. I reran it on a second, more repetitive dataset — 200,000 synthetic log lines with a shared template — and the gap was the same shape: 31.47MB uncompressed and under LZF, 5.91MB with lz4, an 81.2% cut.

The reason LZF does almost nothing here is exactly the limitation the project describes: each JSON document is 150-200 bytes, with no exploitable repetition inside itself, and LZF never looks past one value's boundary. Whole-stream LZ4 compresses the serialized file as one byte stream, so the repeated "active":true, "roles":["user","member"] and field names across a million records are exactly the kind of redundancy it picks up and LZF structurally cannot.

Where it doesn't help

I tried the same experiment on 200,000 keys holding 32-byte random hex tokens, the kind of value you'd get from session IDs or API keys. There is no cross-record redundancy to find, and it showed:

rdbcompression file size SAVE time (best of 3)
no 15.49 MB 0.186s
yes 15.49 MB 0.211s
lz4 14.76 MB 0.283s

A 4.7% size reduction for roughly 35% more wall-clock time than LZF, and 52% more than no compression at all. On a database that's mostly tokens, counters or anything that already looks random, whole-stream LZ4 is close to pure overhead. The win is entirely a property of the data, not the algorithm being generically better than LZF — it just has a wider window to search in.

What I got wrong on the way

My first attempt at measuring the replication effect gave a flat, suspicious result: identical replica-side byte counts under rdbcompression no, yes and lz4, down to a few kilobytes of jitter. I assumed the flag simply wasn't wired into the sync path yet, since this is a release candidate.

Before concluding that, I checked whether my measurement itself was broken, by re-running a plain SAVE on disk with the same dataset and the same setting. That still showed the expected 80% drop. So the instrumentation was fine; the behavior genuinely differed between a local save and a replication sync. Looking at the primary's log line during the sync was the actual answer: it read Starting BGSAVE for SYNC with target: disk using: normal sync, with no mention of compression at all. Setting rdbcompression lz4 on the primary had done nothing for the replication path.

The fix is a second setting, repl-compression, which controls the replication stream independently of the on-disk RDB format. Once I set repl-compression lz4 on the primary, the exact same sync produced this log line instead:

7:M 25 Oct 2026 05:12:15.412 * Disk-based full sync with compression: lz4
7:M 25 Oct 2026 05:12:15.587 * Replication compression enabled for replica 172.17.0.4:6379 (algo=lz4)
Enter fullscreen mode Exit fullscreen mode

and the replica's actual received bytes dropped from 17,627,802 to 3,435,304 — an 80.5% cut, in line with the file-based number. rdbcompression lz4 and repl-compression lz4 are two separate flags controlling two separate code paths, and the release note text I found for this change talks only about the first one. Setting only rdbcompression lz4 on a primary that replicates will leave every full sync exactly as large as before, silently, with no warning anywhere.

Mixed fleets cost more than one BGSAVE

I wanted to know what happens when an old replica and a compression-aware one both attach around the same time. I started a Valkey 8.1 replica and a 9.2.0-rc1 replica configured with repl-compression lz4 against the same primary within the same second. The legacy replica synced cleanly — all million keys landed, master_link_status:up, no errors — so the fallback to plaintext for a replica that can't speak the new protocol works as I'd want. But the primary's log showed two separate BGSAVE passes, not one shared fork:

7:M * Current BGSAVE has socket target. Waiting for next BGSAVE for SYNC
7:M * Diskless rdb transfer, done reading from pipe, 1 replicas still up.
7:M * Streamed RDB transfer with replica 172.17.0.5:6379 succeeded (socket)...
7:M * Starting BGSAVE for SYNC with target: disk using: normal sync
7:M * Disk-based full sync with compression: lz4
Enter fullscreen mode Exit fullscreen mode

A plain diskless, uncompressed save served the 8.1 replica, then a second, disk-based, LZ4-compressed save ran immediately after to serve the 9.2 one. A fleet with a mix of old and new replicas pays for a full extra fork-and-save cycle per sync generation rather than sharing one, which matters on a large dataset where a single BGSAVE already costs real CPU and I/O.

Watching it from outside

I expected INFO persistence or INFO replication to expose which algorithm was actually used, the way rdb_last_bgsave_status or master_link_status do. Neither field exists. I checked both sections after every save and sync in this post and found nothing: no rdb_compression, no repl_compression_algo, nothing under INFO ALL either. The only record of which compression ran is the primary's own log line, and that line only appears for a replica that negotiated compression — a plaintext fallback sync produces a log entry identical to one with no compression configured at all. If you want to confirm in production whether a given replica actually got a compressed sync, you grep the primary's log for algo= at the moment that replica attached. There's no way to ask the running server after the fact.

Run it yourself

This needs Docker and roughly 400MB of free disk for the images and test data.

docker pull valkey/valkey:9.2.0-rc1
docker run -d --name vk -v "$(pwd)/vk-data:/data" valkey/valkey:9.2.0-rc1 --save ""

# load ~1M small JSON-shaped values
python3 - <<'EOF'
import subprocess, random
def feed(cmds):
    subprocess.run(['docker', 'exec', '-i', 'vk', 'valkey-cli', '--pipe'],
                    input=cmds.encode(), capture_output=True)
batch = []
for i in range(1_000_000):
    val = '{"id":%d,"name":"user%d","active":true,"roles":["user","member"]}' % (i, i)
    batch.append(f"SET user:{i} '{val}'")
    if len(batch) >= 200_000:
        feed('\n'.join(batch) + '\n'); batch = []
if batch:
    feed('\n'.join(batch) + '\n')
EOF

for c in no yes lz4; do
  docker exec vk valkey-cli config set rdbcompression "$c" > /dev/null
  docker exec vk rm -f /data/dump.rdb
  time docker exec vk valkey-cli save > /dev/null
  echo "$c: $(docker exec vk stat -c%s /data/dump.rdb) bytes"
done

# replication needs a second setting, not just rdbcompression:
docker exec vk valkey-cli config set repl-compression lz4
Enter fullscreen mode Exit fullscreen mode

If you run this on a dataset you actually care about, check your own ratio before assuming it'll be 80%. The size I got is a property of how repetitive your values are across records, not a fixed number this version of Valkey guarantees.

What to do with this

If you're on Valkey and most of your values are small, structured and repeat a lot of their shape across keys — session hashes, user profiles, event records — rdbcompression lz4 is worth turning on for the RDB file alone; the CPU cost over the existing default is small next to an 80% size cut on disk and in backups. If you also replicate, set repl-compression lz4 on every primary explicitly and don't assume the RDB setting covers it — check the primary's log for algo=lz4 on the next full sync rather than trusting the config to have taken effect. And if your values are already high-entropy, measure your own data before turning anything on, because the win there was close to nothing in my tests.

Top comments (2)

Collapse
 
mrsaynothing profile image
Mr Say Nothing •

The 4.7%-on-random-tokens table is the one worth framing: compression gains are a property of the data, not the algorithm. The two-flag split deserves the loudest note though — the release note names rdbcompression, the replication path only moves for repl-compression, and nothing warns when you set one and needed both. That is the changelog-as-config trap in miniature: the note told the truth and still was not the whole truth. 80% off full-sync bytes on a fleet that re-syncs replicas nightly is real; 4.7% smaller for 52% more CPU on a token store is a loss. Measure both, keep one.

Collapse
 
alexgeorgiev17 profile image
Alex Georgiev •

Agree on the token table, that one surprised me less than the JSON one but it's the more useful warning.

On the two-flag thing though, I have to walk part of the post back, and your comment is what made me go reread the notes. The 9.2.0-rc1 release notes do list repl-compression, it's under Cluster and Replication as LZ4 compression for steady-state replication (#3853). I only read the rdbcompression entry and missed it, so "undocumented in the release notes" is just wrong.

The more awkward part is the entry right next to it (#4075) says the full-sync RDB payload gets compressed with LZ4 when rdbcompression lz4 is set and every attaching replica advertises support. That's exactly the case where I saw identical bytes and a log line with no compression at all. So either rc1 doesn't do what that entry says, or something in my setup made the primary think a replica didn't support it, and "every attaching replica" makes me suspect the second. Either way the "you need both flags" conclusion isn't something I can stand behind until I rerun it with only rdbcompression lz4 and one 9.2 replica attached.

Small thing on the numbers, the 52% is wall-clock SAVE time, not CPU, I didn't measure CPU separately.

I'll rerun the sync test and fix the post either way.