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