8000 One command can see a key both live and expired, so a replica can diverge on *STORE-style commands · Issue #632 · kividbio/kividb · GitHub
[go: up one dir, main page]

Skip to content

One command can see a key both live and expired, so a replica can diverge on *STORE-style commands #632

Open

Description

@murtazayusuf

Found in the review of #631 (which fixed #628). A key can pass its deadline in the microseconds between two lookups inside one command. Each lookup reads the clock again, so one command can see the same key as live at one point and as gone at another. A replica never expires keys by its own clock; it holds the key until the master's UNLINK arrives.

How a replica diverges:

  1. Key a has a TTL and is live when the write starts (Expire a write's keys past their TTL first, and propagate no write that changed nothing (#628) #631's expire-first hook leaves it alone).
  2. a expires before the command's own lookup.
  3. SUNIONSTORE d a b on the master reads a as absent and stores b. The command is propagated as is.
  4. The replica still holds a, so it stores a ∪ b.
  5. The later UNLINK a doesn't fix d.

The same can happen with any command whose effect lands on another key: BITOP, PFMERGE, ZUNIONSTORE/ZINTERSTORE, SORT ... STORE, GEOSEARCHSTORE, SMOVE, LMOVE, COPY.

Fix: take one clock reading per command, as Redis does with commandTimeSnapshot(), and use it for every expiry check in that command: is_past, the shard lookups, and the expire-first hook. The key's state is then the same throughout a command, so the hook's decision and the command's lookups agree.

Activity

  1. added this to the 1.3.0 milestone on Nov 11, 2026
  2. SoumaCloud commented on Nov 11, 2026

    @SoumaCloud

    Let me try this one @murtazayusuf

  3. murtazayusuf commented on Nov 11, 2026

    @murtazayusuf
    ContributorAuthor

    sure, it's yours, thanks @SoumaCloud. it's on 1.3.0 so no rush. the idea is to read the clock once when a command starts (like Redis's commandTimeSnapshot) and pass that time to every expiry check the command does: is_past, the shard lookups, and the expire-first hook in handler::expire_named_keys_first. a good test is a key with a tiny pexpire run through SUNIONSTORE/BITOP/COPY in a loop on a master with a replica, then check the replica's dest key always matches the master's. this is your first PR here so the CI runs need an approval click from us, we'll do that quickly. shout if anything in the handler is unclear.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    0