Skip to content

Add streaming compression for replication full sync - #20

Open
roshkhatri wants to merge 23 commits into
replication-streaming-compression-prfrom
repl-streaming-comression-fullsync-pr
Open

Add streaming compression for replication full sync#20
roshkhatri wants to merge 23 commits into
replication-streaming-compression-prfrom
repl-streaming-comression-fullsync-pr

Conversation

@roshkhatri

Copy link
Copy Markdown
Owner

Adds streaming compression of the full-sync RDB payload for replicas that negotiate the compression capability, covering disk-based sync, diskless sync, and dual-channel sync. Builds on the streaming RDB codec (VCS/LZ4) and reuses the existing repl-compression config (no | lz4-stream); default off. Stacked on replication-streaming-compression-pr (the incremental-stream compression work).

Negotiation and the cohort rule

A replica advertises REPLCONF capa compression (REPLICA_CAPA_COMPRESSION) when repl-compression is enabled; the primary compresses only when it also has repl-compression enabled. Because a single full sync (a shared disk file, or one connset/pipe) serves a whole cohort of replicas, the decision is the AND of the capability over every replica in the attaching cohort: if any replica is not capable, the payload is sent plaintext so all of them can load it. This mirrors the existing skip_rdb_checksum cohort-AND.

Disk-based

The cohort AND is computed in startBgsaveForReplication (the generic rdbSaveBackground is not replication-aware) and passed via RDBFLAGS_REPL_COMPRESSED_SYNC; rdbSaveInternal applies the repl-compression gate, decoupled from the rdbcompression persistence setting.

Diskless

The cohort AND is computed inside rdbSaveToReplicasSockets alongside skip_rdb_checksum. Only the RDB body is wrapped as a VCS frame; the $EOF framing stays plaintext and the frame's own checksum replaces the RDB CRC64. The replica decompresses inline when it loads from the socket (repl-diskless-load enabled), or via the existing rdbLoad auto-detect when it receives the stream to disk first (the default, and dual-channel). The dual-channel handshake now advertises the compression capability.

streamReaderValidateFrameEnd is added so the replica can validate a closed compressed frame without consuming the caller-owned trailing EOF mark.

Testing

Disk grouping (all/mixed/none capable), diskless and dual-channel sync, default disk-receive load, checksum on/off, the compressed-full-sync to compressed-incremental handoff, REPLICAOF NO ONE teardown and resync, and the AOF-base BGREWRITEAOF fallback (a compression-capable replica's compressed disk sync RDB cannot be reused as the AOF base). valkey.conf documents the expanded repl-compression coverage.

Follow-up

A src/unit gtest for the streamReaderValidateFrameEnd split is tracked as a follow-up (currently covered by the integration tests).

roshkhatri and others added 15 commits May 28, 2026 00:52
Adds replication wire compression on top of valkey-io#3531 with lz4 as the first
supported codec for the incremental replication stream. The replication
stream from primary to replica is wrapped in a VKCS envelope (using
STREAM_KIND_REPL) and compressed as a single long-lived frame at the
per-replica buffer layer. Default behavior is unchanged with
'replcompression no'; existing replicas without the new capability stay
uncompressed.

Negotiation is per-replica via the existing PSYNC handshake; a new
REPLICA_CAPA_COMPRESSION capability lets each side opt in independently.
Compression runs inline on the IO thread that owns the replica's write
job; no dedicated compression thread, no IPC, no reordering. Optional
sticky thread affinity (lazy ownership + event-driven rebalance) keeps
the long-lived LZ4 frame state on a single IO thread for cache locality.

Configs:
  replcompression                   bool, default no
  repl-compression-thread-affinity  bool, default yes

Internal constants:
  REPLICA_CAPA_COMPRESSION         (1 << 4)
  REPL_COMPRESSION_ALGO            ALGO_LZ4
  REPL_COMPRESSION_LEVEL           0  (LZ4 fast mode)
  REPL_COMPRESSION_BATCH_LIMIT     1 MB raw input per dispatch
  REPL_STREAM_DECODER_OUTPUT_MAX   256 MB

INFO replication per-replica fields:
  compression=lz4, compressed_bytes, uncompressed_bytes,
  compression_ratio, compression_errors, compression_cpu_usec,
  debug_compression_pending_drains, debug_thread_switches

INFO replication server-level (replica side):
  repl_decompression_errors, repl_decompression_cpu_usec,
  repl_decompressed_bytes_total, repl_apply_cpu_usec,
  repl_apply_batches

CI adds a test-replication-compression job that runs the
replication-tagged integration tests with replcompression=yes to
exercise compression across the broader replication test surface.

Tests: 18 streamReader push-mode unit tests + 3 replCompression unit
tests + 27 integration tests.

Performance (BlockMesh tweets, 3M keys x ~315 byte JSON values, 1,073
MB uncompressed per replica, 30 clients, pipeline 50, 2 cross-region
replicas):
  LZ4 level 0 (default): 0.48 ratio, 52% bandwidth saved, 2.5s
                         compression CPU per replica, <1% throughput
                         overhead vs uncompressed baseline.
Affinity ON vs OFF: throughput unchanged (118.6K vs 118.1K keys/s) but
thread switches drop from ~800K to ~30 per replica.

ZSTD support follows in valkey-io#3798.

Related to valkey-io#3531.

Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
# Conflicts:
#	src/io_threads.c
#	src/networking.c
#	src/replication.c
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
The compression CI job (--config replcompression yes) ran replication-buffer.tcl
and the dual-channel buffer-memory tests, which assert exact replication
buffer/backlog memory and byte volumes. Compression legitimately changes those
(per-replica codec buffers add ~1MB scratch; fewer wire bytes let replicas keep
up), so the assertions fail under compression even though replication is
correct. Drop the repl-compression tag from replication-buffer.tcl and the two
dual-channel blocks holding the memory tests; they still run uncompressed in the
regular job. Functional dual-channel coverage stays in the compression job.

Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
…thakaggarwal97/valkey into replication-streaming-compression-pr

# Conflicts:
#	src/compression_stream.c
#	src/compression_stream.h
#	src/config.c
#	src/rdb.c
#	src/server.h
#	src/unit/test_compression.cpp
#	tests/integration/rdb-compression.tcl
#	valkey.conf
…tate and plaintext passthrough

Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
…val (fix macOS -Werror)

Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
@roshkhatri
roshkhatri force-pushed the repl-streaming-comression-fullsync-pr branch from 25ab585 to acabef9 Compare June 24, 2026 23:49
@sarthakaggarwal97

Copy link
Copy Markdown

tests are failing :P

@roshkhatri
roshkhatri force-pushed the repl-streaming-comression-fullsync-pr branch 3 times, most recently from f018f82 to 0a5e7e5 Compare June 27, 2026 02:38
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
@roshkhatri
roshkhatri force-pushed the repl-streaming-comression-fullsync-pr branch from 203fbec to 4e269f4 Compare June 30, 2026 21:56
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
@roshkhatri
roshkhatri force-pushed the repl-streaming-comression-fullsync-pr branch from 4e269f4 to efc525d Compare June 30, 2026 22:53
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
Compress the full-sync RDB payload for replicas that negotiate the
compression capability, covering disk-based, diskless, and dual-channel
sync. Reuses the streaming RDB codec (VCS/LZ4) and the existing
repl-compression config (no | lz4-stream); default off.

The payload is compressed only when every replica in the attaching cohort
advertised REPLICA_CAPA_COMPRESSION (cohort-AND); a mixed group falls back
to plaintext for all. Disk: decided in startBgsaveForReplication via
RDBFLAGS_REPL_COMPRESSED_SYNC and gated by repl-compression in
rdbSaveInternal. Diskless: decided in rdbSaveToReplicasSockets; the $EOF
framing stays plaintext and the frame checksum replaces the RDB CRC64. The
replica decompresses inline socket loads (both $EOF-mark and size-framed)
and via the existing rdbLoad auto-detect for disk-receive; the dual-channel
handshake advertises the capability. Adds streamReaderValidateFrameEnd and
STREAM_READER_ERROR_TRUNCATED so a link dropped mid-frame is a recoverable
resync rather than codec corruption.

Tests: disk grouping (all/mixed/none/off), diskless, dual-channel,
disk-receive load, checksum on/off, full->incremental handoff,
teardown+resync, mid-transfer truncation recovery, and an in-frame
corruption unit test.

Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
@roshkhatri
roshkhatri force-pushed the repl-streaming-comression-fullsync-pr branch from efc525d to ef02d03 Compare June 30, 2026 23:09
@roshkhatri
roshkhatri force-pushed the replication-streaming-compression-pr branch from 850bd66 to dbe7581 Compare July 30, 2026 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants