Booking Workspace & 2-Phase Holds
Distributed mutex locking, optimistic concurrency, and 15-minute checkout reservation holds.
The booking workspace manages the real-time reservation workflow, acquiring atomic holds to eliminate race conditions.
Two-Phase Hold Lifecycle
Temporary 15-minute reservations reserve slots during payment without race conditions.
[User Selects Slot]
│
▼ (POST /api/v1/venue/booking-holds)
[Hold Active (10-minute Lock Acquired)] ──(No payment after 10m)──► [Expired / Slot Released]
│
▼ (User Submits Sealed Price Quote & Payment)
[Hold Committed -> Reservation Confirmed]
│
▼ (Customer Arrives)
[Checked-In] ────► [Completed]Safe Retries and Reservation Receipts
Keep the same idempotency key for retries of one booking intent. A matching successful request replays its stored result only after the caller's current tenant and venue operational scope are checked; it does not confirm the reservation or consume quota again. Reusing a key for a different reservation, quote, target, time range, or cancellation/no-show reason returns a conflict. In Booking 360, an unchanged retry retains its key; editing the cancellation or no-show reason starts a new intent with a new key, and a refreshed price quote starts a new confirmation intent. Older receipts without request fingerprints remain readable with narrower comparison guarantees. Venue mutations send the same key in the request body and `Idempotency-Key` header; the shared Axios service makes up to three retries for network/time-out failures and HTTP 500, 502, 503, or 504 responses, using a fresh anti-replay nonce on each attempt.
Client-Side State Reducer
Predictive UI state updates keeping slot displays synchronized across multiple active sessions.
export function reduceBookingWorkspace(
state: BookingWorkspaceState,
action: BookingWorkspaceAction
): BookingWorkspaceState {
switch (action.type) {
case "SET_CRITERIA":
return { ...state, criteria: action.payload, candidates: [] };
case "SET_HOLD_ACQUIRED":
return {
...state,
activeHold: action.payload,
holdExpiryUtc: action.payload.expiryUtc,
stage: "held",
};
case "SET_CONFIRMED":
return { ...state, reservation: action.payload, stage: "confirmed" };
default:
return state;
}
}Booking Hold APIs
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/v1/venue/booking-holds | Acquire atomic 15-minute hold on a resource slot | 🔒 Required |
| POST | /api/v1/venue/reservations/{id}/confirm | Confirm reservation upon successful checkout | 🔒 Required |