Streaming Compression support for RDB#3531
Conversation
c77cc98 to
616d763
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## unstable #3531 +/- ##
============================================
+ Coverage 76.68% 77.13% +0.45%
============================================
Files 162 167 +5
Lines 81021 82890 +1869
============================================
+ Hits 62129 63938 +1809
- Misses 18892 18952 +60
🚀 New features to boost your workflow:
|
a241fd7 to
6af049a
Compare
❌ Provenance Check AlertPotential code similarities detected with upstream repository.
This check was performed automatically by the Provenance Guard Action. |
xdk-amz
left a comment
There was a problem hiding this comment.
Overall looks good but recommend separating RDB checksum concerns from compression checksum concerns and letting operators decide what combination they want instead of mixing the handling between the two modules. I would like to see some exploration on the lz4 configs I commented on as well as some defensive fixes to potentially unsafe code + other nits.
|
Benchmarks with using RDB checksum as is over logical RDB Stream: LZ4@0 Save (ms): CRC over logical stream vs LZ4 Block Checksum only over 1M Keys:
LZ4@0 Load (ms):
|
b9a5055 to
750a667
Compare
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds public streaming-compression API ( ChangesStreaming compression public headers
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/aof.c (1)
1009-1054:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPlease request
@core-teamarchitectural review for thisaof.cbehavior change.This changes AOF/RDB sync-restart behavior (including fallback policy), which should get explicit core-team architectural sign-off before merge.
As per coding guidelines, "Request
@core-teamarchitectural review for changes to cluster*.c, replication.c, rdb.c, or aof.c".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/aof.c` around lines 1009 - 1054, The change in aof.c modifies AOF/RDB sync-restart behavior (in functions restartAOFWithSyncRdb and rdbFileUsesStreamingCompression) and the fallback policy when a streamed-compressed RDB is detected; request an explicit architectural review from `@core-team` before merging by adding a review request or TODO in the PR and code comments referencing restartAOFWithSyncRdb and rdbFileUsesStreamingCompression so core-team can approve the behavioral change, and ensure the PR description summarizes the new fallback logic (fall back to BGREWRITEAOF when streaming compression is detected) for their sign-off.src/rdb.c (1)
3689-3695:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winDifferentiate VKCS loads from checksum-disabled RDBs in this notice.
RIO_FLAG_SKIP_RDB_CHECKSUMnow also covers the streaming-compressed path, so this branch will log that checksums were disabled even when integrity is coming from the codec layer. That makes load diagnostics misleading.Suggested fix
- if (rdb->flags & RIO_FLAG_SKIP_RDB_CHECKSUM) { - serverLog(LL_NOTICE, "RDB file was saved with checksum disabled: skipped checksum for this transfer"); + if ((rdb->flags & RIO_FLAG_STREAMING_COMPRESSION) && + (rdb->flags & RIO_FLAG_SKIP_RDB_CHECKSUM)) { + serverLog(LL_NOTICE, + "Skipping logical RDB checksum for streaming-compressed input"); + } else if (rdb->flags & RIO_FLAG_SKIP_RDB_CHECKSUM) { + serverLog(LL_NOTICE, + "RDB file was saved with checksum disabled: " + "skipped checksum for this transfer"); } else if (cksum == 0) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/rdb.c` around lines 3689 - 3695, The notice incorrectly attributes checksum skipping to RIO_FLAG_SKIP_RDB_CHECKSUM even for streaming-compressed (VKCS) loads; update the branch in rdb.c (where server.rdb_checksum, server.skip_checksum_validation, memrev64ifbe, rdb->flags and RIO_FLAG_SKIP_RDB_CHECKSUM are handled) to detect when the input is coming from a streaming-compressed/codec-backed path (e.g., check the RIO or codec metadata on rdb to see if a VKCS/streaming codec provided integrity) and emit a distinct serverLog message for that case versus a true checksum-disabled RDB; keep the existing behavior for cksum==0 and cksum!=expected, but split the RIO_FLAG_SKIP_RDB_CHECKSUM branch into two paths (codec-backed integrity vs. checksum-disabled file) with appropriate, different log text.
🧹 Nitpick comments (3)
src/compression_lz4.h (1)
12-28: ⚡ Quick winAdd API contract comments for exported LZ4 entry points.
These declarations are public-facing but currently undocumented at function level (ownership, error model, buffer/input-consumed guarantees). Please add concise contract comments to prevent misuse.
As per coding guidelines "Document why code exists, not just what it does; document all functions in C code".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/compression_lz4.h` around lines 12 - 28, Add concise API contract comments above each exported LZ4 function declaration (compressionLz4CompressorInit, compressionLz4CompressorDestroy, compressionLz4DecompressorInit, compressionLz4DecompressorDestroy, compressionLz4OutputBound, compressionLz4CompressFeed, compressionLz4DecompressFeed) that describe ownership semantics (who allocates/frees streamCompressor/streamDecompressor and buffer pointers), the error model and possible return values (success codes vs negative error codes), guarantees about how much output will be produced or bounded (for compressionLz4OutputBound and CompressFeed), and for decompression functions explicitly state how input_consumed is set and when callers must retry vs drop input; keep each comment short, consistent, and focused on why the function contract exists (caller responsibilities, lifetime, and failure behavior).deps/lz4/Makefile (1)
26-28: ⚡ Quick winDeclare
cleanas phony.If a file named
cleanappears in this directory,make cleancan silently no-op. Add.PHONY: cleanfor reliable cleanup.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@deps/lz4/Makefile` around lines 26 - 28, The Makefile target "clean" should be declared phony to avoid being skipped if a file named "clean" exists; update the Makefile by adding a .PHONY declaration for the clean target (e.g., add ".PHONY: clean") so that the clean rule (which removes *.o and *.a) always runs.src/rdb.c (1)
3150-3197: ⚡ Quick winMake the new
rdbInputStream*helpers file-local.These helpers are only referenced inside
src/rdb.c, so exporting them widens the symbol surface for no gain.Suggested fix
-void rdbInputStreamInit(rdbInputStream *input, rio *raw_rio) { +static void rdbInputStreamInit(rdbInputStream *input, rio *raw_rio) { @@ -decompressRioInitResult rdbInputStreamPrepare(rdbInputStream *input) { +static decompressRioInitResult rdbInputStreamPrepare(rdbInputStream *input) { @@ -void rdbInputStreamDestroy(rdbInputStream *input) { +static void rdbInputStreamDestroy(rdbInputStream *input) { @@ -int rdbInputStreamValidateEnd(rdbInputStream *input) { +static int rdbInputStreamValidateEnd(rdbInputStream *input) { @@ -bool rdbRioHasCorruptCompressedInput(const rio *rdb) { +static bool rdbRioHasCorruptCompressedInput(const rio *rdb) {As per coding guidelines, "Use static keyword for file-local functions in C code"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/rdb.c` around lines 3150 - 3197, Mark the helper functions as file-local by adding the static storage specifier to their definitions: rdbInputStreamInit, rdbInputStreamPrepare, rdbInputStreamDestroy, rdbInputStreamValidateEnd, and rdbRioHasCorruptCompressedInput. Leave the rdbInputStream type and any external declarations untouched; only change the function declarations/definitions to static so these helpers are not exported from the object file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@deps/lz4/lz4frame.c`:
- Around line 1619-1621: LZ4F_decompress performs pointer arithmetic on
srcBuffer without checking for NULL; add a guard at the start of LZ4F_decompress
to handle srcBuffer == NULL (or *srcSizePtr == 0) before computing
srcStart/srcEnd/srcPtr so you never do NULL + offset arithmetic; if srcBuffer is
NULL or *srcSizePtr == 0, initialize srcStart/srcEnd/srcPtr to a safe
non-dereferenced value (or skip computing srcEnd) and ensure subsequent logic
(e.g., the frame parsing path exercised by LZ4F_getFrameInfo calling
LZ4F_decompress with NULL/0) treats this as an empty input rather than
performing pointer arithmetic or dereferencing.
In `@src/compression_rio.c`:
- Around line 89-100: compressRioFlush and compressRioFinish currently mark the
stream writer errored but do not set the rio-level write error flag, so update
both functions (compressRioFlush and compressRioFinish) to set
RIO_FLAG_WRITE_ERROR on the rio when any flush/finalize failure occurs;
specifically, when streamWriterIsErrored(cr->writer) is detected, when
streamWriterFlush(cr->writer) != 0, and when
cr->inner->flush/cr->inner->finalize returns 0, set the rio flag (e.g., r->flags
|= RIO_FLAG_WRITE_ERROR or call the existing helper to mark rio write error) in
addition to calling streamWriterSetError(cr->writer) so callers relying on the
rio flag see the error.
---
Outside diff comments:
In `@src/aof.c`:
- Around line 1009-1054: The change in aof.c modifies AOF/RDB sync-restart
behavior (in functions restartAOFWithSyncRdb and
rdbFileUsesStreamingCompression) and the fallback policy when a
streamed-compressed RDB is detected; request an explicit architectural review
from `@core-team` before merging by adding a review request or TODO in the PR and
code comments referencing restartAOFWithSyncRdb and
rdbFileUsesStreamingCompression so core-team can approve the behavioral change,
and ensure the PR description summarizes the new fallback logic (fall back to
BGREWRITEAOF when streaming compression is detected) for their sign-off.
In `@src/rdb.c`:
- Around line 3689-3695: The notice incorrectly attributes checksum skipping to
RIO_FLAG_SKIP_RDB_CHECKSUM even for streaming-compressed (VKCS) loads; update
the branch in rdb.c (where server.rdb_checksum, server.skip_checksum_validation,
memrev64ifbe, rdb->flags and RIO_FLAG_SKIP_RDB_CHECKSUM are handled) to detect
when the input is coming from a streaming-compressed/codec-backed path (e.g.,
check the RIO or codec metadata on rdb to see if a VKCS/streaming codec provided
integrity) and emit a distinct serverLog message for that case versus a true
checksum-disabled RDB; keep the existing behavior for cksum==0 and
cksum!=expected, but split the RIO_FLAG_SKIP_RDB_CHECKSUM branch into two paths
(codec-backed integrity vs. checksum-disabled file) with appropriate, different
log text.
---
Nitpick comments:
In `@deps/lz4/Makefile`:
- Around line 26-28: The Makefile target "clean" should be declared phony to
avoid being skipped if a file named "clean" exists; update the Makefile by
adding a .PHONY declaration for the clean target (e.g., add ".PHONY: clean") so
that the clean rule (which removes *.o and *.a) always runs.
In `@src/compression_lz4.h`:
- Around line 12-28: Add concise API contract comments above each exported LZ4
function declaration (compressionLz4CompressorInit,
compressionLz4CompressorDestroy, compressionLz4DecompressorInit,
compressionLz4DecompressorDestroy, compressionLz4OutputBound,
compressionLz4CompressFeed, compressionLz4DecompressFeed) that describe
ownership semantics (who allocates/frees streamCompressor/streamDecompressor and
buffer pointers), the error model and possible return values (success codes vs
negative error codes), guarantees about how much output will be produced or
bounded (for compressionLz4OutputBound and CompressFeed), and for decompression
functions explicitly state how input_consumed is set and when callers must retry
vs drop input; keep each comment short, consistent, and focused on why the
function contract exists (caller responsibilities, lifetime, and failure
behavior).
In `@src/rdb.c`:
- Around line 3150-3197: Mark the helper functions as file-local by adding the
static storage specifier to their definitions: rdbInputStreamInit,
rdbInputStreamPrepare, rdbInputStreamDestroy, rdbInputStreamValidateEnd, and
rdbRioHasCorruptCompressedInput. Leave the rdbInputStream type and any external
declarations untouched; only change the function declarations/definitions to
static so these helpers are not exported from the object file.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: a47a8b43-59a8-4016-a872-af1fde59642c
📒 Files selected for processing (40)
cmake/Modules/SourceFiles.cmakecmake/Modules/ValkeySetup.cmakedeps/CMakeLists.txtdeps/Makefiledeps/lz4/CMakeLists.txtdeps/lz4/LICENSEdeps/lz4/Makefiledeps/lz4/lz4.cdeps/lz4/lz4.hdeps/lz4/lz4frame.cdeps/lz4/lz4frame.hdeps/lz4/lz4hc.cdeps/lz4/lz4hc.hdeps/lz4/xxhash.cdeps/lz4/xxhash.hsrc/CMakeLists.txtsrc/Makefilesrc/aof.csrc/compression.csrc/compression.hsrc/compression_lz4.csrc/compression_lz4.hsrc/compression_rio.csrc/compression_rio.hsrc/compression_stream.csrc/compression_stream.hsrc/config.csrc/rdb.csrc/rdb.hsrc/rio.csrc/rio.hsrc/server.hsrc/unit/CMakeLists.txtsrc/unit/Makefilesrc/unit/test_compression.cppsrc/valkey-check-rdb.ctests/integration/rdb-compression.tcltests/integration/replication-aof-sync.tcltests/integration/valkey-check-rdb.tclvalkey.conf
Add streaming LZ4-backed RDB compression with rio decorators, stream envelope handling, integration changes, and the follow-up fixes and config cleanup needed on top of unstable. Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
- Remove dead code: rdbIsValidMagic() and unused #include <string.h> in rdb.h - Remove redundant first RIO_FLAG_SKIP_RDB_CHECKSUM set in rdbSaveInternal - Remove unrelated changes: config_parse_depth, USE_FAST_FLOAT, write-make-settings - Validate full 8-byte VKCS envelope in aof.c rdbFileUsesStreamingCompression - Add SAFETY comment for rdbRioHasCorruptCompressedInput cast invariant - Rename all snake_case identifiers to camelCase per Valkey conventions: types (compression_algo_t -> compressionAlgo, stream_compressor_t -> streamCompressor, compress_rio_t -> compressRio, etc.), functions (stream_writer_create -> streamWriterCreate, compress_rio_finish -> compressRioFinish, write_vkcs_envelope -> writeVkcsEnvelope, etc.), and static variables (compression_lz4_codec_impl -> compressionLz4CodecImpl) - Drop _t suffix from all types to match Valkey convention - Fix typo: streamWriterIsErrord -> streamWriterIsErrored - Replace silent dummy buffer allocation with assert(needed > 0) in streamWriterEnsureOutBuf Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
* Address streaming RDB compression review * Skip RDB CRC for streaming compression * Remove brittle 32-bit compression unit test --------- Co-authored-by: Sarthak Aggarwal <sarthagg@amazon.com> Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
--------- Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
ae21623 to
2d6bfae
Compare
Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
2d6bfae to
b494e19
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@deps/lz4/Makefile`:
- Around line 26-28: The Makefile's clean target (target name "clean") should be
declared phony to avoid being skipped if a file named "clean" exists; add a
.PHONY declaration for the clean target (e.g., add a line declaring .PHONY:
clean) near the top or before the clean target so make always runs the clean
recipe (rm -f *.o and rm -f *.a).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: ebf9c0e3-08ee-4c87-a906-6a8e31eb4a87
📒 Files selected for processing (40)
cmake/Modules/SourceFiles.cmakecmake/Modules/ValkeySetup.cmakedeps/CMakeLists.txtdeps/Makefiledeps/lz4/CMakeLists.txtdeps/lz4/LICENSEdeps/lz4/Makefiledeps/lz4/lz4.cdeps/lz4/lz4.hdeps/lz4/lz4frame.cdeps/lz4/lz4frame.hdeps/lz4/lz4hc.cdeps/lz4/lz4hc.hdeps/lz4/xxhash.cdeps/lz4/xxhash.hsrc/CMakeLists.txtsrc/Makefilesrc/aof.csrc/compression.csrc/compression.hsrc/compression_lz4.csrc/compression_lz4.hsrc/compression_rio.csrc/compression_rio.hsrc/compression_stream.csrc/compression_stream.hsrc/config.csrc/rdb.csrc/rdb.hsrc/rio.csrc/rio.hsrc/server.hsrc/unit/CMakeLists.txtsrc/unit/Makefilesrc/unit/test_compression.cppsrc/valkey-check-rdb.ctests/integration/rdb-compression.tcltests/integration/replication-aof-sync.tcltests/integration/valkey-check-rdb.tclvalkey.conf
✅ Files skipped from review due to trivial changes (2)
- cmake/Modules/ValkeySetup.cmake
- deps/lz4/LICENSE
🚧 Files skipped from review as they are similar to previous changes (33)
- src/CMakeLists.txt
- deps/CMakeLists.txt
- src/unit/CMakeLists.txt
- tests/integration/valkey-check-rdb.tcl
- valkey.conf
- cmake/Modules/SourceFiles.cmake
- src/config.c
- src/compression_rio.h
- src/rio.h
- tests/integration/replication-aof-sync.tcl
- deps/lz4/CMakeLists.txt
- src/aof.c
- src/server.h
- src/rdb.h
- deps/Makefile
- src/compression_lz4.c
- src/Makefile
- src/compression_lz4.h
- src/compression.h
- src/unit/Makefile
- src/compression_rio.c
- src/compression_stream.h
- deps/lz4/xxhash.c
- deps/lz4/xxhash.h
- tests/integration/rdb-compression.tcl
- src/rdb.c
- src/valkey-check-rdb.c
- deps/lz4/lz4frame.h
- src/compression.c
- src/compression_stream.c
- deps/lz4/lz4hc.c
- deps/lz4/lz4.h
- deps/lz4/lz4hc.h
Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
valkey-check-rdb reported offsets via rioTell() on every read, and rdbLoadProgressCallback computed the transport offset via rioTell() on every read even when no progress event was due. Read processed_bytes directly for the offset, and only compute the transport position when a progress event is actually reported. Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
Replace the separate rdb-compression-algo config with a single rdbcompression no|yes|lz4-stream. 'yes' keeps the legacy per-string LZF path; 'lz4-stream' selects whole-file streaming LZ4 RDB saves. Update valkey.conf and the RDB compression, check-rdb, and replication AOF sync tests to use the new config value. Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
--------- Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
57c5bfe to
52d4a47
Compare
|
A minor concern of mine is it's a bit difficult for me to follow multiple layer of interfaces and might not be easy to get started one (might not be a bad thing if things stay pretty much untouched for years to come). I chatted a bunch with Claude to understand if we could simplify further. Just adding the current structure and what could be removed. Would love to hear author/reviewers thoughts as well @xdk-amz @madolson @sarthakaggarwal97 @roshkhatri @nmvk |
Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
Signed-off-by: Sarthak Aggarwal <sarthagg@amazon.com>
|
Thanks @hpatro for taking a look. Post your review, I did some more refactoring. I removed |
|
@valkey-review-bot Please review this change. |
|
Review complete — nothing met the evidence bar for a comment. (After tracing the save/load/check-rdb paths, the config enum migration, the rio refactor (rioConnRead/rioFileRead/rioReadPartial), checksum policy, AOF-base rejection and replication gating, plus a clean -Werror build, I found no novel high/medium-confidence defect; the candidate concerns (checksum coupling, vendored lz4, signed/unsigned in rioBufferReadSome, the validateEnd dead-check) are already covered by existing review threads or are accepted design decisions with maintainer sign-off.) |




Related to #3195
Overview
Today,
rdbcompressiononly affects individual string payloads inside an otherwise normal RDB stream. This PR adds Valkey Compressed Stream (VCS) support for RDB persistence, withlz4as the first supported whole-stream codec.The RDB can now be wrapped in a
VCSenvelope and compressed as a single stream at theriolayer. The default behavior remains unchanged withlzf. On load, the RDB path probes for the envelope first. If present and valid, the input is transparently decompressed before normal RDB parsing continues. If absent, loading falls back to the existing plain RDB path. If the envelope is malformed or incompatible, the load fails early.Architecture
Key Design Decisions
riodecorator rather than as part of object serialization.VCSadds a small envelope ahead of the compressed payload so loaders can classify the stream before normal RDB parsing begins.lzfremains the default and plain RDB compatibility is preserved.stream_kindvalidation rejects incompatibleVCSstreams early.rdbLoadandvalkey-check-rdbboth handle plain andVCS-wrapped inputs transparently.VCSpath in this PR.Data Flow: Save Path
Important notes:
VCSheader stores magic, version, codec, flags, andstream_kind.rdbchecksum yesenables codec-native integrity signaling for the compressed stream.LZFpath is bypassed.Data Flow: Load Path
This PR is intentionally scoped to RDB streaming compression and the persistence/load paths that need to understand it. It does not add diskless sync and replication compression APIs.
Benchmarks
Benchmarked on r7g.2xlarge (Graviton, 8 vCPUs, 61GB RAM, NVMe). All results averaged over 3 repeats.
Datasets
Notes: