Skip to content

migration: complete target promotion catalog state#1090

Open
bootjp wants to merge 209 commits into
design/hotspot-split-m2-cross-groupfrom
design/hotspot-split-m2-promotion-complete
Open

migration: complete target promotion catalog state#1090
bootjp wants to merge 209 commits into
design/hotspot-split-m2-cross-groupfrom
design/hotspot-split-m2-promotion-complete

Conversation

@bootjp

@bootjp bootjp commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Author: bootjp

Summary:

  • Add the default-group target promotion completion transition.
  • Clear only staged route fields while retaining min_write_ts_exclusive.
  • Persist the route descriptor update and SplitJob promotion witness in one catalog batch.
  • Cover idempotent cleared-descriptor retries and stale input rejection.

Tests:

  • go test ./distribution -run 'Test(CompleteTargetPromotion|CatalogStoreCompleteSplitJobTargetPromotion)'\n- go test ./distribution\n- go test -run '^$' ./...\n- GOCACHE=$(pwd)/.cache GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint run ./distribution --timeout=5m

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a8d4594d-88e0-466a-bd5e-d90d9f80cc93

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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 (351477d):

  • distribution/migration_promotion_complete.go
  • distribution/migration_promotion_complete_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.

@bootjp

bootjp commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

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

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the target promotion completion logic for split job migrations, including the state transition function CompleteTargetPromotionState and the transactional catalog store method CompleteSplitJobTargetPromotion. Feedback suggests strengthening defensive checks by validating the route's parent ID and start key during route clearing, and removing a redundant history lookup that could impact performance.

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 on lines +83 to +85
if !route.StagedVisibilityActive || route.GroupID != job.TargetGroupID {
return nil, errors.WithStack(ErrMigrationInvalidRoute)
}

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.

medium

To enforce defensive programming and ensure catalog consistency, we should also validate that the target route's ParentRouteID and Start key match the split job's SourceRouteID and SplitKey before clearing the staged visibility fields. This prevents accidentally promoting an inconsistent or incorrect route descriptor.

Suggested change
if !route.StagedVisibilityActive || route.GroupID != job.TargetGroupID {
return nil, errors.WithStack(ErrMigrationInvalidRoute)
}
if !route.StagedVisibilityActive || route.GroupID != job.TargetGroupID || route.ParentRouteID != job.SourceRouteID || !bytes.Equal(route.Start, job.SplitKey) {
return nil, errors.WithStack(ErrMigrationInvalidRoute)
}

Comment on lines +151 to +158
if err := s.expectLiveSplitJobAt(ctx, expected.JobID, expectedRaw, readTS); err != nil {
return 0, 0, nil, err
}
if _, found, err := s.historySplitJobAt(ctx, expected.JobID, readTS); err != nil {
return 0, 0, nil, err
} else if found {
return 0, 0, nil, errors.WithStack(ErrCatalogSplitJobConflict)
}

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.

medium

The check s.historySplitJobAt is redundant and inefficient here. Since s.expectLiveSplitJobAt has already succeeded, the split job is guaranteed to be in the live state. In a consistent MVCC catalog, a split job cannot exist in both the live state and the history state simultaneously. Scanning the entire history of split jobs (which can grow very large in production) is expensive and unnecessary. We can safely remove this check.

Suggested change
if err := s.expectLiveSplitJobAt(ctx, expected.JobID, expectedRaw, readTS); err != nil {
return 0, 0, nil, err
}
if _, found, err := s.historySplitJobAt(ctx, expected.JobID, readTS); err != nil {
return 0, 0, nil, err
} else if found {
return 0, 0, nil, errors.WithStack(ErrCatalogSplitJobConflict)
}
if err := s.expectLiveSplitJobAt(ctx, expected.JobID, expectedRaw, readTS); err != nil {
return 0, 0, nil, err
}
References
  1. Remove dead or unreachable code paths, such as asynchronous handlers for tasks that are always performed synchronously, to improve code clarity and maintainability.

@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: 351477d4e1

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

out.Changed = true
out.ClearedRouteIDs = cleared
out.Job.TargetPromotionDone = true
out.Job.PromotionCompletedTS = completionTS

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 Record the catalog CAS timestamp

