Skip to content

migration: add range version RPC handlers#1088

Open
bootjp wants to merge 16 commits into
design/hotspot-split-m2-store-exportfrom
design/hotspot-split-m2-cross-group
Open

migration: add range version RPC handlers#1088
bootjp wants to merge 16 commits into
design/hotspot-split-m2-store-exportfrom
design/hotspot-split-m2-cross-group

Conversation

@bootjp

@bootjp bootjp commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • add Internal ExportRangeVersions and ImportRangeVersions handlers backed by the local group store
  • wire per-group stores into Internal server registration
  • preserve staged migration route metadata in the serving route engine snapshot

Tests

  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter ./distribution ./kv ./store -run 'TestInternal|TestMigration|TestExportVersions|TestImportVersions|TestEngineApplySnapshot|TestShardStore|TestLeaderRoutedStore' -count=1 -timeout=180s\n- GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint run ./adapter ./distribution ./kv ./store --timeout=5m\n\nAuthor: bootjp

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Caution

Review failed

An error occurred during the review process. Please try again later.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

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

Anchored files changed in this PR head (64dc6f4):

  • distribution/engine.go
  • distribution/engine_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.

@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 adds support for range migrations by implementing ExportRangeVersions and ImportRangeVersions on the internal gRPC server, alongside updating the distribution Route metadata to track migration states. Feedback focuses on adding a defensive check in ExportRangeVersions to prevent infinite loops when the cursor does not progress, and removing an unreachable nil guard on i.leader in verifyInternalLeader to comply with guidelines against dead defensive code, which also requires updating the corresponding unit tests to use a mock leader.

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 adapter/internal.go
Comment thread adapter/internal.go
Comment thread adapter/internal_migration_test.go
Comment thread adapter/internal_migration_test.go Outdated
Comment thread adapter/internal_migration_test.go Outdated

@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: 64dc6f4239

ℹ️ 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/internal.go Outdated
Comment thread adapter/internal.go
Comment thread adapter/internal.go
Comment thread adapter/internal.go
Comment thread adapter/internal.go Outdated
@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 (59a1672):

  • distribution/engine.go
  • distribution/engine_test.go
  • 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 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Updated in 59a1672:

  • Route ImportRangeVersions through the target-group Raft/FSM path before ACK.
  • Observe the imported HLC floor before acknowledging non-empty imports.
  • Carry export key_family and bracket exclusions through the internal RPC.
  • Reject unbounded max_commit_ts exports and non-progressing export cursors.

Validation:

  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter ./kv ./store -run 'TestInternal|TestMigration|TestExportVersions|TestImportVersions|TestApply|TestRouteKeyFilter|TestShardStore|TestLeaderRoutedStore' -count=1 -timeout=180s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./distribution -count=1 -timeout=120s
  • GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint run ./adapter ./distribution ./kv ./store --timeout=5m
  • git diff --check

Note: a broader go test ./adapter ./distribution ./kv ./store -count=1 -timeout=240s timed out in the adapter integration suite; the targeted touched-path tests above passed.

@bootjp bootjp force-pushed the design/hotspot-split-m2-cross-group branch from 59a1672 to fbd7f56 Compare July 13, 2026 14:32
@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 (fbd7f56):

  • distribution/engine.go
  • distribution/engine_test.go
  • 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.

@bootjp

bootjp commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Rebased onto the latest design/hotspot-split-m2-fence-drain head in fbd7f56 after #1087 advanced. The migration import/export fixes remain intact and now include the parent broad DEL_PREFIX fence fix.

Validation:

  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter ./kv ./store -run 'TestInternal|TestMigration|TestExportVersions|TestImportVersions|TestApply|TestRouteKeyFilter|TestShardStore|TestLeaderRoutedStore|TestRoutePrefixRange|TestFSMRejects.*WriteFenced|TestShardedCoordinatorRejects.*WriteFenced' -count=1 -timeout=180s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./distribution -count=1 -timeout=120s
  • GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint run ./adapter ./distribution ./kv ./store --timeout=5m
  • git diff --check

@chatgpt-codex-connector

Copy link
Copy Markdown

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

@bootjp

bootjp commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Updated in 0fe341d:

  • Serve GetRouteOwnership from the exact RouteHistorySnapshot requested by catalog_version.
  • Serve GetIntersectingRoutes from the same versioned snapshot, including open-ended ranges.
  • Return NotFound for catalog versions outside the retained route-history window and FailedPrecondition when the distribution engine is absent.

Validation:

  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter -run 'TestDistributionServer(GetRouteOwnership|GetIntersectingRoutes|OwnershipRPCs)' -count=1 -timeout=120s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter ./distribution -run 'TestDistributionServer|TestEngineSnapshotAt|TestEngineGetIntersectingRoutes|TestRouteHistorySnapshot' -count=1 -timeout=180s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter ./kv ./store -run 'TestInternal|TestMigration|TestExportVersions|TestImportVersions|TestApply|TestRouteKeyFilter|TestShardStore|TestLeaderRoutedStore|TestDistributionServer(GetRouteOwnership|GetIntersectingRoutes|OwnershipRPCs)' -count=1 -timeout=180s
  • GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint run ./adapter ./distribution --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 (0fe341d):

  • distribution/engine.go
  • distribution/engine_test.go
  • 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.

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

  • distribution/engine.go
  • distribution/engine_test.go
  • distribution/migrator.go
  • distribution/migrator_export_plan_test.go
  • 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 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Updated in 7d3b01c:

  • ImportRangeVersions now stages imported MVCC versions under the target-local !dist|migstage|<job_id>|<raw_key> shadow keyspace instead of writing raw user keys directly.
  • Added staged-data key helpers for job-scoped scans and future promotion/read-merge code to recover the original raw key.
  • Updated RPC/FSM tests to assert staged writes, raw-key absence before promotion, HLC floor preservation, and key/value clone safety.

Validation:

  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./distribution -run 'TestMigrationStagedDataKeyRoundTrip|TestPlanMigrationBrackets|TestValidateMigrationRouteRange' -count=1 -timeout=120s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter ./kv ./store -run 'TestInternal|TestMigration|TestImportVersions|TestExportVersions|TestApplyMigration|TestDistributionServer(GetRouteOwnership|GetIntersectingRoutes|OwnershipRPCs)' -count=1 -timeout=180s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter ./distribution ./kv ./store -run 'TestInternal|TestMigration|TestExportVersions|TestImportVersions|TestApply|TestRouteKeyFilter|TestShardStore|TestLeaderRoutedStore|TestDistributionServer(GetRouteOwnership|GetIntersectingRoutes|OwnershipRPCs)' -count=1 -timeout=240s
  • GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint run ./adapter ./distribution ./kv --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 (e7f69ef):

  • distribution/engine.go
  • distribution/engine_test.go
  • distribution/migrator.go
  • distribution/migrator_export_plan_test.go
  • 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 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Updated in e7f69ef:

  • Route reads with staged_visibility_active now merge live raw keys with target-local staged keys for GetAt, ScanAt, ReverseScanAt, and LatestCommitTS.
  • The merge chooses the newest MVCC version across live/staged candidates, preserves tombstone and TTL winner semantics, and maps staged keys back to their original raw keys.
  • Physical-limit scans on staged-visible routes now fail closed because the staged/live export merge cannot preserve the physical scan budget exactly.

Validation:

  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./kv -run 'TestShardStore(GetAt_MergesStagedVisibility|ScanAndLatestCommitTS_MergeStagedVisibility)' -count=1 -timeout=120s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./kv -run 'TestShardStore|TestMergeAndTrim|TestScanLockBounds' -count=1 -timeout=180s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./kv -count=1 -timeout=240s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter ./store -run 'TestInternal|TestMigration|TestImportVersions|TestExportVersions|TestApply|TestRouteKeyFilter|TestShardStore|TestLeaderRoutedStore|TestGRPCServer_Raw' -count=1 -timeout=180s
  • GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint run ./kv --timeout=5m
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter ./distribution ./kv ./store -run 'TestInternal|TestMigration|TestExportVersions|TestImportVersions|TestApply|TestRouteKeyFilter|TestShardStore|TestLeaderRoutedStore|TestDistributionServer(GetRouteOwnership|GetIntersectingRoutes|OwnershipRPCs)' -count=1 -timeout=240s
  • 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: e7f69efd9e

