Здравствуйте. Писал сайт и возникла интересная проблема. Я случайно одновременно отправил 2 одинаковых fetch на PHP сервер, которые должны завершать услугу и возвращать деньги на баланс, и они 2 раза завершили одну услугу и 2 раза вернули деньги. Как я понимаю, всё это произошло из-за того, что между получением данных из базы и их валидацией прошло какое то время, и оба запроса получили данные о том что услуга не завершена.
Я довольно долго думал, что можно сделать, но так ни к чему и не пришел.
Как можно исправить данную проблему?
UPD: Решил проблему вот так:
UPDATE `table` SET `status` = 'CANCEL' WHERE `id` = '{$this->id}' AND NOT (SELECT `status` FROM `table` WHERE `id` = '{$this->id}') = 'CANCEL';
Сергей Васильев, что непонятного? непонятно как написать в браузере три слова, mysql блокировка записи, вот прям серьёзно? Интернетом пользоваться в школе не учили?
или как правильно написал Adamos в ответе ниже, "проверку на завершение услуги" делать в самом запросе.
тогда второй запрос проверку не пройдет и списания не будет.
как вариант, блокировать post запросы отправляемые чаще 1сек. на уровне http сервера, блокировать submit перед отправкой данных в js или создавать cookie co сроком жизни 1 сек. и проверять перед отправкой sql запроса в PHP
FanatPHP, Да, куки это конечно гениально, можно с таким же успехом просто писать "не делайте пожалуйста одновременные запросы". В любом случае, проблему уже решил, пока что снова ошибка не возникла
FanatPHP, смотря как реализовать - можно вначале сделать редирект на ту же страницу, можно использовать файловую сессию вместо cookie - суть одна и та же - блокировка повторяющихся/частых запросов.
Антон Шаманов, господи, ну нельзя так тупить
не бывает никаких "файловых сессий". Сессии без кук не работают. чтобы стартовать сессию, надо идентифицировать клиента. для этого служат куки.
При этому удалить куки клиенту вообще ничего не стоит. то есть любые защиты на куках строить бессмысленно.
это вообще, в целом
если же говорить про конкретную проблему, изложенную в посте, то куки там тем более не при чём - запросы приходят одновременно
то есть никакая кука разумеется не успеет вернуться к клиенту чтобы быть посланной со следующим запросом - второй запрос УЖЕ ушёл.
id может передаваться get параметром и id задается при в первом запросе, а отправка данных явно не первый
может, но проблемы остаются те же:
клиент передает идентификатор только если захочет. и может менять его по своему усмотрению. и если захочет, то просто заново авторизуется с другой сессией и пошлет два разных запроса с двумя разными идентификаторами сессии
какие защиты, тебе же сказано нужно блокировать повторную отправку данных
это сказано в заголовке, а читать надо весь вопрос целиком.
и если уж на то пошло, то "заблокировать повторную (читай - параллельную) отправку данных" в принципе невозможно, по причинам, которые я описывал выше.
я воспринимаю это как 2 js запроса к php/http серверу. там не сказано "2 sql запроса к mysql"
Там всё сказано. Надо просто читать весь вопрос целиком.
А он про два одновременные НТТР запроса, которые порождают SQL запрос, который меняет состояние системы.
В вопросе описано банальнейшее race condition.
И он совсем не про блокировку кнопочки.
FanatPHP, да хрен там, как по твоему он это сделал? отправил 2я параллельными процессами? не думаю, скорее всего просто 2 запроса вызванных с минимальным промежутком.
вопрос не в том "как он там делал", а в том, как защититься от этой проблемы, которая вполне реальна на реальных проектах
это трудно понять тем кто всю жизнь говнякал сайты вордпрессе и не видит дальше собственного носа
но всё же надо стараться понять
а не выставлять себя полным ничтожеством этими нелепыми отмазками "он не так запускал"
'запросы в базу данных' в принципе не должны позволять делать двойную трату, причем все финансовые транзакции должны подразумевать что их могут дублировать, подделывать, отменять в процессе выполнения, включая проблемы с железом (например переполненный диск) правильное тестирование финансового сервиса должно пережить не только издевательство клиентов, но и проблемы с самим сервером.
на любой чих нужны проверки статусов, а не делаете ли вы сейчас эту же операцию (уже ответили, в условие обновления ставишь проверку статуса, и все параллельные запросы отвалятся на этом моменте)