Это свойство изоляции транзакции.Об этом много написано, и я очень рекомендую обзор в Проектирование приложений с интенсивным использованием данных .Мне показалось, что это самое полезное описание для улучшения моего личного понимания.
Уровень postgres по умолчанию - READ COMMITTED , который позволяет каждой из этих параллельных транзакций видеть нечто похожее (состояние доступных средств)даже если они должны быть зависимыми.
Один из способов решения этой проблемы - пометить каждую из этих транзакций как последовательность "SERIALIZABLE".
SERIALIZABLE Все операторытекущей транзакции можно увидеть только строки, зафиксированные до того, как в этой транзакции был выполнен первый запрос или оператор изменения данных.Если шаблон чтения и записи среди параллельных сериализуемых транзакций создаст ситуацию, которая не могла бы возникнуть при каком-либо последовательном (по одному) выполнении этих транзакций, одна из них будет откатываться с ошибкой serialization_failure.
Это должно обеспечить правильность вашего приложения за счет доступности, т. Е. В этом случае вторая транзакция не сможет изменить записи и будет отклонена, что потребует повторной попытки.Для POC-приложений или приложений с низким трафиком это обычно первый шаг, так как вы можете убедиться в его правильности прямо сейчас.
Также в упомянутой выше книге я думаю, что был пример того, как банкомат обрабатывает доступность.Они допускают это состояние гонки и пользователь могут сделать переплату, если они не могут подключиться к централизованному банку, но ограничивают максимальный вывод средств, чтобы минимизировать радиус взрыва!
Другой архитектурный способ решения этой проблемы заключается в использованиитранзакции отключаются и делают их асинхронными, так что каждая вызванная пользователем транзакция публикуется в очередь, а затем, имея одного потребителя в очереди, вы естественным образом избегаете любых условий гонки.Компромисс здесь аналогичен, есть фиксированная пропускная способность, доступная от одного работника, но это помогает решить проблему правильности прямо сейчас: P
Блокировка между машинами (например, использование redis через postgres / grpc) называется распределенной блокировкой и о ней написано хорошее количество https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html