ℹ️ 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/shard_store.go Outdated
func latestMVCCVersionAt(ctx context.Context, st store.MVCCStore, key []byte, ts uint64) (store.MVCCVersion, bool, error) {
result, err := st.ExportVersions(ctx, store.ExportVersionsOptions{
StartKey: key,
EndKey: nextScanCursor(key),

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 Use exact-key export bounds for staged point reads

When the backing store is Pebble, ExportVersions converts EndKey to encodeKey(EndKey, MaxUint64) (store/lsm_migration.go), and nextScanCursor(key) (key + 0x00) sorts before normal encoded versions of key because the timestamp suffix begins with ^commitTS. On a staged-visible route backed by Pebble, both the live and staged point exports therefore return no candidates for ordinary keys, so GetAt can incorrectly return ErrKeyNotFound even though the key exists. Use a true single-key upper bound/exact-key filter instead of the scan cursor here.

Useful? React with 👍 / 👎.

Comment thread kv/shard_store.go Outdated
ts uint64,
reverse bool,
) ([]*store.KVPair, error) {
live, err := collectLatestLogicalVersions(ctx, g.Store, start, end, start, end, ts, liveLogicalVersionKey)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve scan limits while merging staged visibility

For staged-visible routes, this call starts an export loop that runs until result.Done, and the caller only applies limit after collecting both the full live range and the full staged prefix into maps. A user scan such as ScanAt(..., limit=100) over a migrated production range can therefore read and retain every version in the route before returning one page, which is a large latency/memory regression versus the previous bounded store scan. Please stop the merge once enough ordered candidates are available or otherwise page the merge by the requested limit.

Useful? React with 👍 / 👎.

Comment thread kv/shard_store.go
Comment thread kv/shard_store.go
Comment thread adapter/internal.go
Comment thread distribution/engine.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 (ca1a050):

  • distribution/engine.go
  • distribution/engine_test.go
  • distribution/migrator.go
  • distribution/migrator_export_plan_test.go
  • kv/fsm.go
  • kv/sharded_coordinator.go

What to check, by subsystem:

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

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


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

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

@bootjp

bootjp commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Updated in ca1a050:

  • Bound staged visibility point reads to the exact key and reject compacted staged reads before merging.
  • Preserve staged scan limits with bounded candidate windows, and fail closed for physical-limit scans when staged visibility is active.
  • Route S3 bucket meta/generation exports by decoded bucket name so route filters match logical ownership.
  • Enforce migration write timestamp floors at coordinator, FSM, and ShardStore write paths.

Validation:

  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./kv -run 'TestFSMRejects.*MigrationTimestampFloor|TestFSMRejectsCommitButNotPrepareAtMigrationTimestampFloor|TestShardStore(GetAt_MergesStagedVisibilityPebbleExactKey|StagedVisibilityReadTSCompacted|PhysicalLimitFailsClosedBeforeStagedVisibilityFallback|RejectsWritesAtMigrationTimestampFloor)|TestShardStoreScanAndLatestCommitTS_MergeStagedVisibility|TestShardedCoordinatorRejects(PointWrite|DelPrefix)AtMigrationTimestampFloor|TestShardedCoordinatorDispatchTxn_RejectsMigrationTimestampFloor' -count=1 -timeout=180s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter -run 'TestInternalExportRangeVersions(UsesDecodedS3BucketRouteFilter|UsesStoreAndRouteFilter|RejectsUnboundedExport)|TestInternalImportRangeVersionsAppliesStoreBatch' -count=1 -timeout=120s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./internal/s3keys -run 'TestBucketGenerationKey|TestParseBucketGenerationKey|TestBucketMetaKey' -count=1 -timeout=120s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./kv -count=1 -timeout=240s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter ./distribution ./store ./internal/s3keys -run 'TestInternal|TestMigration|TestExportVersions|TestImportVersions|TestApply|TestRouteKeyFilter|TestLeaderRoutedStore|TestDistributionServer|TestRouteDescriptor|TestEngine|TestBucket' -count=1 -timeout=240s
  • GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint run ./adapter ./distribution ./kv ./store ./internal/s3keys --timeout=5m
  • git diff --check

…ain' into design/hotspot-split-m2-cross-group

# Conflicts:
#	distribution/engine.go
#	distribution/migrator.go
#	internal/s3keys/keys_test.go
#	kv/shard_store.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 (727c1dd):

  • distribution/engine.go
  • distribution/engine_test.go
  • distribution/migrator.go
  • distribution/migrator_export_plan_test.go
  • kv/fsm.go
  • kv/sharded_coordinator.go

What to check, by subsystem:

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

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


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

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

@chatgpt-codex-connector

Copy link
Copy Markdown

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

@bootjp

bootjp commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Merged latest design/hotspot-split-m2-fence-drain into this branch in 727c1dd and resolved conflicts without dropping either the read-fence route-version path or staged migration visibility.

Conflict resolution notes:

  • Kept cloneRoute for Route snapshot copies so migration fields remain deep-copied.
  • Kept both MigrationStagedDataKey helpers and decoded S3 bucket route filtering.
  • Unified ShardStore GetAt/LatestCommitTS/scan paths so readRouteVersion, explicit group scans, staged visibility, and MinWriteTSExclusive continue to flow together.

Validation:

  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./distribution ./internal/s3keys ./kv -count=1 -timeout=300s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./store -count=1 -timeout=300s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter -run 'TestDistributionServer(GetRouteOwnership|GetIntersectingRoutes|OwnershipRPCs)|TestInternal(ExportRangeVersions|ImportRangeVersions)|TestGRPCServer_Raw(Get|ScanAt|LatestCommitTS|ReadFenceHelpers)|TestStampRawTimestampsRejectsRouteWriteFloor|TestSQSServer_(ScanOneVisibleMessagePage|LoadCandidateRecord)' -count=1 -timeout=300s
  • GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint --config=.golangci.yaml run ./adapter ./distribution ./internal/s3keys ./kv ./store --timeout=5m
  • git diff --check

Caller audit:

  • rg -n 'GetAtWithReadFence|LatestCommitTSWithReadFence|scanRoutesAtWithReadFence|scanRouteAtDirectionWithReadFence|scanRouteAtLeaderPhysicalLimit\(|scanRouteLocal\(|resolveScanLocks\(|MigrationStagedDataKey|containsDecodedS3Route|routeKeyInRange|cloneRoute|GetIntersectingRoutes|MinWriteTSExclusive|StagedVisibilityActive' adapter distribution internal kv store -g '*.go'

Note: broad go test ./adapter ./store -count=1 -timeout=300s timed out in the adapter integration suite; store passed before adapter hit the package timeout, and the touched-path adapter tests above passed.

…ain' into design/hotspot-split-m2-cross-group

# Conflicts:
#	kv/fsm_migration_fence_test.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 (db7a325):

  • distribution/engine.go
  • distribution/engine_test.go
  • distribution/migrator.go
  • distribution/migrator_export_plan_test.go
  • kv/fsm.go
  • kv/sharded_coordinator.go

What to check, by subsystem:

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

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


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

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

@chatgpt-codex-connector

Copy link
Copy Markdown

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

@bootjp

bootjp commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Merged latest design/hotspot-split-m2-fence-drain into this branch in db7a325 after #1087 added the S3 bucket auxiliary write-fence fix.

Conflict resolution:

  • Kept the existing write-floor FSM helper from this PR.
  • Kept the new S3 bucket auxiliary write-fence helper/regression tests from the parent branch.

Validation:

  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./kv -run 'TestFSMRejects.*WriteFenced|TestShardedCoordinatorRejects.*WriteFenced|TestRouteKeyFilterIncludesS3BucketAuxiliary|TestShardStoreScanAtRestrictsStagedVisibilityToSafeFrontier|TestStagedVisibilityCandidateBoundary_UsesSafeFrontier|TestOnePhaseDedup_NoOpsWhenPriorAttemptLandedAsStagedVersion|TestWriteTimestampFloor' -count=1 -timeout=240s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./adapter -run 'TestDistributionServer(GetRouteOwnership|GetIntersectingRoutes|OwnershipRPCs)|TestInternal(ExportRangeVersions|ImportRangeVersions)|TestGRPCServer_Raw(Get|ScanAt|LatestCommitTS|ReadFenceHelpers)|TestStampRawTimestampsRejectsRouteWriteFloor|TestS3Server_(BucketAndObjectLifecycle|PutBucketAcl_ChangeToPublicAndBack|GetBucketAcl_Private|GetBucketAcl_PublicRead|AdminDeleteBucket|PutBucketAcl_RejectsXMLBody)' -count=1 -timeout=300s
  • GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint --config=.golangci.yaml run ./adapter ./distribution ./internal/s3keys ./kv ./store --timeout=5m
  • git diff --check

Caller audit:

  • rg -n 's3BucketAuxiliaryRouteRange|s3BucketAuxiliaryRouteInRange|GetAtWithReadFence|LatestCommitTSWithReadFence|scanRoutesAtWithReadFence|scanRouteAtDirectionWithReadFence|rejectWriteFencedPointElems|rejectWriteFencedPointKey|verifyRouteNotFencedForKey|MigrationStagedDataKey|MinWriteTSExclusive|StagedVisibilityActive' adapter distribution internal kv store -g '*.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: db7a3253e4

ℹ️ 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/internal.go Outdated
if !ok {
return false
}
routeKey := s3keys.RouteKey(bucket, 0, "")

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 Include S3 bucket auxiliaries for intersecting routes

When exporting MigrationFamilyS3BucketMeta/MigrationFamilyS3BucketGeneration, this predicate checks only the single key RouteKey(bucket, 0, ""). If a split migrates a suffix inside the same bucket route prefix (for example a generation/object range after that sentinel), the bucket auxiliary row still needs to move because the bucket prefix intersects the route, but this returns false and the target can import objects without their bucket metadata/generation. Use the bucket-any-generation prefix intersection logic instead of one concrete generation-0 route key.

Useful? React with 👍 / 👎.

Comment thread adapter/internal.go
return err
}
r.Ts = ts
if err := i.rejectWriteTimestampFloorMutations(r.Mutations, r.Ts); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject pre-stamped forwarded writes below the floor

This floor check only runs when the leader assigns a missing raw Ts; forwarded raw requests that already have Ts skip the r.Ts != 0 case above and are committed without consulting the leader's current route floor. During migration, a follower with a stale catalog can forward a write timestamped at or below MinWriteTSExclusive, bypassing the invariant this helper is meant to enforce and allowing an old-timestamp write to land after cutover. Run the floor check for every raw request after Ts is known, not only for newly stamped ones.

Useful? React with 👍 / 👎.

Comment thread kv/shard_store.go
func (s *ShardStore) scanRouteAtLeaderRouteFilter(
ctx context.Context,
g *ShardGroup,
route distribution.Route,

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 Use staged visibility for route-filtered leader scans

The route-filtered leader path now receives the route, but it still pages from g.Store.ScanAt/ReverseScanAt directly and only uses the route later for lock materialization. When RouteBoundsPresent is used against a route with StagedVisibilityActive, staged-only versions are never considered in filteredKVs, so bounded raw-prefix scans can miss migrated data even though the non-filtered path calls scanRouteWithStagedVisibility. Route-filtered scans should take the same staged/live merge path before applying the route-key filter.

Useful? React with 👍 / 👎.

Comment thread kv/sharded_coordinator.go Outdated
continue
}
rkey := routeKey(elem.Key)
route, ok := c.engine.GetRoute(rkey)

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 Apply write floors to S3 bucket auxiliaries

This floor check only looks up the route for routeKey(elem.Key). For S3 bucket metadata/generation keys that is the raw !s3|bucket|... key, not the bucket's !s3route|... object-route prefix, so a bucket auxiliary write at or below MinWriteTSExclusive can bypass the floor even though the matching write-fence path checks s3BucketAuxiliaryRouteRange. Mirror the auxiliary range check in the point/mutation floor helpers so bucket metadata cannot be written with old timestamps during S3 route migration.

Useful? React with 👍 / 👎.

Comment thread adapter/internal.go
return nil
}

func (i *Internal) rejectWriteTimestampFloorMutations(muts []*pb.Mutation, commitTS uint64) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Check write floors for forwarded txns

This route-floor helper is only wired into stampRawTimestamps; when ForwardRequest.IsTxn is true, stampTxnTimestamps assigns the transaction commit timestamp in the txn meta and then forwards the batch without checking the leader's current MinWriteTSExclusive. A follower with a stale catalog can therefore forward a one-phase transaction for a route that the leader has already floor-fenced, and the FSM replay path allows old timestamps, so the write can commit below the cutover floor. Apply the same floor validation to txn mutations after the commit timestamp is known.

Useful? React with 👍 / 👎.

Comment thread kv/shard_store.go
return s.getGroupAtWithReadFence(ctx, groupID, key, ts, readRouteVersion)
}
g, ok := s.groupForKey(key)
route, g, ok := s.routeAndGroupForKey(key)

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 staged S3 bucket auxiliary reads

