Skip to content

release(v0.8.9): a command must not write what it cannot propagate - #829

Merged
TinDang97 merged 1 commit into
mainfrom
release/v0.8.9
Sep 4, 2026
Merged

release(v0.8.9): a command must not write what it cannot propagate#829
TinDang97 merged 1 commit into
mainfrom
release/v0.8.9

Conversation

@TinDang97

Copy link
Copy Markdown
Collaborator

Patch release rolling up 2 merged PRs closing 3 issues since v0.8.8.

One invariant. Propagation is gated on the reply not being an error
(!matches!(resp, Frame::Error(_))), so any command that mutates and THEN
returns an error leaves a write applied on the master, never appended to the
AOF and never sent to a replica: silent data loss across restart, permanent
replica divergence.

CORRECTNESS

The blocking path is an INTERCEPT: it short-circuits the dispatch exit where
every other write meets the AOF and the replication stream. That is the same
structural gap moon#644 closed for tracking invalidation on the same path in
v0.8.7. The new record is the SYNTHESISED non-blocking sibling — a replica
applying a literal BLPOP would park its apply loop — derived from the REPLY,
since a blocking pop takes many keys and only the reply says which one served.

KNOWN DIVERGENCE RIDING THIS RELEASE
moon#825: SPOP and XADD * propagate their literal bytes, so a replica or AOF
replay produces a different result. Deferred to v0.8.10; both blocked fix seams
are documented on the issue.

VALIDATION
GCE Linux gate green on both release heads, all four legs each (monoio with
io_uring LIVE 644s, tokio 620s, client-compat vs real redis-server 234s, MSRV
1.94); hosted dispatch matrix green on both. The #827 fix was mutation-tested:
disabling only the sharded wiring turns all sixteen cases red again, so both
runtime sites are genuinely exercised. Over-propagation was checked against the
AOF bytes — timeouts, WRONGTYPE and misses reach neither plane; BLMPOP … COUNT 10 that popped 3 logs LPOP mp 3; multi-key BLPOP first second served by
second logs LPOP second — and moon#539's phantom-key guard is unaffected.

Also corrects two artefacts of v0.8.8's cut, which was squashed into #785: the
README version table had no v0.8.8 row and still marked v0.8.7 current, and
CHANGELOG carried a duplicate truncated #788 bullet.

Refs #823, #827

Patch release rolling up 2 merged PRs closing 3 issues since v0.8.8.

One invariant. Propagation is gated on the reply not being an error
(`!matches!(resp, Frame::Error(_))`), so any command that mutates and THEN
returns an error leaves a write applied on the master, never appended to the
AOF and never sent to a replica: silent data loss across restart, permanent
replica divergence.

CORRECTNESS
- `redis.call`/`redis.pcall` turned a Lua nil, boolean or table argument into a
  frame shape no wire client can produce, which HSET, HMSET, LPUSH, RPUSH,
  LPUSHX, RPUSHX, ZREM, MSET and MSETNX each discovered from INSIDE their
  mutation loop. Measured: `EVAL "return redis.pcall('LPUSH','mylist','a','b',
  true)" 0` answered an arity error with `LLEN mylist = 2` resident, and 0
  after restart. Fixed at the boundary, as Redis 8.6.1 refuses it, AND by
  hoisting validation above the mutation window in all nine commands — defence
  in depth, because the raw wire reaches the same code with no Lua anywhere
  (#823, PR #824).
- XADD called `get_or_create_stream` before parsing the ID, so all five of
  `bogus`, `0-0`, `1-1-1`, `abc-1` and `-5` created a charged, DBSIZE-visible
  stream that was never logged and vanished on restart. 3,000 rejected XADDs
  cost 1.46 MB nothing credits back (#823, PR #824).
- Blocking pops propagated NOTHING outside MULTI. BLPOP, BRPOP, BLMOVE,
  BRPOPLPUSH, BZPOPMIN, BZPOPMAX, BLMPOP and BZMPOP mutated, acked the client
  and fed neither plane — sixteen cases across all eight commands on both the
  immediate and the parked-then-woken path, sixteen losses. For a queue
  consumer on BLPOP this redelivered every message already consumed after a
  master restart or on failover (#827, PR #828).

The blocking path is an INTERCEPT: it short-circuits the dispatch exit where
every other write meets the AOF and the replication stream. That is the same
structural gap moon#644 closed for tracking invalidation on the same path in
v0.8.7. The new record is the SYNTHESISED non-blocking sibling — a replica
applying a literal BLPOP would park its apply loop — derived from the REPLY,
since a blocking pop takes many keys and only the reply says which one served.

KNOWN DIVERGENCE RIDING THIS RELEASE
moon#825: SPOP and `XADD *` propagate their literal bytes, so a replica or AOF
replay produces a different result. Deferred to v0.8.10; both blocked fix seams
are documented on the issue.

VALIDATION
GCE Linux gate green on both release heads, all four legs each (monoio with
io_uring LIVE 644s, tokio 620s, client-compat vs real redis-server 234s, MSRV
1.94); hosted dispatch matrix green on both. The #827 fix was mutation-tested:
disabling only the sharded wiring turns all sixteen cases red again, so both
runtime sites are genuinely exercised. Over-propagation was checked against the
AOF bytes — timeouts, WRONGTYPE and misses reach neither plane; `BLMPOP … COUNT
10` that popped 3 logs `LPOP mp 3`; multi-key `BLPOP first second` served by
`second` logs `LPOP second` — and moon#539's phantom-key guard is unaffected.

Also corrects two artefacts of v0.8.8's cut, which was squashed into #785: the
README version table had no v0.8.8 row and still marked v0.8.7 current, and
CHANGELOG carried a duplicate truncated #788 bullet.

Refs #823, #827
author: Tin Dang
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 20 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: db000d44-d6ce-479d-9b29-e7c403094536

📥 Commits

Reviewing files that changed from the base of the PR and between 37ed58b and 3046068.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • CHANGELOG.md
  • Cargo.toml
  • README.md
  • RELEASES.md

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.

@TinDang97
TinDang97 merged commit 6251429 into main Sep 4, 2026
21 checks passed
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