Skip to content

tso: add dedicated ceiling fsm#1095

Open
bootjp wants to merge 12 commits into
mainfrom
design/dedicated-tso-fsm
Open

tso: add dedicated ceiling fsm#1095
bootjp wants to merge 12 commits into
mainfrom
design/dedicated-tso-fsm

Conversation

@bootjp

@bootjp bootjp commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add a minimal dedicated TSO state machine that accepts only HLC lease entries.
  • Snapshot and restore the physical ceiling as 8-byte big-endian state, and classify full lease entries as volatile-only.
  • Update the centralized TSO design doc status and remaining runtime wiring.

Validation

  • go test ./kv -run 'TestTSOStateMachine|TestLocalTSOAllocator|TestBatchAllocator|TestShardedCoordinator.*Timestamp|TestCoordinateUsesTSOAllocator' -count=1 -timeout=180s
  • go test ./kv -count=1 -timeout=300s
  • golangci-lint --config=.golangci.yaml run ./kv --timeout=5m
  • git diff --check
  • go test ./adapter -run '^TestMilestone1SplitRange_RestartReloadsCatalog$' -count=1 -timeout=180s
  • go test ./... -count=1 -timeout=600s (adapter package timed out at 600s; other packages completed)

Notes

  • This adds the dedicated FSM implementation and tests. Runtime bootstrap wiring for groupID = 0 remains a follow-up until the TSO leader redirect path exists.

Author: bootjp

Summary by CodeRabbit

  • 新機能
    • 専用TSOグループで、時刻の上限と割り当て基準値を管理できるようになりました。
    • スナップショットの保存・復元に対応し、従来形式のデータも互換的に復元できます。
  • 改善
    • 専用TSOグループでは暗号化変更操作を安全に拒否し、時刻サービスを継続できるようになりました。
    • グループ構築時のエラー処理とリソース解放を改善しました。
  • ドキュメント
    • TSO移行の進捗、互換性、今後の対応範囲を更新しました。

@bootjp

bootjp commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@bootjp, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6de71ecd-7967-412c-9b24-c2c1f75157c4

📥 Commits

Reviewing files that changed from the base of the PR and between 97a42ba and 227aff5.

⛔ Files ignored due to path filters (3)
  • proto/distribution.pb.go is excluded by !**/*.pb.go
  • proto/distribution_grpc.pb.go is excluded by !**/*.pb.go
  • proto/service.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (64)
  • adapter/distribution_server.go
  • adapter/distribution_server_test.go
  • adapter/dynamodb_item_write.go
  • adapter/dynamodb_locks.go
  • adapter/dynamodb_migration.go
  • adapter/dynamodb_onephase_dedup_test.go
  • adapter/dynamodb_schema.go
  • adapter/dynamodb_transact.go
  • adapter/grpc.go
  • adapter/grpc_test.go
  • adapter/redis_collection_ttl.go
  • adapter/redis_compat_helpers.go
  • adapter/redis_delta_compactor.go
  • adapter/redis_expire_cmds.go
  • adapter/redis_hash_cmds.go
  • adapter/redis_list_dedup_test.go
  • adapter/redis_lists.go
  • adapter/redis_lua_context.go
  • adapter/redis_lua_phase_d_test.go
  • adapter/redis_retry_test.go
  • adapter/redis_set_cmds.go
  • adapter/redis_stream_cmds.go
  • adapter/redis_strings.go
  • adapter/redis_txn.go
  • adapter/redis_zset_cmds.go
  • adapter/s3.go
  • adapter/s3_admin.go
  • adapter/s3_admin_objects.go
  • adapter/s3_hlc_fence_test.go
  • adapter/s3_multipart_complete.go
  • adapter/s3_put_object.go
  • adapter/s3_upload_part.go
  • adapter/sqs_catalog.go
  • adapter/sqs_fifo.go
  • adapter/sqs_messages.go
  • adapter/sqs_messages_batch.go
  • adapter/sqs_purge.go
  • adapter/sqs_reaper.go
  • adapter/sqs_redrive.go
  • adapter/sqs_tags.go
  • distribution/catalog.go
  • distribution/catalog_test.go
  • docs/design/2026_04_16_partial_centralized_tso.md
  • kv/coordinator.go
  • kv/keyviz_label.go
  • kv/leader_routed_store_test.go
  • kv/lease_warmup_test.go
  • kv/shard_store.go
  • kv/sharded_coordinator.go
  • kv/sharded_coordinator_txn_test.go
  • kv/tso.go
  • kv/tso_floor_test.go
  • kv/tso_fsm.go
  • kv/tso_fsm_test.go
  • kv/tso_raft.go
  • kv/tso_raft_test.go
  • kv/tso_test.go
  • main.go
  • main_encryption_admin.go
  • main_encryption_admin_test.go
  • main_encryption_rotate_on_startup_test.go
  • main_tso_routing_test.go
  • proto/distribution.proto
  • proto/service.proto
📝 Walkthrough

Walkthrough

専用TSO用Raftステートマシンを追加し、leaseとallocation floorの適用、スナップショット互換復元、group-0 runtime構築、EncryptionAdminのmutator制御を実装した。関連する設計文書と検証テストも更新した。

