Представьте: два пользователя одновременно нажимают кнопку «Купить» для последнего товара на складе. Оба процесса параллельно читают остаток из БД (он равен 1), оба видят, что товар есть, и оба успешно проводят списание. В итоге — у вас продано два товара, хотя физически был один.
Чтобы этого избежать, при чтении строки используйте блокировку FOR UPDATE. Она заставит вторую транзакцию ждать, пока первая не завершит свою работу.
-- Транзакция 1 блокирует строку с товаром
BEGIN;
SELECT quantity FROM products WHERE id = 42 FOR UPDATE;
-- (БД возвращает 1, строка заблокирована для других изменений)
UPDATE products SET quantity = 0 WHERE id = 42;
COMMIT; -- Блокировка снимается