ADR-0008: Booking Lock-Replace on Edit¶
Status: Accepted
Date: 2026-09-02
Deciders: Anil Kumar Pandey (approved 2026-09-02), Staff Architect
Impacted Repos: reservation-api-server, admin-web-app, venuplus-app
Context¶
Two implementations exist for editing booking date/time:
BookingService.update_booking(bookingzone.api.booking.update_booking) — releases existing Confirmed locks, then re-locks vialock_slot→atomic_lock_resources.admin_booking.patch_booking— performs in-place updates on Confirmed locks without releasing them.
The lock table (BZBookingAvailability) is the inventory source of truth.
When a booking holds Confirmed locks, those rows represent reserved capacity.
The release-then-recreate path in update_booking is not atomic with the
booking save. The sequence is:
set_valueeach existingBZBookingAvailabilityrow toReleased- Call
lock_slot→atomic_lock_resourcesto create new Confirmed locks - Update the
BZBookingdocument
If step 2 fails (slot unavailable, exception, timeout), the original locks remain Released with no replacement. The booking document may or may not reflect the partial state.
Production symptom (#883): A Successful booking created on venuplus-app,
followed by an admin update_booking date/time change, left the sole
BZBookingAvailability row in Released status with no replacement Confirmed
lock. This created an inventory hole — capacity appeared available but was
actually sold.
SYS-005 test suites and desk operators must not call the unsafe path.
Decision¶
-
Date/time moves on Confirmed bookings go through
admin_booking.patch_booking(in-place), notupdate_booking. Thepatch_bookingendpoint modifies lock rows in place, preserving Confirmed status throughout the operation. -
update_bookingis frozen for inventory-holding bookings until it is refactored to perform a single atomic replace: SELECT FOR UPDATEon old lock rows- Insert new Confirmed rows
- Release old rows
- All within one transaction
Until then, treat update_booking as unsafe for any booking that holds
Confirmed locks.
- Tests and QA must never call
update_bookingon ATP Midway or any booking with Confirmed locks. Class-bound SYS-005 smoke tests stay on: create_booking/ payment flow (100OFF promo to skip card)patch_bookingif they need a desk-initiated date/time move
The ATP Midway Confirmed lock is live on DEV (#883_lock_path=yes). Do not
treat it as a fixture to poke.
Consequences¶
Positive¶
- Eliminates the inventory-hole risk from non-atomic lock replacement.
- Provides a clear, safe path for desk date/time edits via
patch_booking. - SYS-005 goldens remain stable by avoiding the unsafe code path.
Negative / Trade-offs¶
update_bookingremains available in the whitelist but is effectively forbidden for Confirmed bookings — this split behavior requires documentation and training.- Future refactor needed to make
update_bookingatomic before it can be safely re-enabled.
Risks / Mitigations¶
- Risk: Developers or integrations unknowingly call
update_bookingon Confirmed bookings. Mitigation: Document this ADR in onboarding; add a runtime warning or guard inupdate_bookingwhen Confirmed locks exist (future work). - Risk:
patch_bookinghas undiscovered edge cases. Mitigation: SYS-005 smoke tests exercisepatch_bookingpaths; monitor production for anomalies.
Alternatives Considered¶
-
Add a fourth cancel/release path: Rejected. Proliferating lock-management code paths increases complexity and bug surface. Keep one lock helper (
atomic_lock_resources) and one confirmation helper (atomic_confirm_cart_locks). -
Dual-write to DynamoDB for lock state: Rejected. Adds operational complexity and split-brain risk without addressing the root cause (non-atomic release-then-lock).
-
Wrap release + re-lock in explicit transaction: Considered but deferred. Requires careful refactor of
update_bookingto useSELECT FOR UPDATEand proper rollback semantics. This ADR freezes the unsafe path until that work is complete.
Implementation Notes¶
Relevant files on reservation-api-server develop branch (read-only
references; no edits in this ADR):
| File | Key Functions |
|---|---|
bookingzone/services/booking_service.py |
update_booking — the unsafe path |
bookingzone/api/booking.py |
Whitelist wrapper for update_booking |
bookingzone/api/admin_booking.py |
patch_booking — the safe desk path |
bookingzone/utils/db_locking.py |
atomic_lock_resources, atomic_confirm_cart_locks |
bookingzone/api/booking_transaction.py |
create_booking, finalize_booking_after_payment |
Write path for SYS-005 goldens (ATP Midway, US Super Pass, US ULTIMATE):
cart.add_to_cart→ ActiveCART-*locksbookingzone.api.booking_transaction.create_booking(no payment block) →BZBookingPending;atomic_confirm_cart_locks→ Confirmed- Payment: venuplus
/api/payment/process→ Datacap (PAN never hits Frappe) →record_external_payment_transaction→finalize_booking_after_payment→ Successful
ADR-0003 (gateway registry) remains binding for real charges; this ADR does not reopen gateway choice.
References¶
- ADR-0001 — Frappe as source of truth
- ADR-0003 — Payment gateway abstraction
- System Constitution Principle VII (Idempotency & Resource Safety)
- Issue #883 — Original production incident (lock-replace failure)