Changes

専用TSO FSM

Layer / File(s) Summary
TSO状態の適用と入力検証
kv/tso_fsm.go, kv/tso_fsm_test.go, docs/design/...
TSOStateMachineがHLC leaseとallocation floorをatomicに保持・適用し、不正エントリをhalt付きで拒否する。レガシー暗号制御エントリは状態を変更せず拒否する。
スナップショットと旧形式復元
kv/tso_fsm.go, kv/tso_fsm_test.go, docs/design/...
ceilingとallocation floorを16バイトのv2形式で保存し、旧8バイト形式およびkvFSM由来スナップショットからfloorを導出して復元する。
group-0 runtimeと管理配線
main.go, main_encryption_admin.go, main_encryption_admin_test.go, multiraft_runtime_test.go, docs/design/...
専用TSO groupの構築と既存ストア保持を整理し、group-0ではmutatorを無効化しつつLeaderViewを配線する。失敗時のruntime解放とレガシー暗号エントリ後の継続処理を検証する。

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RaftRuntime
  participant TSOStateMachine
  participant HLC
  participant EncryptionAdmin
  RaftRuntime->>TSOStateMachine: group-0のlease/floorエントリをApply
  TSOStateMachine->>HLC: ceilingまたはallocation floorを反映
  TSOStateMachine-->>RaftRuntime: 更新結果または拒否結果
  RaftRuntime->>EncryptionAdmin: group単位の管理配線を登録
  EncryptionAdmin-->>RaftRuntime: group-0のmutator要求をFailedPreconditionで拒否
Loading

Possibly related PRs

  • bootjp/elastickv#748: TSO FSMが扱うレガシー暗号制御エントリのwire opcodeとデコード処理に関連する。
  • bootjp/elastickv#760: registerEncryptionAdminServerの登録経路とEncryptionAdmin配線に関連する。
  • bootjp/elastickv#1064: 専用TSOのgroup-0予約とTSO固有のruntime配線に関連する。
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 専用TSO用のFSM追加という主要変更を簡潔に表しており、変更内容と整合しています。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements the TSOStateMachine, a minimal state machine for the dedicated timestamp group that handles HLC lease-renewal entries, along with its snapshotting, restoration, and volatile entry classification logic. It also updates the design documentation to reflect this progress and adds comprehensive unit tests for the new state machine. There are no review comments to evaluate, and I have no additional feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@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

| M6 — partial | Dedicated TSO Raft group (`groupID = 0`) is reserved/bootstrap-capable and warmed by the HLC renewal bridge; TSO-leader-only timestamp issuance and the minimal `TSOStateMachine` remain open | Low |

P3 Badge Update stale M6 status row

This milestone row still says the minimal TSOStateMachine remains open, but this same change marks it implemented in §1.0 and adds kv/tso_fsm.go. Readers using this design doc to plan M6 will incorrectly think the FSM work is still pending, so please update the row to match the new runtime-wiring-only remaining status.

