Как правильно разрешить Race Condition ?

capscom

Новичок
Возникла проблема при одновременной загрузке нескольких фотографий пользователем. Каждая загружаемая фотография сохраняется в неблокирующую сессию (сами сессии хранятся в Redis).
Валидация и сохранение выглядит так:

Код:
if ($service->getCountUploaded() <= 6){
   $service->upload($request->file('file'));
}
И если пользователь за раз выбирает 10 фоток, то все 10 процессов одновременно получают: $service->getCountUploaded() == 0 и попадут в условие добавления. А хотелось бы, чтобы каждый процесс получал корректное число уже загруженных фото.

Подскажите, пожалуйста, как правильно разрешить такую ситуацию ?
 
Последнее редактирование:

fixxxer

К.О.
Партнер клуба
1) загружать фотки по одной, блокироваться и ждать (так себе идея)
2) держать в том же редисе счетчик обрабатываемых фоток
 

capscom

Новичок
1) загружать фотки по одной, блокироваться и ждать (так себе идея)
2) держать в том же редисе счетчик обрабатываемых фоток
А что может дать счетчик, ведь в единицу времени все процессы получат одинаковое его значение. Разве нет?
 

Semen

Семён
на клиенте разрешать пользователю загружать определённое кол-во фотографий, и конечно проверять ещё на сервере
 

fixxxer

К.О.
Партнер клуба
А что может дать счетчик, ведь в единицу времени все процессы получат одинаковое его значение. Разве нет?
Для упрощения опустим вопрос с SMP, как ты себе представляешь на одном процессоре две операции одновременно?

А далее, насчет SMP - тоже пофигу, у нас один redis, работающий на одном ядре. С shared memory вот было бы немного сложнее.
 

capscom

Новичок
Для упрощения опустим вопрос с SMP, как ты себе представляешь на одном процессоре две операции одновременно?

А далее, насчет SMP - тоже пофигу, у нас один redis, работающий на одном ядре. С shared memory вот было бы немного сложнее.
Как раз мало себе представляю. Что касается кода, столкнулся вот с этой проблемой - с одинаковым значением счетчика при параллельных запросах. Думал есть классическое решение, но не нагуглил.

Решил поинтересоваться, как на своих рабочих проектах коллеги решают подобную ситуацию.
 

fixxxer

К.О.
Партнер клуба
Как раз мало себе представляю.
Тогда просто заведи счетчик, как я сказал.
Например, так - до начала обработки фотографии делаешь в редисе INCR counter, если сумма возвращенного значения и количество уже имеющихся фоток превышает лимит - сразу DECR counter и возвращаем, иначе DECR counter после обработки, в блоке finally.
У этого способа есть два недостатка:
1) в случае превышения лимита INCR и DECR неатомарны, и может получиться лишний отлуп в другой операции, если там успели еще что-то удалить - но это очень маловероятно;
2) если что-то свалится посреди обработки с fatal error, декремента не произойдет и счетчик будет неконсистентен: имеет смысл на всякий случай дополнительно устанавливать счетчику expire в 3-5 секунд (вряд ли обработка занимает больше пары секунд).

А если хочешь представлять - вспомни про то, как работают многозадачные системы, про переключение контекста, подумай, как внутренне устроен redis...
 

Redjik

Джедай-мастер
Так мьютекс это же семафор, получишь загрузку фоток по одной.
 

Redjik

Джедай-мастер
Я бы всё же ограничил клиентом. Генерить 6 хешей, отправлять на клиент, загрузку делать только при наличии валидного хеша в заголовке.
 

fixxxer

К.О.
Партнер клуба
Я бы всё же ограничил клиентом. Генерить 6 хешей, отправлять на клиент, загрузку делать только при наличии валидного хеша в заголовке.
Либо это не будет работать, если открыть два окна браузера, либо я не понял, что ты предлагаешь.
 

fixxxer

К.О.
Партнер клуба
fixxxer , спасибо. Так понял, это нечто близкое к Mutex.
Вроде есть даже библиотека для эмуляции: https://github.com/arvenil/ninja-mutex

https://github.com/arvenil/ninja-mutex
Не совсем, мы же не хотим, чтобы фотки грузились по одной. Скорее тут просто используем атомарность операций инкремента/декремента.
 

Redjik

Джедай-мастер
Ну можно по извращаться, тогда будет и в разных окнах работать, но твой вариант мне всё равно больше нравится.
 
Сверху