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

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
MarkL
Сообщения: 18
Зарегистрирован: 2017.07.05, 20:37

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

Сообщение MarkL » 2019.10.19, 17:27

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

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

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

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


MarkL
Сообщения: 18
Зарегистрирован: 2017.07.05, 20:37

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

Сообщение MarkL » 2019.10.19, 20:10

Есть два вопроса:
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

skynin
Сообщения: 193
Зарегистрирован: 2017.12.12, 10:09

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

Сообщение skynin » 2019.10.19, 21:04

-- Как решить проблему "Состоянии гонки" в 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

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

MarkL
Сообщения: 18
Зарегистрирован: 2017.07.05, 20:37

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

Сообщение MarkL » 2019.10.20, 11:37

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

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

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

Аватара пользователя
ElisDN
Сообщения: 5428
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

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

Сообщение ElisDN » 2019.10.20, 11:54

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

skynin
Сообщения: 193
Зарегистрирован: 2017.12.12, 10:09

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

Сообщение skynin » 2019.10.21, 11:39

MarkL писал(а):
2019.10.20, 11:37
Пока разбираюсь в этом - есть ли какое-либо временное решение?
Активировать мьютекс - одна строчка кода.
Вернее две, если с use
Неврубающийся не может опознать врубающегося.

Ответить