Nobody has started this yet — be first.
Business impact
This is the single mechanism that makes it safe for a webhook confirming a capture and an auto-void timeout to race each other without corrupting data -- the entire "optimistic concurrency" story the README documents as a core guarantee. Without it, whichever of two concurrent writers commits last simply overwrites whichever committed first, silently. In payment terms: a merchant's capture request and an internal auto-void timeout racing at nearly the same instant could both report success, with one of them's effect vanishing without a trace, no error logged anywhere, no 409 Conflict ever returned.
Problem
Store.UpdateState's UPDATE statement no longer has AND version = $expectedVersion in its WHERE clause -- only WHERE id = $X. A caller holding a stale version can still successfully overwrite the row.
Current behavior
Store.UpdateState(..., expectedVersion=1) returns applied=true even though the row's actual version had already moved on to a higher value by the time of the call.
Expected behavior
UpdateState should return applied = false whenever the row's actual current version doesn't match the expectedVersion the caller read.
Steps to reproduce
TEST_DATABASE_URL=postgresql://... go test ./tickets/tests/... -run TestTicket20 -v -count=5
The natural "fire two truly concurrent, conflicting ApplyEvent calls" test (TestService_ApplyEvent_Conflict_OnlyOneOfTwoConcurrentEventsApplies) still passes even with this bug present, 5/5 runs -- not because the bug doesn't exist, but because a plain UPDATE always takes a row-level lock regardless of its WHERE clause, so the two transactions still serialize on that lock, and the second one often fails machine.Fire's own validity check before it ever reaches UpdateState at all. This ticket's test instead reproduces the exact condition deterministically: read a payment, have some other write bump its version behind its back, then call UpdateState with the now-stale version directly.
Why this matters
Real concurrency bugs are notoriously hard to pin down with timing-dependent tests -- this is exactly why the project ships a deterministic test alongside its own timing-dependent one for this exact code path.
Suggested approach
Compare UpdateState's current WHERE clause against what its own doc comment says it does ("guarded by WHERE version = expectedVersion"). The expectedVersion parameter is still there -- it's just no longer used in the SQL.
Acceptance criteria
Verification
TEST_DATABASE_URL=postgresql://... go test ./tickets/tests/... -run TestTicket20 -v -count=5
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b fix/optimistic-concurrency-removedFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/optimistic-concurrency-removedSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign indocker composepaystate/paystate/go.modreplace reliable => ../reliabledocker compose up --buildpaystate/context: ..docker-compose.ymlCREATED_TIMEOUTAUTHORIZED_TIMEOUTWork the tickets in tickets/docs/ (ticket-01 through ticket-25); each names one Go test in tickets/tests/:
TEST_DATABASE_URL=postgresql://... go test ./tickets/tests/... -run TestTicket01 -v # a single ticket
TEST_DATABASE_URL=postgresql://... ./tickets/run_tickets.sh # all 25, clean pass/fail summary
Tickets that don't touch Postgres (02, 03, 04, 05, 06, 14, 15, 24) run fine without TEST_DATABASE_URL set; the rest report FAIL with a "TEST_DATABASE_URL not set" message if it's missing. make test-race runs the full go test -race -p 1 ./... suite the same way.
Level 1
Fix a bug
Read existing behaviour, correct it.