ADR-013: Seat Hold TTL Alignment with Checkout Session TTL β
Status: β Approved β 2026-04-12 Impacts:
schema-commerce.md(Β§seat_reservations, Β§checkout_sessions)
Context β
SeatReservation originally used a 10-minute hold TTL (hold_expires_at), while CheckoutSession used a 30-minute session TTL (expires_at). This mismatch creates a broken UX: a passenger can hold a valid checkout session but lose their seat at the 10-minute mark. This causes a confusing failure at payment time ("Your seat is no longer available").
Conversely, holding seats too long blocks inventory and reduces conversion for other shoppers.
Options Evaluated β
| # | Option | Pros | Cons |
|---|---|---|---|
| 1 | Extend seat hold to match session TTL (30 min) | Simple, consistent, no UX failure risk | Blocks inventory longer |
| 2 | Heartbeat refresh during checkout | Inventory-friendly (short default + extend on activity) | Complex frontend logic, requires WebSocket or polling |
| 3 | Re-acquire at payment time | Minimal hold time | Risk: another shopper may already hold the seat, poor UX |
Decision β
Option 1 for V1: Align seat hold TTL to checkout session TTL (30 minutes).
SeatReservation.hold_expires_atis set to the same value asCheckoutSession.expires_atat session creation time.- Both expire simultaneously, maintaining consistency.
- The
seat_hold_cleanupHasura Scheduled Trigger (runs every 60s) releases expired holds. - The
checkout_abandoned_sweepHasura Scheduled Trigger (runs every 5 min) expires abandoned sessions.
Phase 2 consideration: The team may implement Option 2 (heartbeat refresh) as a yield optimization when inventory pressure increases. This would shorten the default hold to 10 minutes but extend on frontend activity (every 5 minutes), capped at the session TTL.
Consequences β
Positive:
- Zero UX failures from mismatched TTLs
- Simple implementation β single TTL source, no frontend polling
- Consistent cleanup: both triggers work in harmony
Negative:
- 30-minute seat holds are longer than typical e-commerce (5β15 min). Acceptable for bus tours where operators measure inventory in dozens of seats, not thousands.
Neutral:
- Phase 2 heartbeat optimization is backwards-compatible (reduces TTL, doesn't change cleanup mechanics)