In the promotion-complete path, this stores the caller-supplied completionTS in promotion_completed_ts, but the design contract for that field is the HLC commit timestamp of the default-group CAS that clears the descriptor (see docs/design/2026_06_11_proposed_hotspot_split_milestone2_migration.md, promotion_completed_ts). In normal calls the target-local promotion timestamp/max promoted timestamp can differ from the catalog MVCC commit timestamp chosen later in applyPromotionCompleteMutations, so target cleared-descriptor ACKs can prove or wait on the wrong timestamp; compute the catalog commit timestamp before encoding the job and persist that value instead.

Useful? React with 👍 / 👎.

@bootjp
bootjp force-pushed the design/hotspot-split-m2-promotion-complete branch from 351477d to c57e42a Compare July 13, 2026 16:27
@bootjp

bootjp commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

Addressed review feedback in c57e42a:

  • validate target parent route and split key before clearing staged route fields
  • remove the redundant live+history conflict scan from the promotion-complete read path
  • store the actual catalog CAS commit timestamp in promotion_completed_ts

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

  • distribution/migration_promotion_complete.go
  • distribution/migration_promotion_complete_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.

@bootjp
bootjp force-pushed the design/hotspot-split-m2-promotion-complete branch from c57e42a to 2edefb8 Compare July 13, 2026 16:31
@bootjp

bootjp commented Jul 13, 2026

Copy link
Copy Markdown
Owner Author

Follow-up in 2edefb8:

  • removed caller-supplied completion timestamp from the promotion-complete APIs
  • the catalog CAS commit timestamp is now assigned immediately before encoding the SplitJob witness

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

  • distribution/migration_promotion_complete.go
  • distribution/migration_promotion_complete_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 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: 48a4da82d3

ℹ️ 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 store/migration_readiness.go Outdated
state.MigrationJobID = binary.BigEndian.Uint64(rest[:migrationUint64Bytes])
rest = rest[migrationUint64Bytes:]
state.MinWriteTSExclusive = binary.BigEndian.Uint64(rest[:migrationUint64Bytes])
return state, rest[migrationUint64Bytes:], true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Decode V1 readiness records from the current offset

When a node loads or restores a version-1 target readiness record, rest has already been advanced past the four fixed uint64 fields, so slicing rest[migrationUint64Bytes:] skips the first 8 bytes of the encoded route range. That makes valid V1 records fail decodeTargetReadinessRange, so a rolling upgrade or snapshot restore with retained V1 readiness metadata can reject the record as corrupt instead of preserving the migration fence state.

Useful? React with 👍 / 👎.

Comment thread adapter/distribution_server.go Outdated
if parent.State != distribution.RouteStateActive {
return distribution.RouteDescriptor{}, grpcStatusError(codes.FailedPrecondition, errDistributionSourceRouteNotActive.Error())
}
splitKey := distribution.CloneBytes(req.GetSplitKey())

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 Normalize migration split keys before planning

When StartSplitMigration is used for filesystem chunk-domain splits, this keeps the raw RPC split key instead of normalizing it the way SplitRange does. A raw chunk key is rejected as outside the virtual route interval, and an in-domain key such as a chunk route plus a suffix can be persisted as the job split key, allowing the async migration path to bisect one file's chunk route and bypass the existing file-pinned hotspot guard; normalize once and use that value for validation and job creation.

Useful? React with 👍 / 👎.

Comment thread kv/fsm_migration_readiness.go Outdated

