Repository navigation
fix(ladder): plan the gap under the per-config lock - #846
Merged
Merged
Conversation
- PlanAndSaveLadderRun reads the config's in-flight commitment inside the
persist transaction, after taking pg_advisory_xact_lock on
hashtextextended('cudly:ladder-config:'+id), so two overlapping runs
cannot both plan the same gap.
- One shared helper (READ COMMITTED, empty config id rejected, lock_timeout
applied to the advisory wait only) is used by the planner and by
ClaimDueLadderTranches, so a claim cannot fire a row the planner's
netting read had already excluded as expired.
- Lock order documented next to LockPlanProvidersTx: ladder lock first.
- Additive: the old interface methods stay until the handler switches.
Refs #118
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- executeLadderRun now hands Allocate/BuildTranches to PlanAndSaveLadderRun, so the in-flight read, the plan and the tranche insert happen in one transaction under the config's lock. Provider reads stay outside the transaction so no lock is held across network I/O. - Remove SaveLadderRunWithTranches and GetInFlightLadderCommitUSDHr from the store interface and mocks; the handler had the only production caller. - Real-Postgres test: two overlapping runs for one config persist a total of the target, not twice it. Refs #118 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Contributor
Member
Author
|
Gate verdict (Opus independent adversarial review, money path): APPROVE at Head: author's merge of origin/main (#845 squash f606770). The +/- lines of Review:
Local evidence (macOS, throwaway PG17 cluster,
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #118
Stacked on the PR 1a branch (
feat/ladder-lock-store); retarget tomainonce 1a merges.What
Switches
executeLadderRuntoPlanAndSaveLadderRun: Allocate, BuildTranches and plan assembly run in the locked transaction (ladderRunPlanner.plan), so two overlapping runs for one config cannot both plan the same gap. Provider reads (baseline, layer states) stay before the transaction; no lock is held across network I/O. RemovesSaveLadderRunWithTranchesandGetInFlightLadderCommitUSDHrfrom the interface and mocks (the handler had the only production caller; the concrete store methods remain for tests).#842 and #843 are separate executor blockers and are not addressed here.
Verification (macOS, real Postgres 17 throwaway cluster)
TestLadderLock_ConcurrentRunsPlanTheGapOnce: goroutine 1 is held inside the locked builder,pg_locksshows goroutine 2 waiting on this config's key, then released; total live tranches = 10 (target). With the lock statement replaced by a no-op the same test fails: "second run planned while the first still held the lock" and total 20 vs 10.TestLadder*server integration tests pass; handler unit tests pass.Summary by CodeRabbit