GetAt now decides whether to merge staged rows from routeAndGroupForKey(key), but S3 bucket metadata/generation keys do not normalize to the bucket's !s3route|... object-route prefix. During an S3 route migration where those auxiliary rows are imported under MigrationStagedDataKey, loadBucketMetaAt/nextBucketGenerationAt resolve the raw !s3|bucket|... route, skip getAtWithStagedVisibility, and can report a missing or stale bucket after cutover. Use the S3 auxiliary route-range lookup when selecting the staged-visibility route for these point reads.

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 (295cdf6):

  • distribution/engine.go
  • distribution/engine_test.go
  • distribution/migrator.go
  • distribution/migrator_export_plan_test.go
  • kv/fsm.go
  • kv/sharded_coordinator.go

What to check, by subsystem:

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

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


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

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

@chatgpt-codex-connector

Copy link
Copy Markdown

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

@bootjp

bootjp commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Updated in 295cdf6:

  • Apply write-floor checks to pre-stamped forwarded raw requests.
  • Apply write-floor checks to forwarded COMMIT/NONE txn batches after the effective commit timestamp is known; ABORT cleanup remains outside the floor gate.
  • Include S3 bucket auxiliary metadata/generation rows when an S3 bucket route intersects the export route range.
  • Apply S3 bucket auxiliary route floors in coordinator and ShardStore point/mutation write paths.
  • Resolve staged-visible S3 bucket auxiliary point reads and use staged/live visibility for route-filtered leader scans.

