From 1a478f7583ebe396ad474bb05265c3e00268efa2 Mon Sep 17 00:00:00 2001 From: counterweight Date: Sun, 21 Dec 2025 17:57:42 +0100 Subject: [PATCH] Make copy operation atomic with explicit transaction handling - Wrapped copy operation in try/except with explicit rollback - Added comments explaining atomicity - Ensures all-or-nothing behavior for copying to multiple dates --- backend/routes/availability.py | 57 +++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/backend/routes/availability.py b/backend/routes/availability.py index a0912ce..1d74193 100644 --- a/backend/routes/availability.py +++ b/backend/routes/availability.py @@ -160,34 +160,41 @@ async def copy_availability( detail=f"No availability found for source date {request.source_date}", ) - # Copy to each target date + # Copy to each target date within a single atomic transaction + # All deletes and inserts happen before commit, ensuring atomicity copied_days: list[AvailabilityDay] = [] - for target_date in request.target_dates: - if target_date == request.source_date: - continue # Skip copying to self - - # Delete existing availability for target date - await db.execute( - delete(Availability).where(Availability.date == target_date) - ) - - # Copy slots - target_slots: list[TimeSlot] = [] - for source_slot in source_slots: - new_availability = Availability( - date=target_date, - start_time=source_slot.start_time, - end_time=source_slot.end_time, + try: + for target_date in request.target_dates: + if target_date == request.source_date: + continue # Skip copying to self + + # Delete existing availability for target date + await db.execute( + delete(Availability).where(Availability.date == target_date) ) - db.add(new_availability) - target_slots.append(TimeSlot( - start_time=source_slot.start_time, - end_time=source_slot.end_time, - )) + + # Copy slots + target_slots: list[TimeSlot] = [] + for source_slot in source_slots: + new_availability = Availability( + date=target_date, + start_time=source_slot.start_time, + end_time=source_slot.end_time, + ) + db.add(new_availability) + target_slots.append(TimeSlot( + start_time=source_slot.start_time, + end_time=source_slot.end_time, + )) + + copied_days.append(AvailabilityDay(date=target_date, slots=target_slots)) - copied_days.append(AvailabilityDay(date=target_date, slots=target_slots)) - - await db.commit() + # Commit all changes atomically + await db.commit() + except Exception: + # Rollback on any error to maintain atomicity + await db.rollback() + raise return AvailabilityResponse(days=copied_days)