ℹ️ 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/tso_fsm.go Outdated
}
ceilingMs := int64(binary.BigEndian.Uint64(data[1:])) //nolint:gosec // value is a Unix ms timestamp encoded as uint64.
if f != nil && f.hlc != nil && ceilingMs > 0 {
f.hlc.SetPhysicalCeiling(ceilingMs)

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 Advance the TSO HLC floor on lease apply

When this FSM is used for the dedicated TSO group, applying a lease only updates physicalCeiling while leaving HLC.last at zero on followers/restarted nodes. If the current TSO leader has handed out timestamps under a future ceilingMs and then fails before the wall clock reaches it, the new leader has applied the same lease but has not observed any of those allocations, so its first NextBatchFenced can return (ceilingMs<<16)|0 again and duplicate timestamps. The TSO path needs to persist/observe an allocation floor or force a fresh higher lease before serving after leadership changes.

Useful? React with 👍 / 👎.

Comment thread kv/tso_fsm.go Outdated

func (f *TSOStateMachine) Apply(data []byte) any {
if len(data) != hlcLeaseEntryLen {
return errors.Wrapf(ErrTSOStateMachineInvalidEntry, "expected %d bytes, got %d", hlcLeaseEntryLen, len(data))

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 Return a halting response for invalid TSO entries

If a malformed payload is ever committed to the dedicated TSO group, returning a bare error here does not actually reject it at the raft apply layer: applyNormalCommitted only stops before setApplied for responses implementing HaltApply() error, while plain error responses are still marked applied and only delivered back to the proposer. In a misrouted group-0 proposal or mixed-version entry scenario, the invalid entry would be permanently advanced past instead of failing closed, violating the FSM's “accepts only HLC lease entries” contract.

Useful? React with 👍 / 👎.

@bootjp

bootjp commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Updated commit: 4e49dfbe7f31f084fc98bb5bec47a0e1458c4c24

Addressed current-head review findings:

  • TSOStateMachine now observes a derived allocation floor for each applied/restored HLC lease ceiling, preventing a restarted or newly elected TSO leader from reissuing the previous leader's logical slots.
  • Invalid TSO FSM entries now return a HaltApply response instead of a plain error, so malformed group-0 entries fail closed without advancing applied index.
  • The centralized TSO design doc M6/status text now matches the implemented FSM and remaining runtime-wiring work.

Caller/semantic audit:

  • NewTSOStateMachine remains only used by the new tests in this branch; runtime group-0 wiring is still a documented follow-up.
  • The HLC floor change is limited to the dedicated TSO FSM path and does not change kvFSM HLC lease apply behavior.

Validation:

  • go test ./kv -run 'TestTSOStateMachine|TestLocalTSOAllocator|TestBatchAllocator|TestShardedCoordinator.*Timestamp|TestCoordinateUsesTSOAllocator' -count=1 -timeout=180s
  • go test ./kv -count=1 -timeout=300s
  • golangci-lint --config=.golangci.yaml run ./kv --timeout=5m
  • git diff --check
  • git verify-commit HEAD

@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: 4e49dfbea8

ℹ️ 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/tso_fsm.go Outdated
return
}
hlc.SetPhysicalCeiling(ceilingMs)
hlc.Observe(tsoLeaseAllocationFloor(ceilingMs))

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 Persist the post-lease allocation floor

When the dedicated TSO FSM is wired, applying/restoring a lease with this Observe only records ceilingMs|maxLogical in memory while snapshots/WAL entries still persist just ceilingMs. If a leader applies ceiling C, serves one timestamp, NextBatchFenced returns (C+1,0) because the floor is already C|maxLogical; after a restart or re-election with only the same lease entry/snapshot, applyTSOLeaseToHLC recreates the same floor and can serve (C+1,0) again. This means the new floor does not actually prevent reissuing timestamps unless the allocation floor/current HLC is made durable or allocation is blocked until a higher ceiling is committed.

Useful? React with 👍 / 👎.

Comment thread kv/tso_fsm.go Outdated
Comment on lines +72 to +73
if hlc == nil || ceilingMs <= 0 {
return

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 Reject non-positive TSO lease ceilings

When a full HLC-lease entry encodes 0 or a high-bit value that decodes negative, this branch returns without error, so Apply returns nil and the engine advances the applied index. Renewals only generate positive Unix-ms ceilings, so these are malformed group-0 lease entries; accepting them masks the bad proposal instead of using the new fail-closed HaltApply path. Please reject non-positive ceilings in Apply while keeping snapshot restore's empty-state handling separate if needed.

Useful? React with 👍 / 👎.

Comment thread kv/tso_fsm.go Outdated
if f != nil {
hlc = f.hlc
}
return &tsoFSMSnapshot{ceilingMs: hlcCeilingFromHLC(hlc)}, nil

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 Keep snapshots sourced from TSO-applied leases

When group 0 is wired during the documented bridge, this FSM is likely to share the process HLC that shard FSMs also advance; then Snapshot can serialize a ceiling raised by some data-group lease even if the TSO group never committed that lease. If a shard renewal succeeds while the group-0 proposal fails or lags, installing/restoring the group-0 snapshot advances dedicated TSO state beyond its own consensus log. Keep a TSO-FSM-owned ceiling field updated only from this FSM's Apply/Restore path and snapshot that value instead.

Useful? React with 👍 / 👎.

@bootjp

bootjp commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Updated commit: 44e6c18

Addressed current-head review findings:

  • TSOStateMachine now keeps TSO-owned ceiling and allocation-floor state, and snapshots those fields instead of reading the shared HLC mirror. This prevents data-group lease renewals from contaminating group-0 snapshots.
  • HLC lease entries now advance only the physical ceiling; allocation-floor advancement is an explicit TSO FSM entry and is persisted through the FSM snapshot format.
  • Non-positive HLC lease ceilings and malformed allocation-floor entries now halt apply through ErrTSOStateMachineInvalidEntry.
  • The design doc now reflects the ceiling/floor snapshot contract and the remaining runtime group-0 wiring work.

Caller/semantic audit:

  • NewTSOStateMachine is still referenced only by this branch's TSO FSM tests; runtime group-0 wiring remains documented as a follow-up.
  • kvFSM HLC lease behavior is unchanged; the new allocation-floor opcode is TSO-FSM-local and intentionally uses a byte that data-group kvFSM already routes through the fail-closed reserved range.

Validation:

  • go test ./kv -run 'TestTSOStateMachine|TestLocalTSOAllocator|TestBatchAllocator|TestShardedCoordinator.*Timestamp|TestCoordinateUsesTSOAllocator' -count=1 -timeout=180s
  • go test ./kv -count=1 -timeout=300s
  • golangci-lint --config=.golangci.yaml run ./kv --timeout=5m
  • git diff --check
  • git verify-commit HEAD

Additional check:

  • go test ./... -count=1 -timeout=600s was attempted; all completed packages reported ok, but adapter timed out at 600s in long-running gRPC/SQS adapter tests. This is outside the changed kv/doc surface.

@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: 44e6c1852b

ℹ️ 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/tso_fsm.go Outdated
Comment on lines +104 to +105
case tsoSnapshotV1Len:
ceilingMs = int64(binary.BigEndian.Uint64(payload[:hlcLeasePayloadLen])) //nolint:gosec // legacy snapshot value.

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 Derive the floor when restoring legacy TSO snapshots

When this restores an 8-byte snapshot emitted by the previous TSO FSM format, allocationFloor remains 0. Those legacy snapshots were produced by code that also observed tsoLeaseAllocationFloor(ceilingMs) on apply/restore to prevent a restarted or newly elected TSO leader from handing out logical slots from the old ceiling millisecond. After upgrading from such a snapshot, the HLC mirror has only the ceiling, so the next NextBatchFenced can start at ceiling<<16|0 and overlap timestamps already covered by the prior floor; derive the legacy floor from the decoded ceiling before calling restoreSnapshotState.

Useful? React with 👍 / 👎.

Comment thread kv/tso_fsm.go Outdated
Comment on lines +150 to +151
f.ceilingMs.Store(ceilingMs)
f.allocationFloor.Store(allocationFloor)

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 TSO-owned fields monotonic during restore

When Restore is called on an FSM that has already applied a higher ceiling/floor (the monotonic-restore test exercises this shape), these unconditional stores regress the FSM-owned state even though the HLC mirror remains high via its monotonic setters. Because Snapshot() now serializes f.ceilingMs and f.allocationFloor rather than the HLC, the next snapshot from that FSM can persist the lower values and lose the higher applied TSO floor/ceiling; restore should max these fields the same way apply does.

Useful? React with 👍 / 👎.

@bootjp

bootjp commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Updated commit: 5b6fd30

Addressed current-head P2 findings:

  • Restoring a legacy 8-byte TSO FSM snapshot now derives the allocation floor from the restored ceiling, preserving the previous format's floor behavior across upgrade/restart.
  • Restore now updates TSO-owned ceiling/floor fields monotonically instead of unconditionally storing lower snapshot values; the HLC mirror and subsequent snapshots stay at the highest applied TSO state.
  • The design doc Restore example now reflects the 8-byte legacy and 16-byte current snapshot handling.

Caller/semantic audit:

  • NewTSOStateMachine remains referenced only by this branch's TSO FSM tests; runtime group-0 wiring remains a documented follow-up.
  • The restore semantic change is limited to TSOStateMachine and matches the monotonic apply path.

Validation:

  • go test ./kv -run 'TestTSOStateMachine|TestLocalTSOAllocator|TestBatchAllocator|TestShardedCoordinator.*Timestamp|TestCoordinateUsesTSOAllocator' -count=1 -timeout=180s
  • go test ./kv -count=1 -timeout=300s
  • golangci-lint --config=.golangci.yaml run ./kv --timeout=5m
  • git diff --check
  • git verify-commit HEAD

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: 5b6fd30121

ℹ️ 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".

Base automatically changed from design/dedicated-tso-group to main July 19, 2026 06:07

@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: 46be7a832f

ℹ️ 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 main.go Outdated
Comment on lines +1195 to +1196
sg := &kv.ShardGroup{Engine: runtime.engine}
sg.Txn = kv.NewLeaderProxyForShardGroup(sg, kv.WithProposalObserver(proposalObserver))

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 Don't count the TSO group in raft-envelope cutover scope

When group 0 is configured alongside a single data group, this returned ShardGroup is added to encWiring.attachRaftEnvelopeGroup in buildShardGroups. I checked adapter/encryption_admin.go's EnableRaftEnvelope path and main_raft_envelope_wiring.go's ValidateCutoverScope: the cutover precheck rejects unless exactly one group is attached, so enabling the dedicated TSO group turns a previously valid single-data-group raft-envelope rollout into FailedPrecondition (got 2) even though group 0 holds no user data. Exclude group 0 from that scope check or handle its cutover separately.

Useful? React with 👍 / 👎.

Comment thread kv/tso_fsm.go Outdated
Comment on lines +181 to +183
legacy, err := hasLegacyKVFSMSnapshotHeader(br)
if err != nil || !legacy {
return legacy, 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 Preserve headerless legacy group-0 snapshots

When upgrading a reserved group-0 log that previously ran kvFSM from a version that emitted the supported headerless snapshot format, this probe returns legacy=false, so Restore falls through to readTSOSnapshotState and accepts only exactly 8 or 16 bytes. ReadSnapshotHeader deliberately leaves non-magic/headerless snapshots unconsumed for the inner store restore path, and those raw store payloads are not constrained to the TSO lengths, so affected nodes can fail to restore or misparse a 16-byte store payload instead of draining the legacy MVCC snapshot. Detect and drain headerless kvFSM snapshots before trying the raw TSO format.

Useful? React with 👍 / 👎.

@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: e701cb221b

ℹ️ 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 main_encryption_admin.go Outdated
Comment thread kv/tso_fsm.go Outdated
@bootjp
bootjp force-pushed the design/dedicated-tso-fsm branch from e701cb2 to 97a42ba Compare July 19, 2026 12:17
@bootjp

bootjp commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

Evidence for current HEAD 97a42ba72c18205fcb84a43ad4ca0cedc48efa31:

  • Dedicated TSO encryption-admin wiring keeps mutators disabled but retains LeaderView; ResyncSidecar now rejects group-0 followers.
  • Allocation-floor proposals use a versioned TSO envelope. Bare/future encryption-reserved entries cannot be decoded as TSO state and halt fail-closed.
  • Semantic audit: the sole production encryptionAdminWiringForGroup caller was checked; data-group mutator behavior is unchanged. Allocation-floor encoding remains TSO-FSM-local in this PR and all apply/classifier/test consumers use the same envelope.
  • TLA audit: make tla-check matched all safe and expected-gap model outcomes.
  • The design remains Partial until the full dependency stack is clear.

History sanitation:

  • Rebuilt on current origin/main 915bc77795d940644e09ce3ee521626e64e2f442.
  • PR-visible history is one commit, authored and committed by bootjp <contact@bootjp.me>.
  • GitHub signature verification is verified: true.
  • Desired tree hash before and after rebuild: 5d4fd0dccfb27bf859311df68cdf9a119fcb33bc.

Validation:

  • go test ./kv . -count=1
  • go test -race ./kv . -run "TestTSOStateMachine|TestEncryptionAdmin_(DedicatedTSOGroup|DataGroup)|TestRegisterEncryptionAdminServer" -count=1
  • golangci-lint run ./... --timeout=5m --allow-parallel-runners (0 issues)
  • make tla-check
  • git diff --check origin/main..HEAD

@codex review

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
kv/tso_fsm.go (1)

94-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

//nolint:gosec の抑制は境界チェックへの置き換えを検討してください。

コーディングガイドラインでは //nolint の追加を避け、リファクタリングを優先することが求められています。タイムスタンプの uint64int64 変換については、変換前に math.MaxInt64 との境界チェックを行う小さなヘルパー(例: func unixMillisToInt64(v uint64) (int64, error))を導入することで、複数箇所の //nolint:gosec を排除できます。既存コードで許容される慣例であれば据え置きで構いませんが、新規追加分については抑制の集約を推奨します。

As per coding guidelines: "Avoid adding //nolint unless absolutely required; prefer refactoring."

Also applies to: 165-167, 197-197, 325-325

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kv/tso_fsm.go` at line 94, Replace the new `//nolint:gosec` suppressions
around the `ceilingMs` conversion and the corresponding conversions at the other
referenced sites with a shared checked conversion helper, such as
`unixMillisToInt64`. Have the helper validate against `math.MaxInt64` before
converting and return an error for overflow, then propagate or handle that error
at each caller while preserving existing timestamp behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@kv/tso_fsm.go`:
- Line 94: Replace the new `//nolint:gosec` suppressions around the `ceilingMs`
conversion and the corresponding conversions at the other referenced sites with
a shared checked conversion helper, such as `unixMillisToInt64`. Have the helper
validate against `math.MaxInt64` before converting and return an error for
overflow, then propagate or handle that error at each caller while preserving
existing timestamp behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 378b480f-a7cd-42e8-a4ec-e03b8e9fa899

📥 Commits

Reviewing files that changed from the base of the PR and between 915bc77 and 97a42ba.

📒 Files selected for processing (7)
  • docs/design/2026_04_16_partial_centralized_tso.md
  • kv/tso_fsm.go
  • kv/tso_fsm_test.go
  • main.go
  • main_encryption_admin.go
  • main_encryption_admin_test.go
  • multiraft_runtime_test.go

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: 97a42ba72c

ℹ️ 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 added 2 commits July 19, 2026 22:58
## Summary
- route dedicated group-0 timestamp requests to the current TSO leader
- commit every returned window end and fence each leader term above
authoritative data-group commit floors
- add synchronous fail-closed shadow migration and a durable one-way
cutover marker
- preserve rolling compatibility by rejecting legacy timestamp responses
without durable reservation metadata
- update the centralized TSO design status through M6

## Migration safety
- shadow candidates are serialized through group 0 before the legacy
value is returned
- overlapping legacy candidates are discarded and retried
- cutover commits the marker before the first production window
- group-0, shadow, and cutover failures stop timestamp issuance instead
of falling back

## Validation
- `go test ./kv -count=1 -timeout=10m`
- `go test . -count=1 -timeout=10m`
- `go test ./adapter -run
'Test(DistributionServerGetTimestamp|GRPCServer_RawLatestCommitTS_EmptyKeyReturnsGlobalWatermark)'
-count=1 -timeout=5m`
- `go test ./... -run '^$' -count=1 -timeout=10m`
- `go test -race ./kv -run
'Test(RaftTSOAllocator|LeaderRoutedTSOAllocator|ShadowTimestampAllocator|ShardStoreGlobalCommittedTimestampFloor)'
-count=1 -timeout=10m`
- `golangci-lint --config=.golangci.yaml run ./kv ./adapter .
--timeout=5m`
- `make gen
BREAKING_AGAINST='../.git#subdir=proto,branch=design/dedicated-tso-runtime'`

## Stack
Base: #1103

Author: bootjp

@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: aa94f4eedb

ℹ️ 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/tso_raft.go Outdated
Comment on lines +131 to +132
if min == ^uint64(0) {
return empty, errors.WithStack(ErrTxnCommitTSRequired)

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 Reject near-overflow TSO minimums

When ReserveBatchAfter is called with n > 1 and a minimum close to MaxUint64 (for example Distribution.GetTimestamp{count:2, min_timestamp:MaxUint64-1}), this guard lets the request through; a.clock.Observe(min) then causes NextBatchFenced to wrap the reserved base to 0 and the HLC current value to 1 before the later response validation can fail, and commitAllocationFloor can persist that regressed floor. Reject any min that cannot fit the requested window before observing it.

Useful? React with 👍 / 👎.

Comment thread main.go
cfg.engine,
distCatalog,
adapter.WithDistributionCoordinator(coordinate),
adapter.WithDistributionTimestampAllocator(tsoWiring.serverAllocator),

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 Gate mutating timestamp RPCs during startup rotation

When a client can reach the raft gRPC listener during startup rotation, this wiring makes Distribution.GetTimestamp call the dedicated allocator and commit group-0 allocation/cutover entries, but startupRotationGatedMethod only gates Distribution.SplitRange plus the RawKV/Transactional/Internal/Admin mutators. That lets timestamp proposals bypass the same startup mutator fence before waitRotateOnStartup completes and before public traffic is marked ready; include GetTimestamp in the gate or delay exposing the allocator until the gate opens.

Useful? React with 👍 / 👎.

Comment thread adapter/distribution_server.go Outdated
return &pb.GetTimestampResponse{Timestamp: s.engine.NextTimestamp()}, nil
}

reservation, err := s.allocateTimestampReservation(ctx, count, minTimestamp, activateCutover)

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 Gate TSO cutover activation

When group 0 is configured for Phase A/B but this node was not started with --tsoEnabled, any caller that can reach Distribution.GetTimestamp can set activate_cutover=true; this line forwards that untrusted request bit into the allocator, which commits the one-way cutover marker. Since main wires the server allocator even before production cutover, this bypasses the operator flag and the all-nodes-shadow rollout precondition, so keep activation tied to local/internal configuration rather than the public RPC field.

Useful? React with 👍 / 👎.

Comment thread kv/shard_store.go Outdated
return 0, errors.Wrapf(ErrTSOCommitFloorUnavailable,
"data group %d is not led by this node", groupID)
}
if _, err := linearizableReadEngineCtx(nonNilTSOContext(ctx), engine); err != nil {

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 Bound local floor ReadIndex calls

When the TSO leader also leads a data group and the incoming timestamp request has no deadline, this local LinearizableRead inherits an unbounded context, unlike verifyLeaderEngineCtx and the remote floor RPC timeout. If that ReadIndex stalls, ReserveBatchAfter is still holding the allocator mutex while initializing the term floor, so one stuck local floor probe can wedge all subsequent TSO allocations; wrap this path in a bounded context.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot mentioned this pull request Jul 23, 2026
bootjp added 2 commits July 23, 2026 23:25
Author: bootjp

## Summary
- persist a one-way Phase D marker and floor after the production
cutover marker
- stop data-group HLC renewal and legacy/shadow timestamp issuance once
Phase D is durable
- validate caller-supplied cross-shard SSI timestamps at the current
group-0 leader before allocating a commit timestamp or proposing writes
- migrate adapter read-modify-write snapshots to dedicated TSO
allocations while preserving single-shard and pre-Phase-D compatibility
- retain the parent branch's same-term allocation fences and
RawLatestCommitTS group_id/leader_fenced schema

## Safety
- activation commits cutover, Phase D, and the first post-Phase-D
allocation in order
- validation accepts only timestamps in the durable post-marker
allocation interval and fails closed on inactive state, stale
leadership, unsupported routing, or out-of-range values
- leader term is revalidated after floor/marker work and after the
allocation-floor commit
- proto output was regenerated from the combined schema

## Tests
- make gen
BREAKING_AGAINST='../.git#subdir=proto,branch=design/dedicated-tso-leader-routing'
- go test ./kv ./adapter . -run
'TSO|PhaseD|RenewHLC|CrossShard|ValidateTimestamp|RawLatestCommitTS'
-count=1
- go test ./kv -run
'TestRaftTSOAllocator(RejectsTermChange|DropsCommittedWindow|CommitsPhaseD)'
-count=1
- go test ./adapter -run
'^Test_consistency_satisfy_write_after_read_sequence$' -count=1
-timeout=10m
- go test ./adapter -run '^TestRedis_StreamXReadLatencyIsConstant$'
-count=1 -timeout=10m
- golangci-lint run ./... --timeout=5m --allow-parallel-runners

The full parallel go test ./... run completed all packages except two
long adapter stress tests after their test HLC ceilings expired under
concurrent local load; both tests passed when rerun independently.

@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: ca1a812729

ℹ️ 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/tso_raft.go
Comment on lines +337 to +341
if timestamp == 0 || timestamp > end {
return errors.Wrapf(ErrTSOTimestampInvalid,
"timestamp=%d phase_d_floor=%d allocation_floor=%d", timestamp, floor, end)
}
if timestamp <= floor {

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 Reject unallocated Phase-D timestamp gaps

When Phase D is activated with no previous floor, or after a wall-clock/lease jump, the first reserved TSO Base can be far above PhaseDFloor (for example PhaseDFloor == 0 and Base == current_ms<<16). This validation accepts any timestamp in (floor, allocationFloor], so ValidateDurableTimestamp(1) can succeed even though group 0 never issued that timestamp, letting caller-supplied cross-shard StartTS values in the gap bypass the durable-TSO allocation gate. Track the first post-D base/ranges or set the marker floor to Base-1 before treating the interval as valid.

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

Useful? React with 👍 / 👎.

Comment thread adapter/redis_txn.go Outdated
Comment on lines +2731 to +2736
func (r *RedisServer) beginTxnStartTS(ctx context.Context, label string) (uint64, error) {
readTimestamp, err := r.beginTxnReadTimestamp(ctx, label)
if err != nil {
return 0, err
}
return readTimestamp.Timestamp(), nil

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 Redis read vouchers for wide writes

When Phase D is active and the adapter read watermark predates the Phase-D floor, beginTxnReadTimestamp returns a voucher that authorizes that applied snapshot, but this helper discards it and returns only the numeric timestamp. Callers such as wide set/zset/list mutations then dispatch via dispatchElems/plain Dispatch; in a sharded catalog where the wide collection storage prefixes resolve to more than one group, the coordinator treats the StartTS as caller-supplied without the bound voucher and rejects the command as a pre-Phase-D timestamp. Return the ReadTimestamp through these paths or bind it with DispatchWithReadTimestamp at dispatch time.

Useful? React with 👍 / 👎.

Comment thread kv/tso.go
if err != nil {
return nil, err
}
defer revoke()

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 Avoid revoking another in-flight read voucher

When the same ReadTimestamp is used by overlapping DispatchWithReadTimestamp calls during Phase D, each call increments the same (timestamp, ref) use count, but a call that successfully reaches consumeAppliedReadTimestampVoucher still runs this deferred revoke afterward. If one dispatch consumes its token while another prepared token is still in the map, the defer can delete/decrement the other dispatch's authorization before it validates, causing valid applied-watermark operations to fail as pre-Phase-D timestamps. Make the revoke conditional on the prepared token remaining unused, or mint a distinct ref per prepared dispatch.

Useful? React with 👍 / 👎.

Comment thread adapter/s3.go
err := s.retryS3Mutation(r.Context(), func() error {
readTS := s.readTS()
startTS, err := s.txnStartTS(r.Context(), readTS)
readTimestamp, err := s.beginTxnReadTimestamp(r.Context(), readTS, "s3 delete object: begin read timestamp")

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 Bind S3 delete-object read vouchers

When Phase D is active and the applied S3 read watermark predates the Phase-D floor, this call can return a ReadTimestamp carrying the voucher for the bucket-metadata read at readTS, but the later delete dispatch uses bare s.coordinator.Dispatch with only the object-manifest mutation. If the bucket metadata route and object route are split, the coordinator sees a single mutation shard and no read keys, so it accepts the legacy StartTS without consuming the voucher even though the operation semantically read another shard. Dispatch this transaction with readTimestamp.WithDispatchVoucher/DispatchWithReadTimestamp.

Useful? React with 👍 / 👎.

@bootjp

bootjp commented Jul 24, 2026

Copy link
Copy Markdown
Owner Author

Addressed the latest review and merge-resolution findings:

  • bounded local TSO floor ReadIndex calls
  • rejected near-overflow TSO minimum windows before observing clocks
  • committed Phase-D activation at reservation base-1 and rechecked leader term before allocation floor commit
  • preserved applied-read vouchers through Redis/S3 transactional dispatch paths
  • kept startup timestamp allocation behind the startup rotation gate
  • resolved lint regressions from the merge update

Tests:

  • go test ./kv -count=1 -timeout=600s
  • go test ./adapter -run 'TestRedis.(List|Set|ZSet|ZAdd|ZRem|Hash|Expire|XTrim|PhaseD|Dedup)|Test.(List|Set|ZSet|Hash|Expire|XTrim).*|TestS3.*PhaseDBindsReadVoucher|TestS3CommitUploadPart|TestS3Server_AdminDeleteObject|TestDistribution.*Timestamp|Test.*Timestamp.*PhaseD|Test.*PhaseD|Test.*ReserveBatch|Test.*ValidateTimestamp' -count=1 -timeout=300s\n- go test . -run 'TestStartupGatedCoordinator|TestStartupRotation|Test.*TSO|Test.*Timestamp|Test.*EncryptionAdmin' -count=1 -timeout=300s\n- golangci-lint run ./kv ./adapter . --timeout=5m\n- commit hook: golangci-lint --config=.golangci.yaml run --fix\n- git diff --check\n- git verify-commit HEAD\n\nNote: earlier full go test ./adapter . -count=1 -timeout=600s reached the adapter package timeout after the root package completed; the targeted adapter suites above passed.\n\n@codex review

@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 (fea84f9):

  • distribution/catalog.go
  • distribution/catalog_test.go
  • kv/coordinator.go
  • kv/sharded_coordinator.go

What to check, by subsystem:

  • kv/hlc*.goNext() 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.goRunHLCLeaseRenewal, 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

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: fea84f9b92

ℹ️ 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 force-pushed the design/dedicated-tso-fsm branch from fea84f9 to 227aff5 Compare July 24, 2026 13:09
@bootjp

bootjp commented Jul 24, 2026

Copy link
Copy Markdown
Owner Author

Review feedback addressed on the latest head.

Changes:

  • Keep Phase-D TSO reservations contiguous after the committed floor and reject min_timestamp gaps instead of blessing unallocated ranges.
  • Drain long headerless legacy KV FSM snapshots during TSO restore instead of parsing them as raw TSO state.
  • Document the new Phase-D contiguous allocation and legacy snapshot behavior.

Validation:

  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./kv -count=1 -timeout=600s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter -run 'TestDistributionServerGetTimestamp|TestS3(DeleteObject|AdminDeleteObject)PhaseDBindsReadVoucher|TestS3BeginTxnReadTimestampPhaseD|TestS3(CreateBucket|AdminCreateBucket|AdminPutObject)PhaseDBindsReadVoucher' -count=1 -timeout=300s\n- GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test . -run 'TestStartupGatedCoordinator|TestStartupRotation|Test.*TSO|Test.*Timestamp|Test.*EncryptionAdmin' -count=1 -timeout=300s\n- GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint run ./kv ./adapter . --timeout=5m\n- git diff --check\n\n@codex review

@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 (227aff5):

  • distribution/catalog.go
  • distribution/catalog_test.go
  • kv/coordinator.go
  • kv/sharded_coordinator.go

What to check, by subsystem:

  • kv/hlc*.goNext() 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.goRunHLCLeaseRenewal, 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

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@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

if _, err := d.coordinator.Dispatch(ctx, req); err != nil {

P1 Badge Bind migration dispatches to the read voucher

This migration path receives a ReadTimestamp but dispatches the cross-key table-generation update without binding/registering its voucher. Under Phase D, tables created before the cutover commonly read at a pre-D applied watermark; when the metadata/generation keys are on different shards, the coordinator rejects this caller-supplied StartTS as pre-D instead of consuming the intended applied-read voucher, blocking the legacy key migration. Dispatch through kv.DispatchWithReadTimestamp(readTimestamp.WithDispatchVoucher(ctx), ...) for these migration writes.

ℹ️ 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".

return err
}
req.StartTS = readTS
if _, err := d.coordinator.Dispatch(ctx, req); err == nil {

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 Bind the Phase-D voucher on CreateTable dispatch

When Phase D is required and readTS is an applied pre-D watermark, beginTxnReadTimestamp returns a voucher that is only registered by kv.DispatchWithReadTimestamp; this direct Dispatch drops that capability. If the table metadata and generation keys route to different shards, ShardedCoordinator treats StartTS=readTS as an arbitrary caller timestamp and rejects it with ErrTSOTimestampPrePhaseD, so CreateTable can fail after Phase-D activation on upgraded clusters with pre-D data. Use the read timestamp's dispatch helper here, as the item-write path does.

Useful? React with 👍 / 👎.

Comment thread adapter/sqs_messages.go
Comment on lines +634 to +638
readTimestamp, err := s.beginTxnReadTimestamp(ctx, "sqs send message: begin read timestamp")
if err != nil {
return nil, 0, errors.WithStack(err)
}
readTS := readTimestamp.Timestamp()

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 Preserve SQS send read vouchers through dispatch

Here beginTxnReadTimestamp may mint a Phase-D applied-read voucher, but the helper immediately collapses it to a bare uint64; the standard SendMessage paths then dispatch StartTS=readTS via plain coordinator.Dispatch. On Phase-D clusters with pre-D queue metadata, any standard send whose queue metadata/read keys and message index writes route across shards is rejected as an arbitrary pre-D timestamp instead of consuming the voucher, so standard SQS sends can fail after cutover. Return and bind the ReadTimestamp through the send dispatch path.

Useful? React with 👍 / 👎.

Comment thread adapter/sqs_catalog.go
if err != nil {
return false, errors.WithStack(err)
}
readTS := readTimestamp.Timestamp()

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 Bind SQS queue-generation mutations to vouchers

This path keeps the ReadTimestamp only long enough to extract readTS; the later CreateQueue transaction, and the analogous DeleteQueue/PurgeQueue generation bumps, still dispatch with plain s.coordinator.Dispatch. Under Phase D, when the queue meta/gen/tombstone prefixes route to different shards and the applied watermark is pre-D, cross-shard validation rejects that StartTS as an arbitrary caller timestamp, so queue create/delete/purge can fail after cutover. Carry the ReadTimestamp through those dispatches and bind it with DispatchWithReadTimestamp.

Useful? React with 👍 / 👎.

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