Nobody has started this yet — be first.
Business impact
This project's README explicitly documents idempotent commit as a guarantee: "committing an already-committed reservation must return the same result without incrementing sold again, because a retried payment webhook calling this twice is a completely normal occurrence." With this bug, the second call from a retried webhook now gets an error instead of the expected success -- depending on how the caller handles that error, this can mean re-attempting a charge that already went through, or reporting a successful payment as failed to the customer.
Problem
Calling Commit a second time on a reservation that's already committed returns ErrReservationNotActive instead of succeeding with the same result as the first call.
Current behavior
resolve's terminal-state check (status != active) runs before its idempotent no-op check, so it rejects every re-commit before the code ever reaches the check that was supposed to let it through.
Expected behavior
A second Commit call on an already-committed reservation should succeed, returning the same reservation (still status: committed), without changing sold again. The same applies to Release on an already-released reservation.
Steps to reproduce
go test ./tickets/tests/... -run TestTicket15 -vWhy this matters
resolve is supposed to check "is this the idempotent no-op case?" before checking "is this reservation in some other terminal state?". Those two checks have been swapped: the terminal-state check (status != active) now runs first, and it's true for any already-committed reservation (including the one this exact call is trying to no-op against) -- so it rejects the retry before the code ever reaches the idempotency check that was supposed to let it through.
Suggested approach
Look at the order of the if statements at the top of resolve in pkg/inventory/service.go: the "already committed" / "already released" no-op checks, and the "not active" rejection. One of these needs to run before the other.
Acceptance criteria
Verification
TEST_DATABASE_URL=postgresql://... go test ./tickets/tests/... -run TestTicket15 -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/idempotency-check-orderFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/idempotency-check-orderSubmit 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.