Skip to content

Add live backup pin FSM substrate#1056

Open
bootjp wants to merge 10 commits into
mainfrom
design/live-backup-pin-substrate
Open

Add live backup pin FSM substrate#1056
bootjp wants to merge 10 commits into
mainfrom
design/live-backup-pin-substrate

Conversation

@bootjp

@bootjp bootjp commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a live-backup pin/extend/release FSM envelope for retaining read timestamps during future online logical backup scans
  • extend ActiveTimestampTracker with deadline-based backup pins, expiry sweeping, limits, and idempotent release/extend behavior
  • wire shard FSMs to the same ActiveTimestampTracker used by local compaction

Tests

  • go test ./kv -run 'Test(ActiveTimestampTracker|Backup|ApplyBackup)' -count=1 -timeout=240s
  • go test ./kv -count=1 -timeout=300s
  • go test . -run 'TestBuildShardGroupsWithEtcdEngineRoutesAcrossGroups|TestBuildShardGroupsWithEtcdEngineRestartsAcrossGroups' -count=1 -timeout=240s
  • go test . -run 'TestRaftBootstrapMembers_E2E|TestRaftBootstrapMembers_MultiGroup' -count=1 -timeout=300s
  • go test ./... -run TestNonexistent -count=0 -timeout=300s
  • go test . -count=1 -timeout=300s
  • golangci-lint run ./kv . --timeout=5m
  • git diff --check
  • git verify-commit HEAD

Author: bootjp

Summary by CodeRabbit

  • 新機能
    • 期限付きバックアップピンの登録・延長・解放に対応し、期限切れを自動回収できるようになりました(上限数・スイープ間隔などを設定可能)。
    • バックアップピンを複数Raftグループ単位で管理し、最古の参照時刻計算にも反映されます。
    • 管理APIでライブバックアップ(開始/更新/終了、スコープ一覧、ストリーミング)とノードバージョン取得を提供します。
  • 改善
    • 無効なピンや期限切れ操作、不整合を安全に検出して失敗します。バックアップ中はスナップショット生成をブロックします。

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

ActiveTimestampTracker、バックアップFSM、固定長ワイヤ、AdminバックアップRPC、ルートスナップショット走査、タイムスタンプフロア、リーダー転送を追加し、起動配線と関連インターフェースを更新しました。

Changes

ライブ論理バックアップ