func migrationMutationsIntersect(muts []*pb.Mutation, routeStart []byte, routeEnd []byte) bool {
for _, mut := range muts {
if mut == nil || len(mut.Key) == 0 || isTxnInternalKey(mut.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 Handle empty-prefix deletes in migration write tracking

When the source write tracker is armed and a supported DEL_PREFIX/flush-all mutation uses an empty key, this guard skips it before routePrefixRange can map it to the whole route keyspace. An in-flight flush with a commit timestamp below the migration snapshot can therefore be omitted from MinAdmittedTS, letting delta copy keep the snapshot floor and miss tombstones for keys that were already backfilled.

Useful? React with 👍 / 👎.

Comment thread kv/shard_store.go
Copilot AI and others added 4 commits July 20, 2026 04:42
## Summary
- promote the hotspot split M2 migration design from partial to
implemented
- record the completed production runner, current-voter barriers, route
cutover, cleanup, DONE history, and deterministic Jepsen workload
- update the parent hotspot design and scaling roadmaps to the
implemented path and current status

## Evidence
- PR #1096 merged the final M2 runner/lifecycle slice into this stacked
base
- latest #1096 head review reported no major issues and all GitHub
checks passed
- targeted Go migration, voter barrier, cleanup, promotion, and route
tests passed
- `lein test elastickv.split-workload-test` passed (2 tests, 5
assertions)
- old partial filename references are absent

## Remaining scope
- M3 owns automatic hotspot detection, target selection, and split
scheduling
- M4 owns the broader route-shuffle nemesis matrix and production-scale
fault campaigns
- reverse migration after CUTOVER and concurrent migration jobs remain
future extensions
@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 (4ed488c):

  • distribution/migration_promotion_complete.go
  • distribution/migration_promotion_complete_test.go
  • distribution/split_job_catalog.go
  • distribution/split_job_catalog_test.go
  • distribution/split_job_lifecycle.go
  • kv/fsm.go
  • kv/sharded_coordinator.go
  • store/mvcc_store.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 (ebf6878):

  • distribution/migration_promotion_complete.go
  • distribution/migration_promotion_complete_test.go
  • distribution/split_job_catalog.go
  • distribution/split_job_catalog_test.go
  • distribution/split_job_lifecycle.go
  • kv/fsm.go
  • kv/sharded_coordinator.go
  • store/mvcc_store.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 (f79bc00):

  • distribution/migration_promotion_complete.go
  • distribution/migration_promotion_complete_test.go
  • distribution/split_job_catalog.go
  • distribution/split_job_catalog_test.go
  • distribution/split_job_lifecycle.go
  • kv/fsm.go
  • kv/sharded_coordinator.go
  • store/mvcc_store.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 23, 2026

Copy link
Copy Markdown
Owner Author

Addressed the latest-head review findings in f79bc00:

  • persisted PREPARE commit timestamps in durable txn locks and exposed them through migration lock drain entries
  • normalized StartSplitMigration split keys before validation and job creation
  • recorded target readiness updates with the Raft applied index in one batch
  • tracked empty DEL_PREFIX migration writes and normalized filesystem/S3 auxiliary scan readiness fences

Verification:

  • go test ./kv ./store ./distribution ./adapter -run 'TestTxnLockCommitTSRoundTrip|TestTxnLockLegacyRoundTripOmitsCommitTS|TestDecodeTxnLockRejectsInvalidOptionalCommitTS|TestFSMPrepareUsesCommitTSForRouteFloorWhenPresent|TestPendingTxnLocksInRouteFiltersLocksByRouteKey|TestApplyTargetStagedReadinessCommandPersistsAppliedIndex|TestRecordMigrationWriteTracksEmptyDelPrefix|TestShardStoreS3BucketAuxiliaryScanChecksSourceReadFenceInRouteSpace|TestShardStoreFilesystemChunkScanChecksSourceReadFenceInRouteSpace|TestTargetStagedReadinessCodecDecodesV1|TestCatalogStoreBeginSplitJobAbandonAllowsPlannedPhase|TestDistributionServerStartSplitMigration_NormalizesFilesystemChunkSplitKey' -count=1
  • go test ./adapter -run 'TestDistributionServer(StartSplitMigration|SplitRange)' -count=1
  • go test ./... -run '^$'
  • make -C proto breaking
  • golangci-lint run ./kv ./store ./distribution ./adapter --timeout=5m

@codex review

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f79bc00762

ℹ️ 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/distribution_server.go Outdated
}
err := s.updateSplitJobViaCoordinator(ctx, job.JobID, func(current distribution.SplitJob) (distribution.SplitJob, error) {
if current.Phase == job.Phase {
current.CapabilityRegressed = true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Fail closed when split capability regresses

When the cluster capability gate starts failing after a split job has already armed migration side effects, this path only records CapabilityRegressed and pauses the runner; a repo-wide search shows the bit is not consulted by read/write routing or apply paths. In that downgraded-voter window, coordinators can keep serving the moving range while a regressed participant may ignore the write tracker/fences, so resuming the job later can compute an unsafe delta floor and miss writes. Please make this state fail closed for the moving route, or restrict it to phases before any migration guards/imports have been installed.

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 (4163665):

  • distribution/migration_promotion_complete.go
  • distribution/migration_promotion_complete_test.go
  • distribution/split_job_catalog.go
  • distribution/split_job_catalog_test.go
  • distribution/split_job_lifecycle.go
  • kv/fsm.go
  • kv/sharded_coordinator.go
  • store/mvcc_store.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 23, 2026

Copy link
Copy Markdown
Owner Author

Addressed the latest-head capability regression fail-closed review in 4163665.

Changes:

  • record capability regression before waiting on fail-closed guard synchronization
  • apply conservative source read/write fences plus write tracking and target readiness guards after migration side effects exist
  • restore phase-normal migration controls after capability recovery before clearing CapabilityRegressed
  • add runner coverage for regression after Backfill side effects

Verification:

  • go test ./adapter -run 'TestDistributionServerRunSplitJobRunnerOnce_(PausesOnCapabilityRegression|FailClosesCapabilityRegressionAfterSideEffects)' -count=1\n- go test ./adapter -run 'TestDistributionServer(RunSplitJobRunnerOnce|StartSplitMigration|SplitRange)' -count=1\n- go test ./distribution ./kv ./store -count=1\n- go test ./... -run '^$'\n- GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint run ./... --timeout=5m\n- git diff --check\n\nLatest-head unresolved review threads are now 0. GitHub checks are still running after the push; I will keep watching them.\n\n@codex review

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 416366501b

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread kv/fsm.go
Comment thread adapter/internal.go Outdated
Comment thread adapter/internal.go Outdated
Comment thread kv/fsm.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 (d57699d):

  • distribution/migration_promotion_complete.go
  • distribution/migration_promotion_complete_test.go
  • distribution/split_job_catalog.go
  • distribution/split_job_catalog_test.go
  • distribution/split_job_lifecycle.go
  • kv/fsm.go
  • kv/sharded_coordinator.go
  • store/mvcc_store.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 23, 2026

Copy link
Copy Markdown
Owner Author

Addressed the latest-head review items in d57699d.

Changes:

  • ApplyTargetStagedReadiness no longer depends on the promote opcode gate, so capability-regression fail-closed guards can still be applied through the target Raft group.
  • METADATA_CLEARED probes now include target import ack / HLC-floor metadata, so a lagging target voter cannot be reported clean before applying migration metadata cleanup.
  • Migration write-tracker updates no longer advance metaAppliedIndex ahead of the user write batch; the data mutation / prefix delete batch remains responsible for advancing the applied index.
  • Added regression coverage showing DEL_PREFIX is rejected by durable source fences even when the catalog route itself is active.

Caller audit:

  • Audited ApplyTargetStagedReadiness / ProbeMigrationState / capability-gate callers with rg. The only semantic gate change is the safety guard RPC; PromoteStagedVersions and CleanupMigration still keep their opcode gates.

Verification:

  • go test ./adapter ./kv ./store -run 'Test(InternalApplyTargetStagedReadinessBypassesOpcodeGateForSafetyGuard|InternalApplyTargetStagedReadinessProposesThroughRaft|InternalProbeMigrationMetadataClearedWaitsForImportMetadata|InternalProbeMigrationStateUsesLocalFSMAndCatalog|FSMDurableSourceFenceRejectsPrefixWrite(OnActiveRoute)?|RecordMigrationWriteDoesNotAdvanceAppliedIndexBeforeDataWrite|ApplyTargetStagedReadinessCommandPersistsAppliedIndex|RecordMigrationWriteTracksEmptyDelPrefix|MigrationImportMetadataPresentClearsWithMigrationState|ImportVersionsIdempotencyAndMetadata)' -count=1
  • go test ./adapter -run 'Test(InternalApplyTargetStagedReadiness|InternalProbeMigration|DistributionServerRunSplitJobRunnerOnce|DistributionServerStartSplitMigration|DistributionServerSplitRange)' -count=1 -timeout=10m
  • go test ./adapter -run '^$' -count=1 -timeout=10m
  • go test ./kv ./store -count=1 -timeout=10m
  • go test ./... -run '^$' -count=1 -timeout=10m
  • GOLANGCI_LINT_CACHE=$(pwd)/.golangci-cache golangci-lint --config=.golangci.yaml run ./adapter ./kv ./store --timeout=5m --allow-parallel-runners
  • git diff --check

Note: go test ./adapter ./kv ./store -count=1 hit the adapter package's default 10m timeout in broad integration coverage after the targeted and package compile checks had passed, so I did not treat that as evidence for this focused change.

@codex review

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

ℹ️ 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/split_job_runner.go Outdated
Comment on lines +641 to +642
if err != nil || !metadataComplete {
return err

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Don't treat incomplete source metadata probes as success

When any source voter still reports Ready=false for METADATA_CLEARED, syncSplitMigrationVoterBarrier returns metadataComplete=false with a nil error. This branch returns that nil error, so cleanupSplitJobSourceProofs treats the metadata cleanup as successful and advances the job past the retry point, releasing the retention pin and allowing the split to finish while a lagging source voter may still retain migration fence/tracker metadata. Return an explicit incomplete signal here instead of success.

Useful? React with 👍 / 👎.

Comment thread store/mvcc_store.go
@bootjp

bootjp commented Jul 24, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Addressed the latest-head metadata cleanup and MVCC snapshot findings in dcb7744.

Changes:

  • Metadata cleanup barriers now return an explicit incomplete error when any voter still reports METADATA_CLEARED=false, so cleanup retry points are not advanced while migration metadata may remain on a voter.
  • In-memory MVCC snapshots now select the migration snapshot format when import ack or HLC-floor metadata exists, and persist that metadata in an optional v2 section.
  • Streaming MVCC restore into Pebble writes restored import ack / HLC-floor metadata back to Pebble meta keys before exposing the restored DB.

Semantic audit:

  • Checked cleanupSplitJobTargetProofs, cleanupAbandonedSplitJobTarget, cleanupSplitJobSourceMetadata, cleanupSplitJobSourceProofs, and all syncSplitMigrationVoterBarrier call sites. Only metadata cleanup barriers needed fail-closed retry semantics; cursor-bearing barriers still persist progress and retry normally.
  • Checked restoreStreamingMVCCSnapshotBody, readStreamingMVCCRestoreHeader, writeStreamingMVCCRestoreTempDB, and migration import metadata readers/writers across in-memory and Pebble stores.

Validation:

  • go test ./adapter -run 'TestDistributionServerCleanupSplitJobSourceProofsWaitsForMetadataBarrier|TestSplitMigrationVoterBarrierReopensForMembershipChanges' -count=1 -timeout=10m\n- go test ./adapter -run 'Test(DistributionServerRunSplitJobRunnerOnce|DistributionServerCleanupSplitJobSourceProofsWaitsForMetadataBarrier|InternalProbeMigration|InternalApplyTargetStagedReadiness|DistributionServerSplitRange)' -count=1 -timeout=10m\n- go test ./adapter -run '^$' -count=1 -timeout=10m\n- go test ./store -count=1 -timeout=10m\n- go test ./kv ./store -count=1 -timeout=10m\n- golangci-lint --config=.golangci.yaml run ./adapter ./store --timeout=5m --allow-parallel-runners\n- git diff --check HEAD~1..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 (dcb7744):

  • distribution/migration_promotion_complete.go
  • distribution/migration_promotion_complete_test.go
  • distribution/split_job_catalog.go
  • distribution/split_job_catalog_test.go
  • distribution/split_job_lifecycle.go
  • kv/fsm.go
  • kv/sharded_coordinator.go
  • store/mvcc_store.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: dcb7744f35

ℹ️ 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
Comment on lines +185 to +186
Ready: time.Now().UnixMilli() >= req.GetReadDrainNotBeforeMs() &&
(i.readTracker == nil || i.readTracker.Oldest() == 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 Relax read drain to ignore unrelated active reads

When a split reaches source-read-drain cleanup, this probe only reports ready if the process-wide ActiveTimestampTracker is completely empty. In production the same tracker is wired into the Internal service and all frontend read paths, so any unrelated Redis/Dynamo/S3 read on this node keeps Oldest() nonzero even if it started after cutover or targets another range. On a busy node this can keep SOURCE_READ_DRAINED false indefinitely, preventing source cleanup/metadata cleanup and leaving the migration retention pin in place; the drain check needs to distinguish pre-cutover reads for the moving range (or at least reads older than the cutover/grace point) instead of requiring zero global reads.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants