Nobody has started this yet — be first.
Business impact
This defeats the entire point of the optimistic locking strategy under the exact conditions it's supposed to handle: real contention. A genuine flash sale means many buyers racing to update the same SKU row -- version conflicts are the expected, normal, recoverable case the retry loop exists for. With this bug, a real conflict now fails immediately instead of retrying, so under real load LOCK_STRATEGY=optimistic would reject far more legitimate buyers than it should.
Problem
A version conflict (ErrVersionConflict) -- the one error the retry loop is specifically built to recover from -- is never retried; it fails on the very first collision, regardless of how many attempts are configured.
Current behavior
IsRetryableClaimError returns !errors.Is(err, ErrVersionConflict) -- the extra ! inverts the whole decision, so a real version conflict is classified as not retryable.
Expected behavior
ErrVersionConflict should be retried (up to MaxAttempts, with backoff). Every other error (insufficient stock, unknown SKU, a generic database fault) should fail immediately -- retrying those wastes the attempt budget on something that can never succeed by trying again.
Steps to reproduce
go test ./tickets/tests/... -run TestTicket19 -vWhy this matters
Claim's retry Policy.Classifier is supposed to be errors.Is(err, ErrVersionConflict). It has an extra !, inverting the whole decision. This ticket's test extracts the classifier into its own named function (IsRetryableClaimError, the exact function Claim's policy uses, not a copy of it) and tests it directly as a pure function -- no database, no concurrency, no interaction with ticket-12's separate arithmetic bug on the same code path.
Suggested approach
Look at IsRetryableClaimError in pkg/inventory/optimistic.go and compare it against its own doc comment directly above it.
Acceptance criteria
Verification
go test ./tickets/tests/... -run TestTicket19 -v
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-classifier-invertedFix 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-classifier-invertedSubmit 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 inSKIP LOCKEDhttp://localhost:8081localhost:5434flash-saleeveryday-widgetcurl localhost:8081/healthzgo run ./cmd/loadtest -url http://localhost:8081 -sku flash-sale -concurrency 10000Work the tickets in tickets/docs/ (ordered easy → hard); each names one dedicated Go test in tickets/tests/:
export TEST_DATABASE_URL=postgresql://inventory:inventory@localhost:5432/inventory_test?sslmode=disable
go test ./tickets/tests/... -run TestTicket24 -v # a single ticket
./tickets/run_tickets.sh # all 25, clean pass/fail summary
Tickets that don't touch Postgres (02, 07, 17, 19) run fine without TEST_DATABASE_URL; the rest t.Skip cleanly, not fail, if it's unset.
Level 1
Fix a bug
Read existing behaviour, correct it.