Validation:

  • go test ./adapter ./kv -run 'Test(StampRawTimestampsRejectsPreStampedRouteWriteFloor|StampTxnTimestampsRejectsRouteWriteFloor|InternalExportRangeVersionsIncludesS3BucketAuxiliaryForBucketRouteIntersection|ShardStoreGetAt_MergesStagedVisibilityForS3BucketAuxiliary|ShardStoreRejectsS3BucketAuxiliaryWriteAtMigrationTimestampFloor|ShardStoreRouteFilteredLeaderScanUsesStagedVisibility|ShardedCoordinatorRejectsS3BucketAuxiliaryPointWriteAtMigrationTimestampFloor|ShardedCoordinatorRejectsS3BucketAuxiliaryPointWriteOnWriteFencedRoute)' -count=1 -timeout=240s
  • go test ./adapter -run 'Test(Stamp|FillForwarded|Internal(ExportRangeVersions|ImportRangeVersions|PromoteStagedVersions)|GRPCServer_Raw|Milestone1SplitRange|DistributionServer|BuildCatalogSplitOps)' -count=1 -timeout=240s
  • go test ./kv -count=1 -timeout=300s
  • golangci-lint --config=.golangci.yaml run ./adapter ./kv --timeout=5m
  • git diff --check HEAD

Caller audit:

  • rg -n 'stampRawTimestamps|stampTxnTimestamps|fillForwardedTxnCommitTS|forwardedTxnRequestCommitTS|rejectWriteTimestampFloor|ensureS3BucketAuxiliaryWriteTimestampFloor|ensureMutationWriteTimestampFloors|routeAndGroupForKey|routeForExplicitGroupKey|stagedVisibilityRouteForS3BucketAuxiliaryKey|scanRouteAtLeaderRouteFilter|decodedS3BucketRouteFilter|decodedS3BucketRouteIntersects|rangesIntersect|s3BucketAuxiliaryRouteRange' adapter kv -g '*.go'

Note: broad go test ./adapter ./kv -count=1 -timeout=300s timed out in the adapter integration suite; kv completed successfully and the touched-path adapter tests above passed.

@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: 295cdf6bde

ℹ️ 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/shard_store.go
}

func (s *ShardStore) routeAndGroupForKey(key []byte) (distribution.Route, *ShardGroup, bool) {
if route, ok := s.stagedVisibilityRouteForS3BucketAuxiliaryKey(key); ok {

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 Route S3 auxiliary writes to the staged owner

When an S3 bucket route has staged visibility and the raw bucket-metadata keyspace is owned by a different group, this branch makes ShardStore.GetAt(BucketMetaKey/GenerationKey) read and merge from route.GroupID, but S3 bucket updates still enter through ShardedCoordinator.groupMutationsShardRouter.ResolveGroup(routeKey(rawKey)), which does not use s3BucketAuxiliaryRouteRange. A bucket-fence write from PutObject/CreateBucket with a commit timestamp above the floor can therefore commit on the raw owner while subsequent reads are redirected here to the staged target and never see it; the write path needs the same auxiliary-route resolution before enabling this read redirect.

Useful? React with 👍 / 👎.

Comment thread kv/sharded_coordinator.go
if _, _, ok := distribution.MigrationStagedDataKeyParts(key); ok {
return nil, false
}
route, ok := c.engine.GetRoute(routeKey(key))

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 Add staged aliases for S3 bucket auxiliary txn keys

For S3 bucket meta/generation keys, routeKey(key) remains the raw !s3|bucket|... key because the S3 extractor only normalizes object-scoped keys, so this helper returns no staged alias even while ShardStore reads merge staged bucket auxiliary rows. In a transaction that updates BucketMetaKey during staged visibility, the Raft request’s ReadKeys miss the !dist|migstage|... version, so Pebble OCC can fail to notice an imported staged version newer than startTS and allow a stale bucket metadata overwrite; this needs the same auxiliary-route lookup used by the point-read path.

Useful? React with 👍 / 👎.

Comment thread adapter/internal.go Outdated
if route.MinWriteTSExclusive == 0 || commitTS > route.MinWriteTSExclusive {
return false
}
return kv.RouteKeyFilter(route.Start, route.End)(key)

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 Check forwarded DEL_PREFIX floors by range

This helper treats every forwarded raw mutation as a point key. When a follower forwards a pre-stamped DEL_PREFIX/FLUSHALL whose prefix intersects a route with commitTS <= MinWriteTSExclusive (for example, an empty prefix while a migrated route starts at m), this route-key predicate tests the prefix bytes themselves and returns false. The per-group FSM only checks write fences before applying prefix tombstones to its local Pebble store, so forwarded prefix deletes can still land below the migration floor unless DEL_PREFIX uses the same route-prefix intersection check as the coordinator path.

Useful? React with 👍 / 👎.

Comment thread adapter/internal.go

func decodedS3BucketRouteIntersects(bucket string, routeStart, routeEnd []byte) bool {
bucketRouteStart := s3keys.RoutePrefixForBucketAnyGeneration(bucket)
return rangesIntersect(routeStart, routeEnd, bucketRouteStart, prefixScanEnd(bucketRouteStart))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve raw-route matches for S3 bucket exports

When exporting the S3 bucket meta/generation families with a route bound that covers the raw S3 namespace (for example ["!s3|", +∞) during an internal-prefix migration), this decoded-only predicate tests only the synthetic !s3route|bucket... interval. RouteKeyFilter/MigrationBracket.ContainsRoutedKey also accept the raw bucket key when it is directly in the route range, so this RPC path skips those bucket metadata/generation rows and the target can promote without the bucket-level state.

Useful? React with 👍 / 👎.

Comment thread adapter/internal.go
if req == nil {
return nil, errors.WithStack(status.Error(codes.InvalidArgument, "import range versions request is nil"))
}
if i.migrationProposer == 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 Reject zero migration import identifiers

If an ImportRangeVersions caller omits job_id (and similarly bracket_id), this handler still proposes and acknowledges the batch. The imported versions are then staged under job 0, but staged visibility only activates for routes with a non-zero MigrationJobID, so the target can permanently ack data that no promoted route will read; validate the identifiers before proposing the Raft command.

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 (650c450):

  • distribution/engine.go
  • distribution/engine_test.go
  • distribution/migrator.go
  • distribution/migrator_export_plan_test.go
  • kv/fsm.go
  • kv/sharded_coordinator.go

What to check, by subsystem:

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

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


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

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

@bootjp

bootjp commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Updated in 650c450:

  • route S3 bucket auxiliary point writes and txn read/write aliases through the staged visibility owner.
  • check forwarded DEL_PREFIX write floors by routed prefix range, including empty-prefix broadcasts.
  • preserve raw-route S3 bucket auxiliary export matches without widening decoded !s3route| bucket bounds.
  • reject migration imports missing job_id or bracket_id before proposing.

Caller audit:

  • rg -n "ResolveGroup\(|resolveGroupAndRouteForKey|stagedVisibilityRouteForS3BucketAuxiliaryKey|groupMutations\(|groupReadKeysByShardID|stagedVisibilityReadKeyAlias|RoutePrefixRange|routeWriteTimestampFloorApplies|rejectWriteTimestampFloorMutations|validateImportRangeVersionsRequest|ImportRangeVersions\(|decodedS3BucketRouteFilter|decodedS3BucketRouteIntersects|s3BucketRouteBounds" kv adapter -g "*.go"

Validation:

  • go test ./adapter ./kv -run "Test(StampRawTimestampsRejectsPreStampedDelPrefixRouteWriteFloor|StampRawTimestampsRejectsPreStampedRouteWriteFloor|StampTxnTimestampsRejectsRouteWriteFloor|InternalExportRangeVersions(PreservesS3BucketRawRouteMatches|UsesDecodedS3BucketRouteFilter|DecodedS3EmptyRouteEndIsUnbounded|IncludesS3BucketAuxiliaryForBucketRouteIntersection)|InternalImportRangeVersions(AppliesStoreBatch|RejectsMissingIdentifiers)|ShardedCoordinatorRoutesS3BucketAuxiliaryWriteToStagedOwner|GroupReadKeysByShardID_RoutesS3BucketAuxiliaryToStagedOwner|ShardedCoordinatorRejectsS3BucketAuxiliaryPointWriteAtMigrationTimestampFloor|ShardStoreGetAt_MergesStagedVisibilityForS3BucketAuxiliary)" -count=1 -timeout=240s
  • go test ./adapter -run "Test(Stamp|Internal(ExportRangeVersions|ImportRangeVersions|PromoteStagedVersions|ApplyTargetStagedReadiness)|GRPC|S3|SQS|Dynamo|Redis.*Migration)" -count=1 -timeout=300s
  • go test ./kv -count=1 -timeout=300s
  • golangci-lint --config=.golangci.yaml run ./adapter ./kv --timeout=5m
  • git diff --check / git diff --cached --check

Note: go test ./adapter ./kv -count=1 -timeout=300s timed out in adapter on existing long-running tests TestRedis_StreamXReadLatencyIsConstant, Test_consistency_satisfy_write_after_read_sequence, and Test_grpc_transaction; kv completed successfully in that run.

@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: 650c45072f

ℹ️ 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/shard_store.go
Comment on lines +168 to +169
stagedKey := distribution.MigrationStagedDataKey(route.MigrationJobID, key)
staged, stagedOK, err := latestMVCCVersionAt(ctx, g.Store, stagedKey, 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.

P1 Badge Delete staged rows for prefix deletes

When staged visibility is active and a non-empty DEL_PREFIX is applied after cutover, the apply path still deletes only the raw prefix, but imported rows that exist only under !dist|migstage|<job>|<rawKey> do not match that raw prefix. This merge then continues to read the staged alias with no newer live tombstone, so prefix deletes such as bucket/list cleanup can leave migrated-but-not-promoted keys visible; the delete path needs to also tombstone/delete the corresponding staged prefix (including the staged form of any exclude prefix) or materialize raw tombstones for staged-only keys.

Useful? React with 👍 / 👎.

Comment thread adapter/internal.go
if !ok {
continue
}
if err := i.rejectWriteTimestampFloorMutations(r.Mutations, commitTS); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Ignore txn metadata when checking routed floors

For forwarded transactions on clusters where an unrelated low route has a migration write floor, this checks the synthetic txn metadata mutation along with user writes. txnMetaMutation uses the bare !txn|meta| key, which routeKey maps to the empty key, so any floor on the first route can reject a transaction whose actual writes are all on a different route with no floor; skip the metadata mutation here and apply the floor check only to the real write keys.

Useful? React with 👍 / 👎.

Comment thread kv/shard_store.go
Comment on lines +2837 to +2841
start, end, ok := s3BucketAuxiliaryRouteRange(key)
if !ok {
return distribution.Route{}, false
}
for _, route := range s.engine.GetIntersectingRoutes(start, end) {

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 Include staged S3 bucket metadata in scans

This adds the S3 bucket auxiliary staged route lookup only for point-key routing, but bucket listing paths scan the raw !s3|bucket|meta| prefix via ScanAt. Those scans still choose routes by intersecting the raw range, which does not overlap the staged owner’s !s3route|<bucket> range, so bucket metadata imported to the target group remains invisible to ListBuckets/AdminListBuckets until promotion even though point reads are merged.

Useful? React with 👍 / 👎.

Comment thread kv/sharded_coordinator.go
Comment on lines +1122 to +1123
if route, ok := c.engine.GetRoute(rkey); ok && route.MinWriteTSExclusive != 0 && commitTS <= route.MinWriteTSExclusive {
return errors.Wrapf(ErrRouteWriteTimestampTooLow, "key %q routeKey %q commit_ts=%d floor=%d", key, rkey, commitTS, route.MinWriteTSExclusive)

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 Skip raw-route floors for S3 auxiliary keys

For S3 bucket meta/generation keys, ownership is determined by s3BucketAuxiliaryRouteRange, but this raw routeKey floor check runs first. If an unrelated raw !s3|... range has a migration floor, bucket auxiliary writes for buckets whose !s3route|... owner is not fenced are rejected before the auxiliary-route check can decide, unlike the shard-store path which checks only the staged/bucket route for these keys.

Useful? React with 👍 / 👎.

Comment thread kv/shard_store.go
stagedStart, stagedEnd := stagedVisibilityScanBounds(route.MigrationJobID, start, end)
window := stagedVisibilityCandidateWindow(limit)
for {
liveKVs, err := scanVisibleCandidates(ctx, g.Store, start, end, window, ts, reverse)

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 Filter staged control keys from live scans

When a staged-visibility scan's raw range includes !dist|migstage| (for example a full raw scan), the live candidate scan reads those shadow rows as ordinary keys before the separate staged decode pass. stagedVisibilityCandidateKeys will then materialize both the decoded raw key and the !dist|migstage|... key itself, leaking migration control rows to clients; filter staged-data keys out of liveKVs before merging.

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 (19a28e9):

  • distribution/engine.go
  • distribution/engine_test.go
  • distribution/migrator.go
  • distribution/migrator_export_plan_test.go
  • kv/fsm.go
  • kv/sharded_coordinator.go

What to check, by subsystem:

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

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


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

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

@chatgpt-codex-connector

Copy link
Copy Markdown

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

@bootjp

bootjp commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Updated in 19a28e9:

  • Keep migration write timestamp floor validation on proposal/direct-write paths, but skip it in ShardStore raft replay wrappers so historical accepted entries are not rejected against the current catalog floor.
  • Add coverage that direct writes still reject at the floor while ApplyMutationsRaft, ApplyMutationsRaftAt, DeletePrefixAtRaft, and DeletePrefixAtRaftAt replay below/current floor successfully.

Review candidate audit:

  • Already fixed in latest code: import/export validation, HLC observe, key-family propagation, cursor progress, S3 decoded/raw export matching, forwarded raw/txn floor checks including DEL_PREFIX, staged OCC/write aliases, S3 auxiliary read/write/txn routing, staged scan frontier/physical-limit/route-filtered scan.
  • Fixed now: raft replay floor checks in ShardStore wrappers.

Validation:

  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./kv -run "TestShardStore(RejectsWritesAtMigrationTimestampFloor|RaftApplySkipsMigrationTimestampFloor|ApplyMutations_ValidatesStaged(ReadKeys|WriteKeys)|RouteFilteredLeaderScanUsesStagedVisibility|ScanAt_(ContinuesStagedVisibilityAfterCandidateWindow|IncludesListKeysAcrossShards|RoutesListItemScansByUserKey|RoutesListDeltaScansByUserKey)|ScanAtRestrictsStagedVisibilityToSafeFrontier|PhysicalLimitFailsClosedBeforeStagedVisibilityFallback)" -count=1 -timeout=240s
  • GOCACHE=$(pwd)/.cache GOTMPDIR=$(pwd)/.cache/tmp go test ./kv -count=1 -timeout=300s
  • GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint --config=.golangci.yaml run ./kv --timeout=5m
  • git diff --check / git diff --cached --check

…ain' into design/hotspot-split-m2-cross-group
@bootjp

bootjp commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Merged latest design/hotspot-split-m2-fence-drain into this branch in 1db42a8 after the #1087 retry/fence fixes landed.

Validation:

  • go test ./adapter ./distribution -run "Test(DistributionServer(GetRouteOwnership|GetIntersectingRoutes|OwnershipRPCs|SplitRange)|RouteHistorySnapshot|EngineSnapshotAt|EngineGetIntersectingRoutes)" -count=1 -timeout=240s
  • go test ./kv -run "Test(ShardStore.*Raft|ApplyMutationsRaft|DeletePrefixAtRaft)" -count=1 -timeout=240s
  • golangci-lint --config=.golangci.yaml run ./adapter ./distribution ./kv --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 (1db42a8):

  • distribution/engine.go
  • distribution/engine_test.go
  • distribution/migrator.go
  • distribution/migrator_export_plan_test.go
  • kv/fsm.go
  • kv/sharded_coordinator.go

What to check, by subsystem:

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

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


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

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

@chatgpt-codex-connector

Copy link
Copy Markdown

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1db42a8715

ℹ️ 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/shard_store.go
Comment on lines +2809 to +2811
if route, ok := s.stagedVisibilityRouteForS3BucketAuxiliaryKey(key); ok {
g, ok := s.groups[route.GroupID]
return route, g, ok

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 Add staged routing for S3 bucket scans

This point-key special case makes BucketMetaKey/BucketGenerationKey reads resolve to the staged owner, but prefix scans still go through routesForScan with the raw !s3|bucket|meta| bounds used by adapter/s3.go listBuckets and adapter/s3_admin.go AdminListBuckets. Those raw prefixes do not intersect the decoded !s3route|<bucket> route, so after cutover a bucket whose metadata only exists under MigrationStagedDataKey is missing from ListBuckets/admin scans until a live metadata write happens. Please add the same decoded S3 auxiliary route handling to scan route selection.

Useful? React with 👍 / 👎.

Comment thread kv/fsm.go
if !ok {
return distribution.Route{}, false
}
route, ok := snap.RouteOf(routeKey(key))

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 Probe staged S3 auxiliary keys during dedup

For S3 bucket metadata/generation primary keys, routeKey(key) remains the raw !s3|bucket|... key while the staged owner is found via the decoded !s3route|<bucket> range handled elsewhere by s3BucketAuxiliaryRouteRange. A one-phase retry whose prior attempt was imported as a staged version for such a primary misses this staged CommittedVersionAt probe and then applies the reused write set again at the fresh commit_ts, defeating the dedup path. Please resolve S3 auxiliary routes here before falling back to routeKey.

Useful? React with 👍 / 👎.

Comment thread kv/shard_store.go
if kvp == nil {
continue
}
seen[string(kvp.Key)] = bytes.Clone(kvp.Key)

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 Skip staged shadow rows in staged scans

When a staged-visibility scan range includes the reserved !dist|migstage| prefix (for example an unbounded/raw full scan), the live candidate scan can return the shadow rows themselves; adding those keys here makes visibleLogicalKVs emit both the internal staged key and the logical raw key. That leaks migration staging rows and duplicates results instead of keeping staged data visible only through the original key, so staged shadow keys need to be filtered out of the live-candidate path.

Useful? React with 👍 / 👎.

Comment thread adapter/internal.go
Comment on lines +463 to +464
if mut == nil || (len(mut.Key) == 0 && mut.GetOp() != pb.Op_DEL_PREFIX) {
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 Ignore txn metadata in forwarded floor checks

When Internal.Forward handles a transaction, each request includes the synthetic !txn|meta| mutation before the real write keys. This loop now applies route timestamp floors to that metadata key too; routeKey strips the prefix to an empty key, so any forwarded transaction with commit_ts below a floor on the first route is rejected even when all actual mutations belong to an unrelated route. Skip txn metadata here or check only the decoded write keys.

Useful? React with 👍 / 👎.

Comment thread kv/shard_store.go
EndKey: prefixScanEnd(key),
MaxCommitTSInclusive: ts,
MaxVersions: 1,
MaxScannedBytes: 0,

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 Continue latest-version lookups across export pages

Leaving MaxScannedBytes at zero makes ExportVersions install the sparse-scan budget, so a single large/newer version above the requested read timestamp can return Done=false with no eligible Versions. latestMVCCVersionAt then treats that first partial page as not found instead of following NextCursor, causing staged-visibility point reads/scans at older timestamps to drop existing older versions. Loop until a version is found or the export reports done.

Useful? React with 👍 / 👎.

Base automatically changed from design/hotspot-split-m2-fence-drain to design/hotspot-split-m2-store-export July 15, 2026 12:10
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