Skip to content

Examples/Ticketing

Concert ticketing (example)

Concert ticket sales with seat maps, 10-minute seat holds during checkout, and a waiting room in front of the API for on-sale spikes. Postgres holds seat state, a queue feeds ticket issuing, and an object store keeps the ticket PDFs.

Scale: 100k buyers in the first minute of an on-sale; 20k seats per show

123456789101112CLIENTBuyer weband appEDGEWaiting roomEDGECDNSERVICETicketing APICACHESeat availabilitycacheDATABASEPostgresEXTERNALPayment providerQUEUEOrder queueWORKERTicket issuerOBJECT STORETicket storeEXTERNALEmail providerWORKERHold sweeper

Select a component to see what it is responsible for and which state it owns.

  1. 1Buyer web and app → Waiting room: Join queue
  2. 2Buyer web and app → CDN: Load event page
  3. 3Buyer web and app → Ticketing API: View seats
  4. 4Ticketing API → Seat availability cache: Read availability
  5. 5Ticketing API → Postgres: Hold seats
  6. 6Ticketing API → Payment provider: Pay
  7. 7Payment provider → Ticketing API: Payment webhook
  8. 8Ticketing API → Order queue: Order confirmed
  9. 9Ticket issuer → Order queue: Consume orders
  10. 10Ticket issuer → Ticket store: Store ticket
  11. 11Ticket issuer → Email provider: Email ticket
  12. 12Hold sweeper → Postgres: Release expired holds
  • Request / response
  • Asynchronous
  • Bulk data

Select a part to trace its flows.

  • Request / response
  • Asynchronous
  • Bulk data

Parts 12

Flows 12

  1. 1Buyer web and app → Waiting roomJoin queueRequest / response
  2. 2Buyer web and app → CDNLoad event pageRequest / response
  3. 3Buyer web and app → Ticketing APIView seatsRequest / response
  4. 4Ticketing API → Seat availability cacheRead availabilityRequest / response
  5. 5Ticketing API → PostgresHold seatsRequest / response
  6. 6Ticketing API → Payment providerPayRequest / response
  7. 7Payment provider → Ticketing APIPayment webhookAsynchronous
  8. 8Ticketing API → Order queueOrder confirmedAsynchronous
  9. 9Ticket issuer → Order queueConsume ordersRequest / response
  10. 10Ticket issuer → Ticket storeStore ticketBulk data
  11. 11Ticket issuer → Email providerEmail ticketRequest / response
  12. 12Hold sweeper → PostgresRelease expired holdsRequest / response

Invariants 3

  • A seat is sold to one buyer

    Conditional UPDATE on seats that takes only rows that are available or whose hold has expired; the transaction checks the row count and rolls back unless every requested seat was taken.

    Kept by

  • A payment for an expired hold does not sell the seat

    Conditional UPDATE from held to sold that matches the hold id and requires an unexpired hold; a zero row count sends the payment to a refund.

    Kept by

  • The API sees at most the admitted rate

    The waiting room issues signed admission tokens with an expiry at a fixed rate; the API rejects any request without a valid token.

    Kept by