Страница 1 из 1

Как решить проблему состояния гонки в API Yii2?

Добавлено: 2019.10.19, 17:27
MarkL
Проблема: При отправке множества запросов одновременно на метод API - создания заказа возникает некорректная работа: создается заказов больше, чем сумма на балансе. Фактически проблема связана с "Состоянием гонки".

Основные действия метода:
1. Валидация данных, в том числе баланса.
2. Выполнение коммерческого действия.
3. Создание заказа.
4. Списание баланса.
Обернуто в транзакцию.

Пример: у пользователя баланс 2200 поинтов. Шлем запросы, и на момент когда баланс становится нулевым, сумма заказов превышает 3400 поинтов.

Вопросы:
1. Как решить проблему "Состоянии гонки" в API Yii2?
2. Разве "блокировка сессий PHP" не служит для предотвращения такого поведения? Или же это нарушение принципа REST об "Отсутствии состояния" и поэтому она не фигурирует в API модуле?

Re: Как решить проблему состояния гонки в API Yii2?

Добавлено: 2019.10.19, 17:58
ElisDN

Re: Как решить проблему состояния гонки в API Yii2?

Добавлено: 2019.10.19, 20:10
MarkL
Есть два вопроса:
1. Правильно ли я понимаю, что в моих реалиях нужно:настроить optimisticLock, в частности поле version для таблицы сущности User(именно там поле баланса) и все?
2. Не понятен момент документации со вставкой версии строки в frontend:

Код: Выделить всё

echo Html::activeHiddenInput($model, 'version');
Зачем это делать? Для валидации? (У меня API и в качестве формы используется наследник Model).
Транзакции использую, после заполнения сущностней они оборачиваются методом execute(): https://pastebin.com/FbNKHMuE

Код: Выделить всё

$this->transactionManager->execute(function () use ($user, $order) {
	$this->userRepository->save($user);
	$this->orderRepository->add($order);
});
Благодарю за ответ.

P.S. Схема работы постановки заказа: https://i.imgur.com/iwTqDRv.jpg

Re: Как решить проблему состояния гонки в API Yii2?

Добавлено: 2019.10.19, 21:04
skynin
-- Как решить проблему "Состоянии гонки" в API Yii2?

https://ru.wikipedia.org/wiki/Мьютекс

в Yii2 смотреть в сторону:
yii\mutex\Mutex

Можно и залочить изменяемые данные

Я использую свое расширение для MySQL/MariaDB (аналогично можно дописать для остальных БД)
надо бы обновить, это первая реализация: https://github.com/skynin/mysql-activequery

Итого:
-- у пользователя баланс 2200 поинтов
на момент старта транзакции:
0. устанавливаем мьютекс, даем ему имя, например $user_id, или лочим данные из п1
1. Валидация данных, в том числе баланса
2. Выполнение коммерческого действия.
3. Создание заказа.
4. Списание баланса.

Остальные транзакции будут ждать снятия мьютекса или commit/rollback

А когда стартанут, то там уже будет другой баланс, если предыдущая транзакция его изменила.
И опять же, залочит одна, остальные опять будут ждать.

Re: Как решить проблему состояния гонки в API Yii2?

Добавлено: 2019.10.20, 11:37
MarkL
skynin писал(а): 2019.10.19, 21:04 -- Как решить проблему "Состоянии гонки" в API Yii2?

https://ru.wikipedia.org/wiki/Мьютекс

в Yii2 смотреть в сторону:
yii\mutex\Mutex
Благодарю. Пока разбираюсь в этом - есть ли какое-либо временное решение? Например, Rate Limiter какой-то установить с допустимым запросом раз в 2-3 секунду? Думаю, это даст немного времени чтобы разобраться и ввести Мютексы.

Re: Как решить проблему состояния гонки в API Yii2?

Добавлено: 2019.10.20, 11:54
ElisDN
Либо оборачиваем всю пачку запросов в транзакцию в режиме полной блокировки, либо в простой транзакции используем оптимистическую блокировку от момента получения баланса до его сохранения, либо используем мьютексы.

Re: Как решить проблему состояния гонки в API Yii2?

Добавлено: 2019.10.21, 11:39
skynin
MarkL писал(а): 2019.10.20, 11:37 Пока разбираюсь в этом - есть ли какое-либо временное решение?
Активировать мьютекс - одна строчка кода.
Вернее две, если с use