Skip to content

SET ... KEEPTTL loses the TTL on WAL replay / standby (command is marked IsOverwrite) #504

Description

@liunyl

SetCommand::IsOverwrite() returns true unconditionally, including for SET key val KEEPTTL. Overwrite commands are pruned in the log record and replayed with ignore_previous_version (the object is rebuilt from scratch rather than mutated in place), so on recovery / standby-forward the kept TTL is dropped and the key becomes immortal.

Evidence

  • include/redis_command.h:1379-1382SetCommand::IsOverwrite() returns true (no exception for KEEPTTL).
  • Overwrite pruning + fresh-object rebuild on replay: data_substrate/tx_service/include/cc/cc_entry.h (overwrite handling, ignore_previous_version).

The live execution path (CommitOn) reads the existing object to preserve its TTL when KEEPTTL is set, but a replayed/forwarded SET … KEEPTTL rebuilds the object with no prior version available → TTL lost.

Repro

SET k v EX 100; SET k v2 KEEPTTL; then crash + replay (or observe on a standby) → TTL k returns -1 (no expiry) instead of the remaining ~100s.

Fix: SET … KEEPTTL must not be treated as a full overwrite (it depends on prior state), or the replay/forward image must carry the resolved absolute TTL.


Found during a code audit (docs PR #492). IsOverwrite()==true verified; the replay-rebuild interaction is the documented overwrite-pruning behavior.

🤖 Found with Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions