Rescheduling is atomic
Why it matters
Non-atomic reschedules split a logically single operation into two sequential database calls: first release the old slot, then reserve the new one. If the new slot reservation fails (conflict, timeout, or error), the old slot has already been released — and the booking is now in an invalid state with no slot attached. CWE-362 (race condition) and CWE-841 both apply. The customer ends up with a booking record that points to no slot, or a booking that was moved to a slot that was simultaneously taken by another user. A transaction wrapping both mutations prevents this by rolling back the release if the reservation fails.
Severity rationale
Medium because non-atomic reschedules produce booking records in an invalid intermediate state on any failure during the two-step operation.
Remediation
Wrap the availability recheck, old-slot release, and new-slot reservation in a single transaction in src/lib/booking-service.ts.
await db.$transaction(async (tx) => {
const conflict = await tx.booking.findFirst({
where: { slotId: newSlotId, status: { not: 'CANCELLED' } }
});
if (conflict) throw new ConflictError('New slot unavailable');
await tx.booking.update({
where: { id: bookingId },
data: { slotId: newSlotId, startTime: newStartTime, status: 'CONFIRMED' }
});
// slot capacity adjustments go here too, inside the same transaction
});
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Booking Flow & Lifecycle Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-362 — Concurrent Execution Using Shared Resource with Improper Synchronization
- cwe · CWE-841 — Improper Enforcement of Behavioral Workflow
- iso-25010:2011 · functional-suitability.functional-correctness
Taxons
History
- 2026-04-18·v1.0.0·Initial import from booking-flow-lifecycle·automated