Контекст: BookingHandler.Cancel уже использовал UpdateStatusIfPending (атомарный UPDATE ... WHERE status='pending'), а Confirm вызывал безусловный UpdateStatus(id, "confirmed"). Это позволяло:
cancelled → confirmed);Суть: Изменение статуса — это переход в конечном автомате, а не «затирание поля». Допустимый переход нужно зашивать в WHERE, а число затронутых строк использовать как признак валидности перехода. Иначе появляются недопустимые состояния и race conditions.
Решение: переиспользовать тот же условный апдейт и для подтверждения:
ok, err := h.bookingRepo.UpdateStatusIfPending(r.Context(), id, "confirmed")
if err != nil { /* 500 */ }
if !ok {
writeError(w, http.StatusConflict, "booking cannot be confirmed in its current state", nil)
return
}
Безусловный BookingRepo.UpdateStatus удалён как мёртвый и опасный код.
Связанные заметки: [[atomic-refresh-token-race-condition]], [[backend-validation]]
Источник: Code review 2026-06-30 (neyrogovnarik), fix в handlers/bookings.go + repository/bookings.go
#golang #backend #state-machine #concurrency #bug