Skip to content

Add autosplit detector core#1097

Merged
bootjp merged 6 commits into
mainfrom
feature/hotspot-split-m3-detector
Jul 19, 2026
Merged

Add autosplit detector core#1097
bootjp merged 6 commits into
mainfrom
feature/hotspot-split-m3-detector

Conversation

@bootjp

@bootjp bootjp commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Author: bootjp

Summary

  • add a pure distribution/autosplit detector core for hotspot split M3 PR2a
  • cover route-row aggregation, active-route candidacy, weighted ops/min scoring, hysteresis, cooldown, p50 split-key selection, route caps, and per-cycle split budget
  • update the M3 automation design doc to split detector core from later sampler/runtime integration

Validation

  • go test ./distribution/autosplit ./distribution -count=1 -timeout=180s
  • go test ./distribution/... -count=1 -timeout=180s
  • golangci-lint --config=.golangci.yaml run ./distribution/autosplit --timeout=5m
  • git diff --check
  • git verify-commit HEAD

Summary by CodeRabbit

  • 新機能
    • 負荷状況と既存のコミット済みウィンドウから、自動スプリット候補の意思決定(判断)とスキップ理由を生成できるようになりました。
    • 連続高負荷の継続、クールダウン、ルート・実行上限、分割キー選定の境界条件を考慮します。
  • テスト
    • 意思決定、状態遷移、上限制御、クールダウン抑制、P50境界の分割キー選択などを網羅するテストを追加しました。
  • ドキュメント
    • 自動スプリットの計画とフラグ定義を更新しました。

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@bootjp

bootjp commented Jul 17, 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 (1fa4210):

  • distribution/autosplit/detector.go
  • distribution/autosplit/detector_test.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.

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

Run ID: a31f87fa-7557-4c5a-8c4d-6206852680ff

📥 Commits

Reviewing files that changed from the base of the PR and between daae162 and 7a486a1.

📒 Files selected for processing (3)
  • distribution/autosplit/detector.go
  • distribution/autosplit/detector_test.go
  • docs/design/2026_06_11_partial_hotspot_split_milestone3_automation.md
📝 Walkthrough

Walkthrough

autosplitデテクタの公開契約、ルート状態管理、ウィンドウ評価、候補制限、P50分割キー選定を追加した。包括的な評価テストと、M3のPR分割および進捗を更新する設計文書も追加した。

Changes

Autosplit検出コア

Layer / File(s) Summary
公開契約と検出状態
distribution/autosplit/detector.go, docs/design/...
設定、入出力型、スキップ理由、分割起点、ルートごとの信頼度・クールダウン状態を追加し、M3の進捗とPR分割記述を更新した。
ウィンドウ集計と閾値評価
distribution/autosplit/detector.go, distribution/autosplit/detector_test.go
ウィンドウの妥当性、集計行、ルート状態、読み書き負荷、ops/min、連続閾値超過を評価し、各状態遷移をテストした。
決定制限とP50分割キー
distribution/autosplit/detector.go, distribution/autosplit/detector_test.go
候補をスコア順に選択して分割数・ルート数の上限を適用し、重み付き中央値から境界内の分割キーとSplitOriginを生成する処理をテストした。

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

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Evaluate
  participant DetectorState
  participant Result
  Caller->>Evaluate: ConfigとInputを渡す
  Evaluate->>DetectorState: ルート状態を取得・更新
  Evaluate->>Evaluate: ウィンドウを集計し候補を選択
  Evaluate->>Result: DecisionとEventを生成
  Result-->>Caller: 評価結果を返す
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.55% 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 追加された変更は autosplit の純粋な detector core 実装で、タイトルは主要な変更点を簡潔に表しています。
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.

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

Actionable comments posted: 5

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

Inline comments:
In `@distribution/autosplit/detector.go`:
- Around line 245-248: Update the invalid-window branch in the detector flow to
clear the confidence and latestHot state for every active route before
returning. Preserve the existing SkipReasonInvalidWindow event, ensuring invalid
windows break consecutive hot-window promotion and fail closed.
- Around line 269-275:
クールダウン中の履歴ウィンドウが期限後に評価されても昇格材料へ加算されないよう、`status.CooldownUntil` と現在時刻だけでなく
`window.Column.At` も期限後か検証し、期限前の列は `latestHot`
や連続超過判定から破棄してください。クールダウン中に複数列を蓄積し、期限後にまとめて処理しても再昇格しない回帰テストを追加してください。
- Around line 208-211: Input.Windows の順序に依存しないよう、detector
のウィンドウ評価処理で入力スライスをコピーし、各ウィンドウの Column.At を基準に安定ソートしてから processWindow
を呼び出してください。元の Input.Windows は変更せず、ソート済みコピーを使って latestHot と result
を更新する既存の処理を維持してください。

In `@docs/design/2026_06_11_partial_hotspot_split_milestone3_automation.md`:
- Line 10: Update the document’s Updated date from 2026-07-18 to the actual
current date, 2026-07-17.
- Around line 549-550: Move state-map garbage-collection reconciliation against
the live catalog from M3-PR2b to M3-PR2a in the milestone table. Update PR2a’s
scope and tests to include Evaluate’s existing state.gc(live) behavior, and
remove that responsibility and its test from PR2b while preserving the other
PR2b items.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e092fee0-78f1-4657-85e5-7b515fd69d24

📥 Commits

Reviewing files that changed from the base of the PR and between c9f0d53 and 1fa4210.

📒 Files selected for processing (3)
  • distribution/autosplit/detector.go
  • distribution/autosplit/detector_test.go
  • docs/design/2026_06_11_partial_hotspot_split_milestone3_automation.md

Comment thread distribution/autosplit/detector.go
Comment thread distribution/autosplit/detector.go
Comment thread distribution/autosplit/detector.go
Comment thread docs/design/2026_06_11_partial_hotspot_split_milestone3_automation.md Outdated
@bootjp

bootjp commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Addressed current-head CodeRabbit findings in 978a6e9:

  • evaluate copied windows in stable Column.At order without mutating Input.Windows.
  • invalid windows now reset confidence and clear current hot candidates for active routes.
  • cooldown now rejects historical windows generated before CooldownUntil, even when evaluated after the deadline.
  • M3 milestone docs now place detector state-map GC in PR2a and remove it from PR2b.
  • Updated the doc date to match the review context.

Validation:

  • go test ./distribution/autosplit -count=1 -timeout=180s
  • go test ./distribution/... -count=1 -timeout=180s
  • golangci-lint --config=.golangci.yaml run ./distribution/autosplit --timeout=5m
  • git diff --check
  • git verify-commit HEAD

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

  • distribution/autosplit/detector.go
  • distribution/autosplit/detector_test.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: 978a6e9079

ℹ️ 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 distribution/autosplit/detector.go Outdated
result *Result,
) {
status := state.routes[route.RouteID]
if now.Before(status.CooldownUntil) || window.Column.At.Before(status.CooldownUntil) {

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 cooldown-overlapping windows

When an evaluation runs just after a cooldown deadline, this only rejects columns whose flush At is before the deadline. A committed ColumnWindow with At after CooldownUntil but At-Duration before it still contains traffic collected during cooldown, so with CandidateWindows=1 it can immediately promote a just-split child from cooldown-period load (and with the default it still counts as the first post-cooldown evidence). Since ColumnWindow carries Duration, reject/reset windows whose start overlaps the cooldown or require the next full post-cooldown column.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 48e3982. The cooldown check now treats a window as cooldown evidence if Column.At-Duration is before CooldownUntil, so only a full post-cooldown column can advance confidence. Added TestCooldownDropsWindowThatStartedBeforeDeadline.

Comment on lines +484 to +486
if cumulative >= total/2 {
median = row
break

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Coalesce duplicate sub-bucket rows before splitting

When keyviz label attribution is enabled, a column can contain multiple non-aggregate rows for the same route and sub-bucket (one per label). The score path sums those rows, but this median loop can stop on the first duplicate that crosses half the total, so LeftLoad/RightLoad exclude the remaining load in the same [Start,End) bucket and can report the hot side on the wrong child (for example bucket0 labels 1000+900 and bucket1 100). Those estimates are part of the detector decision and feed p50 target admission, so rows with the same sub-bucket bounds should be coalesced before the median is selected.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 48e3982. P50 selection now coalesces usable non-aggregate rows with identical route/sub-bucket bounds before weighted median selection, so duplicate label-attributed rows contribute as one bucket. Added TestP50CoalescesDuplicateSubBucketRows to assert left/right load estimates.

return
}

status.ConsecutiveOver++

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 Deduplicate already-processed columns

This increments confidence every time a hot ColumnWindow is supplied, but the state does not remember the last processed MatrixColumn.At. If the sampler reader returns the same committed column again (for example an eval interval shorter than the keyviz step, or a trailing Snapshot window reused across cycles), a single hot flush can be counted repeatedly and promote after CandidateWindows evaluations instead of CandidateWindows distinct committed columns. Store and skip already-processed column boundaries, or make duplicate/stale columns a no-op before this increment.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 48e3982. RouteStatus now tracks LastProcessedAt and processRouteWindow ignores duplicate or stale MatrixColumn.At boundaries before changing confidence, preventing one committed column from advancing hysteresis across repeated evaluations. Added TestDuplicateColumnsAreSkippedAcrossEvaluations.

@bootjp

bootjp commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Addressed current-head detector evidence findings in 48e3982:

  • cooldown checks now reject windows whose committed interval starts before CooldownUntil, requiring a full post-cooldown column.
  • detector state now tracks LastProcessedAt per route and ignores duplicate/stale MatrixColumn.At boundaries, so one committed column cannot advance hysteresis multiple times.
  • p50 split-key selection now coalesces duplicate non-aggregate rows with identical route/sub-bucket bounds before weighted median selection, keeping left/right load estimates correct.

Validation:

  • go test ./distribution/autosplit -count=1 -timeout=180s
  • go test ./distribution/... -count=1 -timeout=180s
  • golangci-lint --config=.golangci.yaml run ./distribution/autosplit --timeout=5m
  • git diff --check
  • git verify-commit HEAD

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

  • distribution/autosplit/detector.go
  • distribution/autosplit/detector_test.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: 48e3982e6c

ℹ️ 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 on lines +231 to +234
if route.State != distribution.RouteStateActive {
state.ApplyRouteState(route.RouteID, route.State)
result.Events = append(result.Events, Event{RouteID: route.RouteID, Reason: SkipReasonNonActiveState})
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Advance skipped non-active routes past buffered columns

When a route is non-active in an evaluation that includes committed windows, this only clears confidence and leaves LastProcessedAt unchanged because the route is omitted from active. If the sampler reader passes the same buffered MatrixColumn.At values again after the route returns to RouteStateActive, processRouteWindow will accept those old columns and count load from the fenced/migrating interval as fresh active evidence; with CandidateWindows=2, one skipped hot column plus one post-active hot column can immediately promote. The non-active skip needs to advance the route's processed watermark through the freshest skipped window (or otherwise suppress those buffered columns) before the route can rebuild confidence.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in daae162. Non-active routes now advance LastProcessedAt through the freshest window in the evaluation while clearing confidence, so buffered columns observed during migrating/fenced state are skipped if replayed after the route becomes active again. Added TestNonActiveRouteAdvancesSkippedBufferedColumns.

Comment on lines +250 to +255
if window.Duration <= 0 {
result.Events = append(result.Events, Event{Reason: SkipReasonInvalidWindow, At: window.Column.At})
for _, route := range active {
state.resetConfidenceAt(route.RouteID, window.Column.At)
delete(latestHot, route.RouteID)
}

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 Ignore stale invalid windows before resetting confidence

This invalid-window path resets every active route before checking whether that column boundary is stale for the route. resetConfidenceAt keeps LastProcessedAt unchanged when window.Column.At is older, but it still clears ConsecutiveOver, so a duplicate/old zero-duration column kept in sampler history can erase already-earned hysteresis on every evaluation and prevent a hot route from ever reaching the candidate window count. The reset and latestHot deletion should be conditional on the invalid column being newer than the route's LastProcessedAt, matching the stale-column guard used for normal windows.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in daae162. Invalid windows now reset confidence only when the invalid column is newer than the route's LastProcessedAt; stale invalid columns preserve existing hysteresis and do not clear the current candidate. Added TestStaleInvalidWindowDoesNotResetConfidence.


// DefaultConfig returns the M3 detector defaults from the design doc.
func DefaultConfig() Config {
return Config{

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 Set a default route cap for autosplit

This default config literal leaves MaxRoutes as zero, and the admission check only enforces the cap when cfg.MaxRoutes > 0. If the scheduler uses DefaultConfig() or a zero-valued config with only selected overrides, autosplit has no total route-count guardrail and can keep emitting one split per hot cycle indefinitely, bypassing the documented cap meant to bound catalog growth. Please set a positive default here or make callers explicitly supply one before decisions are admitted.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in daae162. DefaultConfig now sets MaxRoutes=1024 and withDefaults applies it to zero/partial configs, so autosplit keeps a positive route-count guardrail by default unless an explicit cap overrides it. Added TestDefaultConfigSetsRouteCap and TestZeroMaxRoutesUsesDefaultRouteCap, and updated the design doc default.

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

  • distribution/autosplit/detector.go
  • distribution/autosplit/detector_test.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 commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Addressed current-head detector guardrail findings in daae162:

  • non-active route evaluations now advance LastProcessedAt through the freshest skipped column while clearing confidence, so buffered migrating/fenced evidence is not replayed after the route becomes active again.
  • invalid windows now reset confidence only when their column boundary is newer than the route watermark, so stale invalid columns cannot repeatedly erase earned hysteresis.
  • DefaultConfig and zero/partial config normalization now apply MaxRoutes=1024, keeping autosplit under a positive route-count guardrail by default; the design doc default was updated to match.

Validation:

  • go test ./distribution/autosplit -count=1 -timeout=180s
  • go test ./distribution/... -count=1 -timeout=180s
  • golangci-lint --config=.golangci.yaml run ./distribution/autosplit --timeout=5m
  • git diff --check

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

ℹ️ 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 distribution/autosplit/detector.go Outdated
//
// Non-active route states clear confidence but preserve any cooldown deadline.
func (s *DetectorState) ApplyRouteState(routeID uint64, state distribution.RouteState) {
s.applyRouteStateThrough(routeID, state, 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.

P2 Badge Advance watermark when applying non-active transitions

When the watcher uses this transition hook for an active→migrating→active interval that occurs between detector cycles, only clearing ConsecutiveOver leaves LastProcessedAt at the old column. The next Evaluate over buffered columns since that watermark will count hot columns from the non-active interval and can promote immediately after reactivation (e.g. CandidateWindows=2 with one migrating column plus one active column), so the route does not re-earn consecutive active evidence. Please carry a skipped-through column boundary/transition watermark through this path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 4833261. ApplyRouteState now requires a processed-through column boundary and advances LastProcessedAt for non-active transitions, so future watcher wiring cannot clear confidence without also fencing buffered columns from the non-active interval. Caller audit found only Evaluate and the new regression test use this hook. Added TestApplyRouteStateAdvancesProcessedWatermark.

bootjp commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Addressed the current-head transition watermark finding in 4833261:

  • ApplyRouteState now requires a processed-through timestamp and advances LastProcessedAt when applying non-active route transitions, so buffered columns from a migrating/fenced interval cannot be replayed after reactivation.
  • Evaluate now uses that same public hook for non-active routes discovered in an evaluation window.

Caller audit:

  • ApplyRouteState is only called by Evaluate and TestApplyRouteStateAdvancesProcessedWatermark in this PR head.

Validation:

  • go test ./distribution/autosplit -count=1 -timeout=180s
  • go test ./distribution/... -count=1 -timeout=180s
  • golangci-lint --config=.golangci.yaml run ./distribution/autosplit --timeout=5m
  • git diff --check

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

  • distribution/autosplit/detector.go
  • distribution/autosplit/detector_test.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. Delightful!

Reviewed commit: 4833261867

ℹ️ 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 (7a486a1):

  • distribution/autosplit/detector.go
  • distribution/autosplit/detector_test.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

@codex review

Current head 7a486a1 is merged with current main. The detector-only PR diff remains limited to detector.go, detector_test.go, and the M3 design status update.

Validation:

  • go test ./distribution/autosplit -count=1 -timeout=10m
  • golangci-lint run ./... --timeout=5m --allow-parallel-runners
  • git diff --check

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 7a486a16f5

ℹ️ 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 merged commit 902b9b3 into main Jul 19, 2026
13 checks passed
@bootjp
bootjp deleted the feature/hotspot-split-m3-detector branch July 19, 2026 14:17
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