Skip to content

Fix lost Redis appends during Raft leader handoff - #1286

Merged
bootjp merged 8 commits into
mainfrom
feature/fix-redis-jepsen-lost-append
Oct 22, 2026
Merged

bootjp merged 8 commits into
mainfrom
feature/fix-redis-jepsen-lost-append

Conversation

@bootjp

@bootjp bootjp commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Reject one-phase writes whose commit timestamp is at or below the applied watermark, so a leader handoff cannot add a version behind an already served snapshot.
  • Identify Raft replays by applied index before consulting exact key versions. A distinct entry with the same timestamp now receives a write conflict instead of a false success.
  • Compare final PUT values during one-phase retry deduplication, including Redis lists and scalar writes.
  • Reject colliding one-phase writes staged in the same Raft entry batch.
  • Serialize local filesystem Jepsen register setup across clients while preserving the reset on every setup! call.

Failures and fixes

The scheduled Jepsen run acknowledged an append of 44 on list 23, then a later successful read returned the list with 45 but without 44. The history shows a leader change during these operations. Regression tests reproduce distinct-entry, same-batch, and retry timestamp collisions.

The stricter timestamp fence exposed a separate filesystem Jepsen setup race: multiple local clients initialized the same FUSE register files concurrently. Their writes overtook one another at Raft apply, and the fence returned write conflicts that FUSE surfaced as Resource temporarily unavailable. A shared setup lock prevents these self-conflicts; each new setup still resets register values as the existing test requires.

Verification

  • go test ./store
  • go test ./kv -skip '^TestShardedCoordinator_RenewHLCLeases_SkipsInFlightGroupWithoutBlockingPeers$'
  • go test ./kv -run '^TestShardedCoordinator_RenewHLCLeases_SkipsInFlightGroupWithoutBlockingPeers$' -count=1
  • go test ./adapter -run '^(TestListPushDedup_|TestItemWriteDedup_|TestRedis_MultiExecAtomic|TestRedis_RPushLRange|TestXAddDedup_)' -count=1
  • go build ./internal/filesystem/fuseadapter
  • golangci-lint run ./store/... ./kv/... ./adapter/... (0 issues)
  • Jepsen unit tests: 204 tests, 566 assertions, 0 failures, 0 errors.
  • Final Jepsen CI run at 78cab1ba: all steps passed with encryption off and on, including filesystem overwrite and rename workloads.

Remaining risk

Exact timestamp plus matching values cannot uniquely identify a retry if two independent attempts write identical bytes. Adapter-side self-conflict probes also use exact timestamp existence. The unfenced raw-write batch path still treats equal (key, timestamp) versions as replays within an entry. A durable attempt identity and a separate raw-write replay rule are needed to close those cases.

The repository-wide macOS run cannot compile the Linux FUSE test (Statfs_t.Namelen); the same unrelated error blocks the local commit hook. The full kv suite twice hit a one-second lease-warmup timing condition; that test passed on its own, and the remaining kv tests passed when it was excluded.

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (fd25bda):

  • kv/fsm.go
  • store/mvcc_store.go

What to check, by subsystem:

  • kv/hlc*.go — Next() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.go — RunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

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

Next included review available in 43 minutes.

Check out review usage here.

View limit details

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

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 938f04ed-4d77-4678-971c-2af4002c6f0a
📥 Commits

Reviewing files that changed from the base of the PR and between 429f5ee and d23ab8a.

📒 Files selected for processing: 13
  • jepsen/src/elastickv/filesystem_workload.clj
  • kv/fsm.go
  • kv/fsm_dynamodb_timestamp_collision_test.go
  • kv/fsm_occ_test.go
  • kv/fsm_onephase_dedup_test.go
  • kv/leader_routed_store.go
  • kv/shard_store.go
  • store/apply_order_fence_jepsen_test.go
  • store/lsm_store.go
  • store/lsm_store_applied_index_test.go
  • store/mvcc_store.go
  • store/raft_entry.go
  • store/store.go
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (2f5e5a8):

  • kv/fsm.go
  • store/mvcc_store.go

What to check, by subsystem:

  • kv/hlc*.go — Next() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.go — RunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@bootjp
bootjp marked this pull request as ready for review October 22, 2026 08:26
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-22T17:05:26.266385Z d23ab8a Manual request
🔒 Security Review ✅ Completed 2026-10-22T17:07:51.781186Z d23ab8a Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (2f5e5a8):

  • kv/fsm.go
  • store/mvcc_store.go

What to check, by subsystem:

  • kv/hlc*.go — Next() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.go — RunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (afbb73d):

  • kv/fsm.go
  • store/mvcc_store.go

What to check, by subsystem:

  • kv/hlc*.go — Next() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.go — RunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f5e5a84b2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread kv/fsm.go
