> Как снять бронь товара при отмене заказа (Go)
Уровень: senior · Роль: backend · Категория: Технические вопросы
Компании: sferaplatform.ru
Стек: Go
> Пример ответа
При отмене заказа снятие брони товара - это критическая операция, которая должна быть атомарной и устойчивой к сбоям. В Go с типичным стеком (PostgreSQL, Redis) я бы реализовал это через транзакцию в базе данных.
Пример на Go с использованием database/sql:
GOfunc CancelOrder(ctx context.Context, orderID int64) error {tx, err := db.BeginTx(ctx, nil)if err != nil {return fmt.Errorf("begin tx: %w", err)}defer tx.Rollback() // откат в случае ошибки// 1. Получаем товары заказа с блокировкой строкrows, err := tx.QueryContext(ctx, `SELECT product_id, quantityFROM order_itemsWHERE order_id = $1 FOR UPDATE`, orderID)if err != nil {return fmt.Errorf("select items: %w", err)}defer rows.Close()var items []struct {ProductID int64Quantity int}for rows.Next() {var item struct {ProductID int64Quantity int}if err := rows.Scan(&item.ProductID, &item.Quantity); err != nil {return fmt.Errorf("scan item: %w", err)}items = append(items, item)}// 2. Возвращаем товары на склад (снимаем бронь)for _, item := range items {_, err := tx.ExecContext(ctx, `UPDATE productsSET reserved = reserved - $1WHERE id = $2 AND reserved >= $1`, item.Quantity, item.ProductID)if err != nil {return fmt.Errorf("unreserve product %d: %w", item.ProductID, err)}}// 3. Меняем статус заказа_, err = tx.ExecContext(ctx, `UPDATE ordersSET status = 'cancelled', updated_at = NOW()WHERE id = $1 AND status = 'active'`, orderID)if err != nil {return fmt.Errorf("update order: %w", err)}// 4. Фиксируем транзакциюif err := tx.Commit(); err != nil {return fmt.Errorf("commit tx: %w", err)}return nil}
Ключевые моменты:
-
Используем
FOR UPDATEдля блокировки строкorder_items, чтобы избежать гонок. -
Проверка
reserved >= $1предотвращает уход в минус при повторных отменах. -
Вся логика в одной транзакции - если что-то пошло не так, изменения откатываются.
-
Если используется Redis для кэша брони, после коммита БД нужно инвалидировать кэш (например, через паттерн
cache-aside).
Этот подход гарантирует консистентность данных даже при параллельных запросах.
> Похожие задачи по backend
Напиши функцию на Go, которая принимает слайс каналов int и возвращает канал с суммами чисел из каждого канала
Как гарантировать, что заказ не будет выполнен без исполнителя после резерва товара
Как обрабатывать ошибки при отмене брони на складе
Как обнаруживать и реагировать на проблемы с отменой брони
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью