Skip to content

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:

  1. BookingService.update_booking (bookingzone.api.booking.update_booking) — releases existing Confirmed locks, then re-locks via lock_slot → atomic_lock_resources.
  2. 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:

  1. set_value each existing BZBookingAvailability row to Released
  2. Call lock_slot → atomic_lock_resources to create new Confirmed locks
  3. Update the BZBooking document

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

  1. Date/time moves on Confirmed bookings go through admin_booking.patch_booking (in-place), not update_booking. The patch_booking endpoint modifies lock rows in place, preserving Confirmed status throughout the operation.

  2. update_booking is frozen for inventory-holding bookings until it is refactored to perform a single atomic replace:

  3. SELECT FOR UPDATE on old lock rows
  4. Insert new Confirmed rows
  5. Release old rows
  6. All within one transaction

Until then, treat update_booking as unsafe for any booking that holds Confirmed locks.

  1. Tests and QA must never call update_booking on ATP Midway or any booking with Confirmed locks. Class-bound SYS-005 smoke tests stay on:
  2. create_booking / payment flow (100OFF promo to skip card)
  3. patch_booking if 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_booking remains available in the whitelist but is effectively forbidden for Confirmed bookings — this split behavior requires documentation and training.
  • Future refactor needed to make update_booking atomic before it can be safely re-enabled.

Risks / Mitigations

  • Risk: Developers or integrations unknowingly call update_booking on Confirmed bookings. Mitigation: Document this ADR in onboarding; add a runtime warning or guard in update_booking when Confirmed locks exist (future work).
  • Risk: patch_booking has undiscovered edge cases. Mitigation: SYS-005 smoke tests exercise patch_booking paths; 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_booking to use SELECT FOR UPDATE and 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):

  1. cart.add_to_cart → Active CART-* locks
  2. bookingzone.api.booking_transaction.create_booking (no payment block) → BZBooking Pending; atomic_confirm_cart_locks → Confirmed
  3. 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)