Comment on lines +1665 to +1666
if mut.Op != pb.Op_PUT {
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Match delete mutations before deduplicating retries

When an option-2 retry contains a delete and another transaction committed any version of its primary key at the colliding PrevCommitTS, this loop skips the delete entirely; for a delete-only write, or when all PUTs happen to match, the function therefore returns true without verifying that a tombstone exists at that timestamp. The FSM then no-ops the whole Redis EXEC or DynamoDB retry and can acknowledge a deletion that never occurred. Compare the exact operation/tombstone state for DEL mutations before declaring the prior attempt a match.

Useful? React with 👍 / 👎.

Comment thread kv/fsm.go
Comment on lines +1678 to +1680
value, err := f.store.GetAt(ctx, key, prevTS)
if err != nil {
return false, errors.WithStack(err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Read retry values without applying the retention floor

When PrevCommitTS has fallen below a replica's local retention watermark but the exact version is still physically present, CommittedVersionAt deliberately reports the version so retry dedup remains deterministic, but this subsequent GetAt returns ErrReadTSCompacted. A genuine landed retry that previously no-op'ed therefore becomes an error, and replicas with different local retention floors can produce different apply results for the same Raft entry. Use an exact-value probe that bypasses the retention watermark, matching the existence probe's contract.

Useful? React with 👍 / 👎.

Comment thread store/lsm_store.go
return s.stageMutationsInRaftEntry(ctx, scope, mutations, readKeys, startTS, commitTS, appliedIndex)
}
return s.applyMutationsWithOpts(ctx, mutations, readKeys, startTS, commitTS, s.raftApplyWriteOpts(), false, appliedIndex, true)
return s.applyMutationsWithOpts(ctx, mutations, readKeys, startTS, commitTS, s.raftApplyWriteOpts(), false, appliedIndex, true, false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Fence live raw Raft mutations against the watermark

The raw FSM path still calls ApplyMutationsRaftAt, and this false disables the new commit-timestamp fence for those live entries. If concurrent requests allocate timestamps in one order but reach Raft in the opposite order, a raw PUT/DELETE with commitTS <= LastCommitTS()—especially an equal timestamp on a different key—passes because raw writes use startTS == commitTS; it can therefore add a version after a reader has already used that timestamp as an applied snapshot. Route live raw mutations through a fenced path while retaining the unfenced method only for PREPARE/2PC semantics and verified replay.

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (01e8424):

  • kv/fsm.go
  • store/mvcc_store.go

What to check, by subsystem:

  • kv/hlc*.go — Next() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.go — RunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (aa51b35):

  • kv/fsm.go
  • store/mvcc_store.go

What to check, by subsystem:

  • kv/hlc*.go — Next() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.go — RunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aa51b35c4f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread kv/fsm.go
Comment on lines +1678 to +1680
value, err := f.store.GetAt(ctx, key, prevTS)
if err != nil {
return false, errors.WithStack(err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Treat an exact tombstone as a nonmatching PUT

When a PUT retry's PrevCommitTS collides with another transaction's delete on the same key during a leader handoff, CommittedVersionAt reports the tombstone but this GetAt returns ErrKeyNotFound. The FSM consequently rejects the retry even though its prior PUT never landed, potentially surfacing a Redis or DynamoDB write failure; inspect the exact version's operation and treat a tombstone as a mismatch for PUT.

AGENTS.md reference: AGENTS.md:L33-L35

Useful? React with 👍 / 👎.

Comment on lines +549 to +552
(locking setup-done
(when-not @setup-done
(with-retries max-retries #(local-setup! mount-dir keys))
(reset! setup-done true)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve setup's reset behavior

After one local setup succeeds, this persistent guard prevents every subsequent setup! call on the same workload client from running local-setup! again. That leaves values written by a previous invocation in place and deterministically breaks the existing local-client-setup-is-idempotent-and-resets-values test, which writes 3, invokes setup again, and expects the initial value; scope the once-only coordination to concurrent initialization rather than permanently suppressing later setup calls.

AGENTS.md reference: AGENTS.md:L14-L14

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (6d874a3):

  • kv/fsm.go
  • store/mvcc_store.go

What to check, by subsystem:

  • kv/hlc*.go — Next() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.go — RunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6d874a3744

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread kv/fsm.go
Comment on lines +1682 to +1683
matched = bytes.Equal(value, mut.Value)
break

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep searching aliases after a mismatched live version

When staged visibility is active and both the live key and a staged alias have versions at PrevCommitTS, this unconditional break stops after the live candidate even if its value belongs to a colliding transaction. A matching staged copy of the actual prior attempt is therefore ignored, so a genuine landed retry falls through to normal application and may conflict or be recomputed. Continue through the remaining aliases when bytes.Equal is false, and stop only after finding a match.

Useful? React with 👍 / 👎.

Comment thread store/store.go
Comment on lines +318 to +322
// ApplyMutationsRaftAtFenced rejects a live one-phase write whose
// commit timestamp is at or below the already-applied watermark. A write
// below that watermark could appear after a reader took that snapshot.
// PREPARE and 2PC COMMIT use the unfenced method above.
ApplyMutationsRaftAtFenced(ctx context.Context, mutations []*KVPairMutation, readKeys [][]byte, startTS, commitTS, appliedIndex uint64) error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Fence the primary 2PC commit too

When a multi-shard transaction receives commit timestamp c1 and another entry advances the primary shard's watermark to c2 > c1 before PREPARE arrives, a reader can successfully take snapshot c2 before the intent exists; handleCommitRequest then uses the unfenced ApplyMutationsRaftAt and later publishes the primary version at c1, changing that already-served snapshot. Subsequent validation also misses the write because c1 <= c2. The first primary COMMIT needs the timestamp fence, while idempotent replays and secondary COMMITs can retain their existing exemptions.

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (78cab1b):

  • kv/fsm.go
  • store/mvcc_store.go

What to check, by subsystem:

  • kv/hlc*.go — Next() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.go — RunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@bootjp

bootjp commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@bootjp

bootjp commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (78cab1b):

  • kv/fsm.go
  • store/mvcc_store.go

What to check, by subsystem:

  • kv/hlc*.go — Next() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.go — RunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@claude

claude Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Claude finished @bootjp's task in 17s —— View job


TLA+ spec divergence review (head 78cab1b)

I read the diff for the two anchored files, kv/fsm.go and store/mvcc_store.go, plus the supporting store/ changes. I did not run tests or TLC, and I did not read tla/hlc/HLC.tla or the design doc §3 against the code. This is a code-reading review only.

HLC / ceiling (HLC-4): no change. The PR doesn't touch kv/hlc*.go, RunHLCLeaseRenewal, Observe(MaxAppliedHLC), the 48/16 bit layout, or the ceiling getter/setter. Nothing diverges from the spec here.

kv/fsm.go:

  • HLC lease apply (SetPhysicalCeiling) is untouched.
  • The one-phase apply now calls ApplyMutationsRaftAtFenced, which rejects commitTS <= LastCommitTS() with a write conflict. This is a strengthening: a version can no longer appear at or below an already-served snapshot. I read it as consistent with the MVCC visibility invariant, and it narrows the G10 handoff window. It does not replace the A0 current-term grant.

store/mvcc_store.go:

  • The in-memory fence is checked under the store lock, and ApplyMutations stays unfenced. That's fine for the direct path.
  • The fence compares against lastCommitTS, so the watermark has to be monotone and updated atomically with the write. I saw that in the in-memory store. For pebble I only saw it checked under the apply lock.

Gaps relative to MVCC-1..4 (not regressions):

  1. Raw FSM writes are unfenced. ApplyMutationsRaftAt is still used for raw PUT/DELETE, so a raw write at commitTS <= LastCommitTS can still land behind a served snapshot. (Codex P1; the PR's "Remaining risk" section mentions it.)
  2. The primary 2PC COMMIT is unfenced. A COMMIT at c1 can land after another entry advanced the watermark to c2 > c1. The version then appears under a snapshot that was already served. (Codex P1.) A first-time primary COMMIT should be fenced. Replays and secondary COMMITs can stay exempt.
  3. Retry dedup, in priorMutationValuesMatch:
    • A DEL mutation is skipped, so a delete-only retry can be treated as landed. Compare the tombstone state instead.
    • If the exact version is a tombstone, GetAt returns ErrKeyNotFound, which surfaces as an error. It should count as a mismatch.
    • GetAt applies the retention floor and can return ErrReadTSCompacted even though CommittedVersionAt reported the version. That can make replicas diverge on the same Raft entry. An exact-version read that bypasses the floor is needed.
    • The break after the first landed alias stops searching even when the value mismatches. A matching staged copy is then missed.
  4. Same-timestamp retries with identical bytes can't be told apart. This is a known gap, and it needs a durable attempt ID.

Per the repo convention, each of gaps 1–3 should get a failing regression test before the fix.

Spec update? No change to tla/hlc/HLC.tla is needed for what this PR does. If M3 later models MVCC-1..4, the model should include the commit-ts fence as an apply precondition. It should also keep the raw and 2PC-primary exemptions explicit until they are closed.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 78cab1baba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 78cab1baba

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@bootjp

bootjp commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

Related acceptance evidence from #1290: encrypted DynamoDB list-append with follower ingress/no HTTP map, snapshot interval 20, prior append -> fresh table reset -> six verified leader transfers failed on 8db0155 (G-single-item-realtime/G1c-realtime; acknowledged append 37 to key 16 was absent from a later read including 46). A separate diagnostic build combining 8db0155 with this PR at 78cab1b passed one identical Jepsen run with six transfers; this suggests overlap but is not a causal proof or a completed stress campaign. No duplicate Raft implementation was added to #1290. That combined run then exposed a separate encrypted restart guard trying to scan a compacted Raft interval, so it is not counted as snapshot/restart success.

@bootjp

bootjp commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

Follow-up controlled matrix for #1290: Java 25, encryption on, three separate nodes, no HTTP leader map, snapshot interval 20, prior append then fresh-table seal workload (15s/rate30/concurrency9/maxTxn4/transfer2s). Alternating 8db0155 versus 8db0155+78cab1ba for three pairs: all six Jepsen histories passed, but all six encrypted process restarts failed on the compacted-prefix startup guard. Seed was not recorded by this workload, so these are matched-parameter repetitions, not identical seeded schedules; this does not establish that #1286 causes or resolves the original acknowledged-append loss. No dependency changes or implementation copies into #1290. I am isolating the distinct compacted-restart guard fix on feature/encryption-compacted-restart-checkpoint, leaving this PR branch untouched. Its scope is verified snapshot checkpoint evidence with fail-closed negative controls, not disabling compaction/rollback guards.

@bootjp

bootjp commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

Causal reproduction of the original #1290 append37/key16 failure is now established from the retained WAL, beyond the stochastic matrix. ReadOnly decoding found no version containing37 on any of the three replicas. Replaying the SAME committed WAL from its legitimate index1/term1 anchor into fresh Pebble+real FSMs: entry371/term5 writes key16 list ending43 at commitTS117485124856709124; entry372/term5 attempts a different value ending37 at the SAME key/commitTS (startTS117485124856709122, PrevCommitTS0, 1PC phaseNONE). Baseline8db0155d returns nil for372 while latest value does not match; 8db0155+thisPR78cab1ba returns write conflict for372. This identifies the distinct-entry exact-timestamp replay false-success fixed here as the cause of the captured37 loss. No runtime implementation will be duplicated in #1290. I will add only a new non-overlapping regression file kv/fsm_dynamodb_timestamp_collision_test.go in an isolated worktree based on this HEAD, using the captured request shapes; existing files and your worktree remain untouched. Guard work remains separate in #1293. No force-push/merge/deploy.

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (d23ab8a):

  • kv/fsm.go
  • store/mvcc_store.go

What to check, by subsystem:

  • kv/hlc*.go — Next() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.go — RunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@bootjp

bootjp commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

Added only kv/fsm_dynamodb_timestamp_collision_test.go in signed bootjp commit d23ab8a via normal fast-forward push from an isolated worktree; no runtime implementation or existing file was changed. Minimized from captured WAL entries371/372 (key16, same commitTS117485124856709124, different protobuf item values ending43 vs37, PrevCommitTS0). Baseline8db0155d fails both plaintext/encrypted variants because the second entry returns nil; this HEAD passes both variants with -race -count=10, while preserving legitimate same-entry replay and fresh read/recompute retaining both appends. KV lint: 0 issues. Full retained-WAL replay comparison logs are /private/tmp/elastickv-seal-wal-baseline.log and /private/tmp/elastickv-seal-wal-with1286.log; no keys were output/published. @codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: d23ab8afc5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@bootjp

bootjp commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

Current-head d23ab8a code receipt says no major issues and CI is green; paginated review roots contain no original_commit_id matching this HEAD. The mutable summary nevertheless reports findings and security details still loading, without a current-head security receipt or actionable finding. Please refresh the current-head security result; old-head root comments remain stale under the review protocol. No runtime ownership or implementation changes are proposed here. @codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: d23ab8afc5

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@bootjp

bootjp commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

Current HEAD is still d23ab8a with successful CI, a current-head no-major-issues receipt, and completed clean security review. The refresh code-review summary has remained running since16:28:55Z for over30min; paginated current-head root inline findings remain empty. Please complete or refresh the unchanged HEAD review; no duplicate PR or additional runtime changes. @codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: d23ab8afc5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: d23ab8afc5

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@bootjp

bootjp commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

Four-source current-head checkpoint at d23ab8a: latest individual code receipt says no major issues (17:05:24Z), refreshed security receipt reports no issues, every CI check succeeds, and paginated root inline original_commit_id==HEAD count is0. Review bodies and older issue-style review findings refer to older SHAs and remain stale under the requested protocol. The mutable summary still sets historical unresolved-finding flags despite the clean current-head receipt; no actionable current-head finding was returned by any source. No duplicate PR, runtime edits, merge or deploy performed.

@bootjp
bootjp merged commit db42201 into main Oct 22, 2026
12 checks passed
@bootjp
bootjp deleted the feature/fix-redis-jepsen-lost-append branch October 22, 2026 17:22
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