Layer / File(s) Summary
バックアップピンとワイヤ形式
kv/active_timestamp_tracker.go, kv/backup_codec.go, kv/*_test.go
期限、上限、グループスコープ、期限切れ回収、スイーパー、固定長Pin/Extend/Release/Reserve/Unreserveワイヤを追加しました。
FSM適用とタイムスタンプフロア
kv/fsm.go, kv/fsm_backup.go, kv/fsm_backup_test.go
バックアップ操作をFSMからトラッカーへ適用し、フロア永続化、HLC観測、遅延書き込みフェンス、アクティブピン中のスナップショット拒否を追加しました。
バックアップ走査と出力デコード
kv/backup_scan.go, internal/backup/*, distribution/engine.go, kv/shard_store.go
ルートスナップショット、ページング、所有権解決、スコープ分類、Redis/SQS出力デコードを追加しました。
全グループフェンスと転送
kv/coordinator.go, kv/sharded_coordinator.go, kv/leader_*, adapter/internal.go
全グループのリースTS取得、タイムスタンプフロア観測、管理提案とリース読み取りのリーダー転送を追加しました。
AdminバックアップAPI
adapter/admin_backup.go, adapter/admin_grpc.go, main.go
Begin/Renew/End、スコープ一覧、ストリーミング、署名トークン、ピン・予約fanout、セッション更新、補償処理を追加しました。
プロトコルと起動配線
proto/*.proto, main.go, internal/raftengine/*, main_*_test.go
バックアップRPCと内部転送RPCを定義し、共有トラッカー、設定検証、Admin依存、スナップショット周期契約を配線しました。
コンパクション互換性
kv/compactor.go, kv/txn_keys.go, kv/*_test.go
グループ別バックアップピンをコンパクション境界へ反映し、トランザクション内部キー判定とテスト用エンジン契約を更新しました。

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BackupClient
  participant AdminServer
  participant ShardedCoordinator
  participant kvFSM
  participant ActiveTimestampTracker
  participant BackupScanner
  BackupClient->>AdminServer: BeginBackup
  AdminServer->>ShardedCoordinator: LeaseReadAllGroupsTimestamp
  AdminServer->>kvFSM: reserve/pin proposals
  kvFSM->>ActiveTimestampTracker: apply backup pin
  AdminServer->>BackupScanner: capture snapshot and scan at read timestamp
  BackupScanner-->>AdminServer: scoped backup records
  AdminServer-->>BackupClient: StreamBackup responses
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed ライブバックアップ用のピン/延長/解放を支えるFSM基盤の追加を適切に要約しており、変更内容と整合しています。
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch design/live-backup-pin-substrate

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.

@bootjp

bootjp commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

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

  • kv/fsm.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.

@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 introduces a backup pinning mechanism to the ActiveTimestampTracker and kvFSM to retain MVCC versions at live-backup read timestamps during background compaction. It adds FSM commands for pinning, extending, and releasing backup pins, alongside a background sweeper to reap expired pins. The reviewer provided critical feedback to improve robustness: first, expired backup pins should be ignored in Oldest() to avoid blocking compaction before the sweeper runs; second, validation and limit errors must not halt the FSM to prevent DoS vulnerabilities; and third, a graceful shutdown mechanism (Close() and stopCh) should be added to the tracker to prevent goroutine leaks from the background sweeper.

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.

Comment thread kv/active_timestamp_tracker.go Outdated
Comment thread kv/fsm_backup.go
Comment thread kv/active_timestamp_tracker.go
Comment thread kv/active_timestamp_tracker.go
Comment thread kv/active_timestamp_tracker.go
@bootjp
bootjp force-pushed the design/live-backup-pin-substrate branch from 9a7491c to b9e3e10 Compare July 10, 2026 19:47
@bootjp

bootjp commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Addressed latest-head review findings:

  • expired backup pins are ignored by Oldest() before the sweeper runs
  • invalid backup pins and active-backup limit failures now return non-fatal apply errors instead of halting the FSM
  • ActiveTimestampTracker now has an idempotent Close() path for the backup-pin sweeper

Validation:

  • go test ./kv -run 'Test(ActiveTimestampTracker|ApplyBackup|BackupPayload)' -count=1 -timeout=240s\n- go test ./kv -count=1 -timeout=300s\n- golangci-lint run ./kv --timeout=5m\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 (b9e3e10):

  • kv/fsm.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

Here are some automated review suggestions for this pull request.

Reviewed commit: b9e3e104a4

ℹ️ 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/active_timestamp_tracker.go Outdated
Comment on lines +149 to +150
if _, exists := t.backupPins[pinID]; !exists && len(t.backupPins) >= t.maxBackupPins {
return errors.WithStack(ErrTooManyActiveBackups)

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 Reap expired pins before enforcing the active limit

When a replica still has maxBackupPins expired entries that Oldest() already ignores but the sweeper has not deleted yet, this raw len(t.backupPins) check returns ErrTooManyActiveBackups for a new valid backup pin. Since applyBackup treats that error as non-fatal, the Raft entry is advanced without recording the pin on that replica; if it later compacts or becomes the backup-serving leader, the backup read timestamp is not retained. Reap or exclude expired pins before applying the cap.

Useful? React with 👍 / 👎.

Comment thread kv/active_timestamp_tracker.go Outdated
Comment on lines +170 to +171
pin.deadline = deadline
t.backupPins[pinID] = pin

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 late renewals for expired backup pins

If a BackupExtend arrives after the previous deadline but before the sweeper has deleted the entry, Oldest() has already stopped honoring this pin, so compaction may have advanced past the backup's read timestamp during that gap. This assignment makes the expired pin active again and reports a successful renewal, allowing a backup to continue even though its retention fence was temporarily absent. Treat expired pins as missing/invalid before extending them.

Useful? React with 👍 / 👎.

Comment thread kv/backup_codec.go
Comment thread main.go
@bootjp
bootjp force-pushed the design/live-backup-pin-substrate branch from b9e3e10 to 91a7d04 Compare July 10, 2026 20:01
@bootjp

bootjp commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Addressed the latest-head findings:

  • backup pin capacity checks now reap expired pins before enforcing the limit
  • expired or missing backup renewals now return ErrInvalidBackupPin instead of reactivating a stale retention fence
  • deadline_ms=0 decodes to time.Time{} so apply validation rejects it as invalid
  • backup pin tracker entries are scoped by Raft group, so one group's Release cannot remove another group's pin for the same pin_id

Validation:

  • go test ./kv -run 'Test(ActiveTimestampTracker|BackupCodec|ApplyBackup|BackupPayload)' -count=1 -timeout=240s\n- go test ./kv -count=1 -timeout=300s\n- golangci-lint run ./kv --timeout=5m\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 (91a7d04):

  • kv/fsm.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

Here are some automated review suggestions for this pull request.

Reviewed commit: 91a7d04265

ℹ️ 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_backup.go Outdated
Comment on lines +27 to +28
if errors.Is(err, ErrTooManyActiveBackups) || errors.Is(err, ErrInvalidBackupPin) {
return 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.

P1 Badge Do not drop committed backup pins as proposal errors

When this branch is reached for the local-state-dependent errors (capacity, missing/expired renewals), applyBackup returns a plain error instead of haltErr, so the Raft engine treats the entry as applied and only the proposer observes the response. In a multi-replica group, one replica can hit this branch because its tracker still has an unexpired pin or its clock has already expired a renewal while the leader installed the new/extended fence; that replica then advances without retaining read_ts, and after a leader change or local compaction the backup can lose the version it was meant to protect.

Useful? React with 👍 / 👎.

Comment thread kv/active_timestamp_tracker.go Outdated
t.mu.Lock()
expired := t.reapExpiredBackupPinsLocked(time.Now())
key := newBackupPinKey(pinID, groupID)
if _, exists := t.backupPins[key]; !exists && len(t.backupPins) >= t.maxBackupPins {

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 Count backup capacity by pin ID, not group entry

Now that backupPins is keyed by (pin_id, groupID) and every shard FSM shares this tracker, len(t.backupPins) charges one slot per Raft group. A single logical backup fan-out with the same pin_id across more than 64 groups will fill the default limit and the next group’s BackupPin returns ErrTooManyActiveBackups, so large sharded deployments cannot start even one backup unless the limit is raised by group count.

Useful? React with 👍 / 👎.

@bootjp
bootjp force-pushed the design/live-backup-pin-substrate branch from 91a7d04 to b04b7fb Compare July 10, 2026 20:19
@bootjp

bootjp commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Addressed latest-head review findings:

  • committed backup Pin entries now use an apply path that does not enforce the local active-backup cap, so a replica does not advance without installing the retention fence
  • committed backup Extend entries treat missing/expired local pins as no-op apply results instead of proposal errors, while direct Extend still rejects missing/expired renewals
  • the active-backup limit now counts logical pin IDs rather than per-group tracker entries, so one backup fan-out across groups consumes one capacity slot

Validation:

  • go test ./kv -run 'Test(ActiveTimestampTracker|BackupCodec|ApplyBackup|BackupPayload)' -count=1 -timeout=240s
  • go test ./kv -count=1 -timeout=300s
  • golangci-lint run ./kv --timeout=5m
  • git diff --check
  • git verify-commit HEAD

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

  • kv/fsm.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. Nice work!

Reviewed commit: b04b7fb235

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

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
main.go (1)

389-416: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

readTracker.Close() を shutdown cleanup に追加してください。 ActiveTimestampTracker はスイーパー goroutine を持つため、cleanup.Add(readTracker.Close) で終了時に止める必要があります。

🤖 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 `@main.go` around lines 389 - 416, Register readTracker.Close with the shutdown
cleanup after creating the ActiveTimestampTracker, using
cleanup.Add(readTracker.Close), so its sweeper goroutine is stopped during
termination.
🧹 Nitpick comments (1)
kv/fsm.go (1)

372-373: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

applyBackup(data)applyHLCLease(data[1:]) でオペコードバイトの扱いが一貫していません。

applyHLCLeasedata[1:](オペコード除外)を渡すのに対し、applyBackupdata(オペコード含む)をそのまま渡しています。decodeBackupEntry がフルデータを期待しているため機能上は問題ありませんが、将来的な保守で混乱を招く可能性があります。

🤖 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/fsm.go` around lines 372 - 373, applyBackup と applyHLCLease
で入力データのオペコード除外方法を統一してください。applyBackup 呼び出し側では applyHLCLease と同様にオペコードバイトを除いた
data[1:] を渡し、decodeBackupEntry の期待する入力形式も確認して関連処理を一貫させてください。
🤖 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.

Outside diff comments:
In `@main.go`:
- Around line 389-416: Register readTracker.Close with the shutdown cleanup
after creating the ActiveTimestampTracker, using cleanup.Add(readTracker.Close),
so its sweeper goroutine is stopped during termination.

---

Nitpick comments:
In `@kv/fsm.go`:
- Around line 372-373: applyBackup と applyHLCLease
で入力データのオペコード除外方法を統一してください。applyBackup 呼び出し側では applyHLCLease と同様にオペコードバイトを除いた
data[1:] を渡し、decodeBackupEntry の期待する入力形式も確認して関連処理を一貫させてください。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b885a775-9adc-47fb-833c-e1990bc6fa44

📥 Commits

Reviewing files that changed from the base of the PR and between cd58354 and b04b7fb.

📒 Files selected for processing (11)
  • kv/active_timestamp_tracker.go
  • kv/active_timestamp_tracker_test.go
  • kv/backup_codec.go
  • kv/backup_codec_test.go
  • kv/fsm.go
  • kv/fsm_backup.go
  • kv/fsm_backup_test.go
  • main.go
  • main_bootstrap_e2e_test.go
  • main_encryption_write_wiring.go
  • multiraft_runtime_test.go

@bootjp

bootjp commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Addressed latest-head review findings:

  • registered readTracker.Close with the shutdown cleanup stack so the backup-pin sweeper stops on termination
  • made backup FSM apply pass payload bytes consistently with the other reserved-opcode apply paths while preserving the full wire encoder/decoder contract

Validation:

  • go test ./kv -run 'Test(ActiveTimestampTracker|BackupCodec|ApplyBackup|BackupPayload)' -count=1 -timeout=240s\n- go test ./kv -count=1 -timeout=300s\n- go test . -count=1 -timeout=180s\n- golangci-lint run ./kv --timeout=5m\n- golangci-lint run . ./kv --timeout=5m\n- git diff --check\n- git verify-commit HEAD\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 (a39218b):

  • kv/fsm.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. 👍

Reviewed commit: a39218b5b9

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

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

  • kv/fsm.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

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d56518ea2

ℹ️ 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/active_timestamp_tracker.go Outdated
}
return errors.WithStack(ErrInvalidBackupPin)
}
pin.deadline = deadline

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 the later backup deadline on renewals

When two BackupExtend proposals for the same pin are in flight, or a retry of an older renewal commits after a newer one, this unconditional assignment can move the deadline backwards. If that stale deadline expires before the next renewal, Oldest() stops honoring the pin and local compaction can advance past the backup read timestamp while the backup is still running; apply should keep max(existing deadline, requested deadline) rather than shortening it.

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 (2f77c32):

  • kv/fsm.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 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: 471c6afe3f

ℹ️ 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/backup_codec.go
Comment on lines +47 to +49
type BackupExtendEntry struct {
PinID BackupPinID
Deadline time.Time

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 Carry read_ts in backup renewals

When a backup is renewed long enough for Raft to snapshot/compact the original BackupPin, a restarted replica or one catching up via snapshot restores only MVCC data, not the in-memory tracker. The remaining BackupExtend entries carry only PinID and Deadline, and ApplyExtendForGroup no-ops when the pin is missing, so that replica cannot recreate the read_ts retention fence and its compactor can remove versions still being scanned. This affects long-running backups that cross a snapshot/restore boundary; include the read timestamp in renewals or persist the active pins in snapshots.

Useful? React with 👍 / 👎.

Comment thread kv/fsm.go
// RaftAppliedIndex from the engine's appliedIndex.
func (f *kvFSM) IsVolatileOnlyPayload(payload []byte) bool {
return len(payload) > 0 && payload[0] == raftEncodeHLCLease
return len(payload) > 0 && (payload[0] == raftEncodeHLCLease || payload[0] == raftEncodeBackup)

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 Validate backup payloads before volatile replay

This classifies every payload starting with the backup opcode as safe to replay during the cold-start skip path, but that path calls StateMachine.Apply and discards the returned value. If the skipped WAL tail contains an unknown future backup subtype, malformed backup payload, or a backup entry on an FSM without the tracker, normal apply would halt via applyBackup, while cold start silently seeds Raft past the entry without applying the intended fence/release effect. Decode known backup subtypes here, or make volatile replay honor HaltApply.

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 (748c75b):

  • kv/fsm.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.

@bootjp

bootjp commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

Updated the signed HEAD to 748c75bb5d2eeb172041e657f55eedd3a4159a39 by merging current main.

Verification:

  • backup pin and compactor-focused kv tests pass
  • proto breaking and generation checks pass with no generated diff
  • current-head CI is green across tests, lint, proto, TLA+, build, and CodeQL
  • the merge commit has a valid bootjp signature

Please review the latest head.

@codex review

@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: 748c75bb5d

ℹ️ 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/active_timestamp_tracker.go
@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 (13d22a8):

  • kv/fsm.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.

@bootjp

bootjp commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

Current signed HEAD 13d22a8171d828d6eed4bd87aceed69ece2e801a addresses the latest-head renewal finding.

  • BackupExtend carries read_ts and committed apply reconstructs a locally swept pin
  • pin merge remains monotonic: earliest read_ts, latest deadline
  • direct Extend continues to fail on missing or expired pins
  • the wire schema and focused design text now match the 34-byte extend entry

Caller audit:

  • ApplyExtendForGroup has one production caller in kv/fsm_backup.go
  • EncodeBackupExtendEntry has no production caller; live renewal already replays a complete BackupPin
  • direct Extend behavior is unchanged for its caller surface

Verification:

  • focused backup tracker/FSM/codec tests
  • go test ./kv -count=1 -timeout=300s
  • focused go test -race ./kv
  • golangci-lint run ./kv --timeout=5m --allow-parallel-runners
  • git diff --check

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: 13d22a8171

ℹ️ 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/live-backup-pin-substrate branch from 13d22a8 to 26d45ba Compare July 19, 2026 11:08
@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 (26d45ba):

  • kv/fsm.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.

@bootjp

bootjp commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

History-only author-compliance repair completed at 26d45baebb34ae6afbfaf39394e62deafcddc481.

  • final tree is byte-identical to prior head 13d22a8171d828d6eed4bd87aceed69ece2e801a
  • the PR now exposes one linear commit authored and committed by bootjp <contact@bootjp.me>
  • GitHub reports the commit signature as verified/valid
  • parent is current origin/main (37277ea86cb4cf66bf980b8a09427394f6de69e1)

No runtime or source-tree semantics changed. CI is running on the reconstructed head.

@codex review

@bootjp

bootjp commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

Current reconstructed head 26d45baebb34ae6afbfaf39394e62deafcddc481 is fully green. PR-visible authorship and signature verification pass, the final tree remains identical to the pre-rewrite head, and there are no current-head root findings. Please complete the latest-head review. @codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: 26d45baebb

ℹ️ 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 8 commits July 19, 2026 22:25
## Summary

- add BeginBackup, RenewBackup, EndBackup, ListAdaptersAndScopes, and
StreamBackup admin RPCs
- replicate bounded backup pin reservations and per-group retention
fences through Raft
- gate backup start on live member capabilities and snapshot headroom
- scan and classify user-visible keys at one pinned read timestamp using
the existing logical encoders
- rotate HMAC-protected renewal tokens with a hard deadline and reapply
complete pins so partial delivery cannot leave a replica unprotected

## Safety

- enforce a cluster-wide active backup cap with deterministic
reservation and compensating release
- reject expired tokens for renew, list, and stream while allowing
EndBackup cleanup
- retry transient per-group proposals and fail closed when renewal
cannot finish before the prior deadline
- scope compactor retention fences to their Raft group while preserving
process-wide ordinary read pins

## Validation

- go test ./adapter -run
Test\(BeginBackup\|RenewBackup\|BackupToken\|StreamBackup\|BackupProtocol\|GetRaftGroups\|GetNodeVersion\|Admin\)
-count=1 -timeout=240s
- go test ./kv ./internal/backup . -count=1 -timeout=240s
- go test -race ./adapter ./kv ./internal/backup -run
Test\(BeginBackup\|RenewBackup\|BackupToken\|StreamBackup\|BackupProtocol\|GetRaftGroupsLeaderVersion\|GetRaftGroupsSnapshotsEachGroupOnce\|LeaderVersionProbeAttemptTimeout\|ActiveTimestampTracker\|ApplyBackup\|FSMCompactorScopesBackupPinsByGroup\|LiveDecoder\|ScopeForKey\)
-count=1 -timeout=300s
- golangci-lint run . ./adapter/... ./kv/... ./internal/backup/...
--timeout=5m --allow-parallel-runners
- buf generate
- buf breaking --against the stacked admin API base

Author: bootjp

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **新機能**
  * Admin 経由でバックアップの開始・更新・終了、対象スコープ一覧、キー・バリューのストリーミング取得に対応しました。
  * スコープ単位の絞り込みと、バックアッププロトコルの対応可否をバージョンで判定します。
* **改善**
  * 固定した読み取り時点とルートスナップショットで安定した取得を実現しました。
  * 予約/解除の管理を強化し、キーのみの走査・再利用を最適化しました。
* **信頼性**
  * TTL/ヘッドルーム/容量/互換性の事前検証、部分失敗時の補償、エラー時の後処理を強化しました。
  * バックアップピンの同時上限を引き下げました。
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

@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 (4)
internal/backup/live.go (1)

20-25: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

redis 分岐は冗長なデッドコードです。

s.Adapter == "redis" のとき "redis/" + s.Name は default 分岐の s.Adapter + "/" + s.Name と完全に同一の文字列になるため、この特別扱いは効果がありません。あわせて String()(Line 245-247)も同じ adapter/name 形式を返しており重複しています。分岐を削除し、必要なら ID()String() に委譲することを検討してください。

♻️ 冗長分岐の削除案
 func (s Scope) ID() string {
-	if s.Adapter == "redis" {
-		return "redis/" + s.Name
-	}
 	return s.Adapter + "/" + s.Name
 }
🤖 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 `@internal/backup/live.go` around lines 20 - 25, Remove the redundant s.Adapter
== "redis" branch from Scope.ID and keep the single generic adapter/name
construction. Reuse the existing String method if appropriate so ID and String
share the same formatting without duplicating logic.
kv/leader_admin_proposer.go (1)

77-128: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

リトライ/バックオフの骨格が複数箇所で重複しています。

forwardAdminWithRetry/runAdminForwardCyclekv/leader_proxy.goforwardWithRetry/runForwardCycle および新規の forwardLeaseRead と本質的に同じ「デッドライン計算→ループ→lastErr nilガード→バックオフ→再デッドラインチェック」の骨格です。詳細はファイル末尾の consolidated comment を参照してください。

🤖 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/leader_admin_proposer.go` around lines 77 - 128,
重複しているリトライ/バックオフ処理を共通化し、leader_proxy.go の
forwardWithRetry・runForwardCycle・forwardLeaseRead と leader_admin_proposer.go の
forwardAdminWithRetry・runAdminForwardCycle が同じデッドライン管理、lastErr nil
ガード、バックオフ、再チェックの骨格を共有するよう更新してください。各処理固有の forwardAdmin
などの実行部分とエラー判定は既存の挙動を保ったまま、共通ヘルパーを再利用して重複実装を除去してください。
kv/leader_proxy.go (2)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

リーダー転送のリトライ/デッドライン/バックオフ骨格が3箇所で重複しています。

kv/leader_proxy.go の既存 forwardWithRetry/runForwardCycle、新規 forwardLeaseRead/forwardLeaseReadOnce、そして kv/leader_admin_proposer.goforwardAdminWithRetry/runAdminForwardCycle は、いずれも「デッドライン計算→ループでフォワード実行→lastErr==nil時にErrLeaderNotFoundへフォールバック→デッドライン超過チェック→バックオフ→再チェック」という同一の制御フローを持ちます。戻り値型が異なるだけなので、Go genericsを使った共通ヘルパーへの抽出でリトライ挙動のバグ修正・変更を1箇所に集約できます。

  • kv/leader_proxy.go#L228-270: forwardLeaseRead/forwardLeaseReadOnce を共通ヘルパーを呼ぶ形に置き換える。
  • kv/leader_admin_proposer.go#L77-128: forwardAdminWithRetry/runAdminForwardCycle も同じ共通ヘルパーを利用する形に置き換える。
🤖 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/leader_proxy.go` at line 1,
リーダー転送のリトライ制御がforwardWithRetry/runForwardCycle、forwardLeaseRead/forwardLeaseReadOnce、forwardAdminWithRetry/runAdminForwardCycleで重複しているため、デッドライン計算・実行ループ・ErrLeaderNotFoundフォールバック・バックオフを扱うGoジェネリック共通ヘルパーを追加する。forwardLeaseReadとforwardAdminWithRetryの各処理をそのヘルパー呼び出しへ置き換え、既存の戻り値型とリトライ挙動を維持し、個別の制御フロー重複を削除する。

228-270: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

forwardLeaseRead/forwardLeaseReadOnce は既存の forwardWithRetry/runForwardCycle とほぼ同一のリトライ骨格です。

デッドライン計算、lastErr==nil ガード、バックオフ後の再デッドラインチェックが同ファイル内で二重実装されています。詳細はファイル末尾の consolidated comment を参照してください。

🤖 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/leader_proxy.go` around lines 228 - 270, Refactor forwardLeaseRead to
reuse the existing forwardWithRetry and runForwardCycle retry helpers instead of
duplicating deadline calculation, lastErr handling, backoff, and deadline
checks. Adapt the lease-read operation through these helpers while preserving
transient-error retries, immediate propagation of non-transient errors, and the
existing ErrLeaderNotFound fallback.
🤖 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 `@internal/backup/live.go`:
- Around line 20-25: Remove the redundant s.Adapter == "redis" branch from
Scope.ID and keep the single generic adapter/name construction. Reuse the
existing String method if appropriate so ID and String share the same formatting
without duplicating logic.

In `@kv/leader_admin_proposer.go`:
- Around line 77-128: 重複しているリトライ/バックオフ処理を共通化し、leader_proxy.go の
forwardWithRetry・runForwardCycle・forwardLeaseRead と leader_admin_proposer.go の
forwardAdminWithRetry・runAdminForwardCycle が同じデッドライン管理、lastErr nil
ガード、バックオフ、再チェックの骨格を共有するよう更新してください。各処理固有の forwardAdmin
などの実行部分とエラー判定は既存の挙動を保ったまま、共通ヘルパーを再利用して重複実装を除去してください。

In `@kv/leader_proxy.go`:
- Line 1:
リーダー転送のリトライ制御がforwardWithRetry/runForwardCycle、forwardLeaseRead/forwardLeaseReadOnce、forwardAdminWithRetry/runAdminForwardCycleで重複しているため、デッドライン計算・実行ループ・ErrLeaderNotFoundフォールバック・バックオフを扱うGoジェネリック共通ヘルパーを追加する。forwardLeaseReadとforwardAdminWithRetryの各処理をそのヘルパー呼び出しへ置き換え、既存の戻り値型とリトライ挙動を維持し、個別の制御フロー重複を削除する。
- Around line 228-270: Refactor forwardLeaseRead to reuse the existing
forwardWithRetry and runForwardCycle retry helpers instead of duplicating
deadline calculation, lastErr handling, backoff, and deadline checks. Adapt the
lease-read operation through these helpers while preserving transient-error
retries, immediate propagation of non-transient errors, and the existing
ErrLeaderNotFound fallback.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d36b16f2-fda5-408f-b7a0-4be2ee194b98

📥 Commits

Reviewing files that changed from the base of the PR and between b04b7fb and 1eddbb1.

⛔ Files ignored due to path filters (5)
  • proto/admin.pb.go is excluded by !**/*.pb.go
  • proto/admin_grpc.pb.go is excluded by !**/*.pb.go
  • proto/internal.pb.go is excluded by !**/*.pb.go
  • proto/internal_grpc.pb.go is excluded by !**/*.pb.go
  • proto/service.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (51)
  • adapter/admin_backup.go
  • adapter/admin_backup_test.go
  • adapter/admin_grpc.go
  • adapter/admin_grpc_test.go
  • adapter/internal.go
  • adapter/internal_admin_proposal_test.go
  • distribution/engine.go
  • docs/design/2026_04_29_proposed_logical_backup.md
  • internal/backup/live.go
  • internal/backup/live_test.go
  • internal/backup/s3.go
  • internal/backup/sqs.go
  • internal/raftadmin/server_test.go
  • internal/raftengine/engine.go
  • internal/raftengine/etcd/engine.go
  • internal/raftengine/etcd/wal_purge_test.go
  • kv/active_timestamp_tracker.go
  • kv/active_timestamp_tracker_test.go
  • kv/backup_codec.go
  • kv/backup_codec_test.go
  • kv/backup_scan.go
  • kv/compactor.go
  • kv/compactor_test.go
  • kv/coordinator.go
  • kv/coordinator_retry_test.go
  • kv/fsm.go
  • kv/fsm_backup.go
  • kv/fsm_backup_test.go
  • kv/keyviz_label.go
  • kv/leader_admin_proposer.go
  • kv/leader_admin_proposer_test.go
  • kv/leader_proxy.go
  • kv/leader_proxy_test.go
  • kv/lease_read_test.go
  • kv/shard_store.go
  • kv/shard_store_test.go
  • kv/sharded_coordinator.go
  • kv/sharded_coordinator_leader_test.go
  • kv/sharded_coordinator_txn_test.go
  • kv/tso_test.go
  • kv/txn_keys.go
  • main.go
  • main_admin.go
  • main_admin_test.go
  • main_bootstrap_e2e_test.go
  • main_encryption_write_wiring.go
  • main_sqs_leadership_refusal_test.go
  • multiraft_runtime_test.go
  • proto/admin.proto
  • proto/internal.proto
  • proto/service.proto
🚧 Files skipped from review as they are similar to previous changes (4)
  • multiraft_runtime_test.go
  • main_encryption_write_wiring.go
  • kv/active_timestamp_tracker_test.go
  • kv/active_timestamp_tracker.go

@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: 1eddbb1aa2

ℹ️ 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 adapter/admin_backup.go
if err != nil {
return nil, err
}
defer s.forgetBackupSession(tok.pinID)

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 Prevent renewals from resurrecting ended backups

When a client's background RenewBackup overlaps with EndBackup, this defer leaves the session live while the release entries are being proposed. If the group log orders EndBackup's Release before the in-flight renewal's complete Pin, finishRenewBackup can still extend the session before this defer runs, then EndBackup returns and forgets the session without issuing another release; the renewed pin remains active until its deadline and continues blocking compaction/capacity after the backup was ended. Mark the session as closing (or otherwise reject/serialize renewals for the pin) before proposing releases so no renewal can commit after the final release.

Useful? React with 👍 / 👎.

Comment thread adapter/admin_backup.go
if err != nil {
return nil, err
}
if err := s.requireUnexpiredBackupToken(tok); 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 Allow retrying a committed renewal after the old token expires

If a RenewBackup RPC commits successfully but the response carrying the rotated token is lost, the server has already extended the in-memory session in finishRenewBackup, but the client can only retry with the old token. Once the old embedded deadline passes, this pre-session check rejects the retry even though the pin is still live until the committed later deadline, leaving the backup unable to obtain the current token and eventually losing its retention fence. Check the live session for the same pin/readTS before rejecting, or return the session's current token idempotently.

Useful? React with 👍 / 👎.

Comment thread kv/backup_scan.go
}

func (s *ShardStore) txnCommitTSAt(ctx context.Context, primaryKey []byte, startTS uint64, ts uint64) (uint64, bool, error) {
b, err := s.GetAt(ctx, txnCommitKey(primaryKey, startTS), ts)

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 Resolve backup lock status against the captured route

When a backup races a route split or move, ValidateBackupSnapshotAt scans locks from the captured read-ts route set, but this lookup resolves the primary commit record through the live ShardStore routing table. If the primary key has moved after read_ts, GetAt checks the new owner instead of the historical group that contains the commit/rollback record, so an already-resolved transaction is treated as pending and BeginBackup fails despite a clean snapshot. Read the txn status via the captured route/group for the primary key rather than live routing.

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.

2 participants