Если сначала прочитать остаток через
SELECT, а потом уменьшить его отдельным запросом, два покупателя могут одновременно увидеть последнюю единицу.В PostgreSQL проверку и списание можно объединить:
UPDATE products
SET stock = stock - 1
WHERE id = 42
AND stock > 0
RETURNING id, stock;
Что получится при одновременных запросах:
• Первый покупатель уменьшит остаток с 1 до 0.
• Второй дождётся освобождения строки, и условие
stock > 0 будет проверено повторно.• Его запрос не обновит строку и ничего не вернёт — товар закончился.
Так работает обычный уровень изоляции PostgreSQL
READ COMMITTED.Добавьте защиту от отрицательных остатков:
ALTER TABLE products
ADD CONSTRAINT stock_nonnegative
CHECK (stock >= 0);
Списание остатка и создание заказа выполняйте в одной транзакции, чтобы при ошибке заказа изменение остатка тоже откатилось.