Проблема: При отправке множества запросов одновременно на метод API - создания заказа возникает некорректная работа: создается заказов больше, чем сумма на балансе. Фактически проблема связана с "Состоянием гонки".
Основные действия метода:
1. Валидация данных, в том числе баланса.
2. Выполнение коммерческого действия.
3. Создание заказа.
4. Списание баланса.
Обернуто в транзакцию.
Пример: у пользователя баланс 2200 поинтов. Шлем запросы, и на момент когда баланс становится нулевым, сумма заказов превышает 3400 поинтов.
Вопросы:
1. Как решить проблему "Состоянии гонки" в API Yii2?
2. Разве "блокировка сессий PHP" не служит для предотвращения такого поведения? Или же это нарушение принципа REST об "Отсутствии состояния" и поэтому она не фигурирует в API модуле?
Как решить проблему состояния гонки в API Yii2?
Re: Как решить проблему состояния гонки в API Yii2?
Есть два вопроса:
1. Правильно ли я понимаю, что в моих реалиях нужно:настроить optimisticLock, в частности поле version для таблицы сущности User(именно там поле баланса) и все?
2. Не понятен момент документации со вставкой версии строки в frontend:
Код: Выделить всё
echo Html::activeHiddenInput($model, 'version');
Транзакции использую, после заполнения сущностней они оборачиваются методом 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?
-- Как решить проблему "Состоянии гонки" в 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
А когда стартанут, то там уже будет другой баланс, если предыдущая транзакция его изменила.
И опять же, залочит одна, остальные опять будут ждать.
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?
Благодарю. Пока разбираюсь в этом - есть ли какое-либо временное решение? Например, Rate Limiter какой-то установить с допустимым запросом раз в 2-3 секунду? Думаю, это даст немного времени чтобы разобраться и ввести Мютексы.skynin писал(а): ↑2019.10.19, 21:04 -- Как решить проблему "Состоянии гонки" в API Yii2?
https://ru.wikipedia.org/wiki/Мьютекс
в Yii2 смотреть в сторону:
yii\mutex\Mutex
Re: Как решить проблему состояния гонки в API Yii2?
Либо оборачиваем всю пачку запросов в транзакцию в режиме полной блокировки, либо в простой транзакции используем оптимистическую блокировку от момента получения баланса до его сохранения, либо используем мьютексы.
Re: Как решить проблему состояния гонки в API Yii2?
Активировать мьютекс - одна строчка кода.
Вернее две, если с use
Не желайте странного, и не будет у вас головной боли чтобы достичь этого странного.
Тем более что окажется что оно вам и не нужно было, странное это.
Тем более что окажется что оно вам и не нужно было, странное это.