Ticket oversell: capacity is never reserved between invoice creation and settlement #69
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Capacity is enforced only in
api_ticket_create(views_api.py:816-821), againstevent.amount_tickets(or the selected wave's), but that counter is decremented only when the invoice settles inset_ticket_paid(services.py:87-97). Nothing holds a seat for an unpaid invoice. With one seat left, K concurrent buyers all pass the gate, all receive an invoice, and all can pay;set_ticket_paidthen runs K times. The per-eventasyncio.Lockinset_ticket_paidserialises the decrement but does not reject the K-1 payments that cannot be seated, so the buyer is charged for a ticket that does not exist in inventory. Since the v1.6.8 rebase the same shape exists per wave (selected_wave.amount_tickets -= 1).Fix direction: reserve at invoice creation with a guarded conditional UPDATE (
... SET reserved = reserved + :q WHERE id = :id AND sold + reserved + :q <= capacity) inside onedb.connect()block so the ext DB lock serialises it; confirm the reservation inset_ticket_paid; release on purchase failure and inpurge_unpaid_tickets. This has to live on the wave (extra.ticket_waves[].amount_tickets) as well as the event-level fallback. The sandbox implemented this against the pre-waves target (sandbox-team/events PR #14,atomicity.py+ migrations_fork m003 +tests/test_reservation.py), which is a usable starting point for the event-level path.Found during reforge run #1 (sandbox events#2).