mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
f09ef7da8f
Closes the 'concurrent proposal-recovery expiry races' gap noted in §8.46. GetProposal (read-side recovery) and RespondToProposal both run the identical expiry-advance SQL in their own transaction, so any number of them can observe the same past-expiry proposal at once — this had never been exercised concurrently, only sequentially (the existing late-response test drives one call at a time). TestPostgreSQLConcurrentProposalExpiryRecoveryAppliesCooldownsExactlyOnce races 8 concurrent GetProposal/RespondToProposal calls, each with a distinct 'now' past the proposal window, against one proposal and asserts: EXPIRED lands on the proposal and both tickets exactly once, a PROPOSAL_TIMEOUT penalty lands exactly once per offending player (not once per racing transaction), and no idempotency row survives a closed-proposal response. The design already defends against this — ProposalParticipantExpireSQL only ever flips a still-PENDING row once, so a losing racer's 'now' can't match recordProposalTimeoutCooldowns' responded_at filter — this test is what actually proves that holds under real concurrent load rather than by inspection. Verified: real postgres:17-alpine container, go test -tags integration ./store/... -run TestPostgreSQLConcurrentProposalExpiryRecoveryAppliesCooldownsExactlyOnce -race -count=3 clean; full -tags integration ./store/... -race run clean; full non-integration go build/vet/test -race clean across every server package; container removed after the run.