Skip to content

Support Valkey 9.1 rollback compatibility with Redis 6.0#4216

Open
chenys wants to merge 4 commits into
valkey-io:6.0from
chenys:valkey-9.1-redis-6.0-downgrade-compat
Open

Support Valkey 9.1 rollback compatibility with Redis 6.0#4216
chenys wants to merge 4 commits into
valkey-io:6.0from
chenys:valkey-9.1-redis-6.0-downgrade-compat

Conversation

@chenys

@chenys chenys commented Jul 20, 2026

Copy link
Copy Markdown

Summary

  • Extend the Redis 6.0 RDB downgrade compatibility work from Roll backward downgrade compatibility from Redis 7.0/7.2 and Valkey 7.2/8.0 to Redis 6.0 #2549 to Valkey 9.1 RDB version 80 (VALKEY080).
  • Load Valkey 9.1 data that Redis 6.0 can represent, including stream entries, consumer groups, and pending-entry ownership.
  • Consume newer stream metadata and SLOT_INFO while preserving RDB alignment.
  • Fail closed instead of silently losing semantics when snapshots contain hash-field expiration, function libraries, or active atomic slot migration state.
  • Fail corrupt quicklist conversion rather than silently dropping nodes.
  • Accept SET ... EXAT/PXAT on the Redis 6.0 downgrade target. Valkey rewrites relative SET expirations and SETEX/PSETEX to absolute SET ... PXAT before propagation; rejecting PXAT causes permanent replica divergence.
  • Preserve absolute expiration timestamps and reject relative-expiration arithmetic overflow.

This PR includes the compatibility commit from #2549 because that PR was not merged and provides the Redis 7.0/7.2 and Valkey 7.2/8.0 compatibility foundation.

Test plan

  • make -j4 — passed.
  • make test — all 61 test units passed.
  • Targeted SET parser, AOF reload, replication downgrade, RDB downgrade, RESTORE, and corrupt-input tests — passed.
  • Valkey 9.1 primary to patched Redis 6.0 replica: SET EX, SET PX, SETEX, and PSETEX propagated successfully as absolute expirations; values and TTLs matched, the replication link remained up, and no unexpected error replies were recorded (6 checks passed, 0 failed).
  • git diff --check — passed.

Satheesha Chattenahalli Hanume Gowda and others added 2 commits August 26, 2025 15:47
…key 7.2/8.0

Signed-off-by: Satheesha Chattenahalli Hanume Gowda <satheesha@apple.com>
Extend the Redis 6.0 compatibility loader introduced by valkey-io#2549 to read Valkey RDB v80 data that can be represented safely.

Consume newer stream and slot metadata, preserve stream entries, groups, and pending-entry ownership, and reject hash-field expiration, function libraries, and active slot migration state instead of silently dropping semantics.

Add Valkey 9.1 fixtures and integration coverage.

Signed-off-by: yusheng.chen <yusheng.chen@uber.com>
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0e75a75b-6d76-4cdc-96d4-fd3272f436bb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chenys

chenys commented Jul 20, 2026

Copy link
Copy Markdown
Author

No need to merge/approve, it is to provide rollback option from valkey 9.1 to redis 6.0

@chenys chenys closed this Jul 20, 2026

@valkey-review-bot valkey-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found three correctness gaps in the new downgrade-compat path. RESTORE relaxes the footer version check without switching to the compatibility object loader, and there are two corrupt-input error-path bugs in the new conversion helpers.

Comment thread src/cluster.c
rdbver = (footer[1] << 8) | footer[0];
if (rdbver > RDB_VERSION) return C_ERR;
if ((rdbver >= RDB_FOREIGN_VERSION_MIN && rdbver <= RDB_FOREIGN_VERSION_MAX) ||
(rdbver > RDB_VERSION && server.rdb_version_check == RDB_VERSION_CHECK_STRICT)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relaxing the footer check here only updates the first half of RESTORE. The decode path still goes through rdbLoadObjectType(&payload) / rdbLoadObject(type, &payload, key->ptr) in src/cluster.c:5080-5082, and rdbLoadObject() still rejects the new encoded object types with Unknown RDB encoding type in src/rdb.c:2031-2033. In relaxed mode that means future-version payloads only work for object types 6.0 already knew; listpack-backed hash/zset/set/stream dumps will still fail with Bad data format.

Thread the validated footer version into restoreCommand() and call rdbLoadObjectCompat() there, the same way rdbLoadRio() does.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6e5d24f.

RESTORE now captures the validated footer RDB version and routes object decoding through rdbLoadObjectCompat(). The regression test restores authentic Valkey 9.1 hash, zset, list, set, and stream DUMP payloads in relaxed mode.

[addressed by agent]

Comment thread src/rdb_downgrade_compat.c Outdated

case RDB_TYPE_SET_LISTPACK:
/* Set with listpack encoding - convert to regular set */
o->type = OBJ_SET;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

o->ptr is still the temporary ziplist here, but o->type / o->encoding have already been changed to OBJ_SET / OBJ_ENCODING_HT. Any decrRefCount(o) before o->ptr = set_dict—for example the duplicate-element error path below—will therefore run freeSetObject() and call dictRelease() on a ziplist pointer (src/object.c:295-299), turning the corrupt-input path into a crash.

Keep the object in its original string/ziplist state until the dict is fully built, or assign o->ptr = set_dict before any path that can decref it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6e5d24f.

Set conversion now installs the destination dictionary on the object before any error path can decrement it, while retaining the source ziplist separately until conversion completes. A duplicate-element payload now returns Bad data format and the server remains alive.

[addressed by agent]

Comment thread src/rdb_downgrade_compat.c Outdated
* ziplist and storing it at 'zl'.
* The function is safe to call on non-validated listpacks, it returns 0
* when encounter an integrity validation issue. */
int quicklistConvertAndValidateIntegrity(unsigned char *lp, unsigned char **zl) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quicklistConvertAndValidateIntegrity() never reports a validation failure: src/rdb_downgrade_compat.c:968-974 just walks lpFirst() / lpNext() and unconditionally returns 1, so the failure branch at the call site is dead code. That matters here because the listpack helpers are documented as assuming already-valid input (src/listpack.c:487-490), so corrupt packed quicklist nodes will still be accepted or can walk invalid memory during conversion instead of being rejected.

This helper needs a real listpack validity check and should return 0 when that validation fails.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6e5d24f.

Quicklist conversion now receives the serialized node length and runs a bounded deep listpack integrity validation before invoking conversion callbacks. A corrupt listpack header is rejected with Bad data format rather than being traversed.

[addressed by agent]

Use the compatibility object loader for relaxed RESTORE payloads, validate listpacks before quicklist conversion, and make set conversion cleanup safe on duplicate elements.

Add RESTORE coverage for Valkey 9.1 listpack encodings and corrupt-input regressions.

Signed-off-by: yusheng.chen <yusheng.chen@uber.com>
@chenys chenys reopened this Jul 20, 2026
Valkey rewrites relative SET expirations to SET PXAT before propagation. Redis 6.0 rejects PXAT and EXAT during parsing, which can leave replicas permanently divergent.

Backport EXAT and PXAT parsing with absolute expiration handling and overflow checks. Add direct command, AOF reload, and replication downgrade compatibility coverage.

Signed-off-by: yusheng.chen <yusheng.chen@uber.com>
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.

1 participant