Как воспроизвести и устранить гонку при изменении данных

Контролируемый параллельный сценарий, проверка инварианта и выбор защиты от потерянного обновления.

Практическое решение · 3 мин чтения
Как воспроизвести и устранить гонку при изменении данных — tarasovcode
РазработкаКак воспроизвести и устранить гонку при изменении данных

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

Что подготовить

  • Минимальный обработчик чтения и записи, границы транзакции и используемый уровень изоляции.
  • Бизнес-инвариант: например, остаток не становится отрицательным, а каждое списание учитывается ровно один раз.
  • Контролируемая среда с возможностью поставить барьер между чтением и обновлением.

Порядок работы

  1. Создайте исходную запись с известным остатком и два независимых соединения. Не имитируйте параллельность последовательными вызовами.
  2. Остановите оба обработчика после чтения исходного значения. Затем разрешите им выполнить запись и сохраните последовательность событий.
  3. Определите тип нарушения: потерянное обновление, повторное действие или проверка условия вне защищенного участка.
  4. Выберите защиту по инварианту: атомарное условное обновление, контроль версии либо блокировку в транзакции. Учитывайте необходимость повторов.
  5. Повторите управляемый сценарий и серию конкурентных запусков. Добавьте тест конфликтов и убедитесь, что отказ одному участнику обрабатывается явно.

Разобранный пример

На складе осталось пять единиц. Два клиента просят по четыре. Условное обновление уменьшает остаток только при достаточном количестве: одна операция меняет пять на один, вторая не меняет строку. Вторая получает понятный конфликт, а не подтверждение заказа без товара.

Как проверить результат

  • Итоговый остаток соответствует сумме подтвержденных операций.
  • Конфликт возвращает ожидаемый результат, а не маскируется как успешная запись.
  • Отмена, таймаут и повтор клиента не приводят к дополнительному списанию.

Если что-то пошло не так

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

Промпт для разбора ваших материалов

По коду и инвариантам построй расписание двух конкурентных операций, которое вызывает ошибку. Предложи минимальную защиту и тест с управляемыми барьерами. Объясни поведение при конфликте, таймауте и повторе; не предлагай произвольные задержки.

Частые вопросы

Нужна ли глобальная блокировка?

Обычно сначала рассматривают защиту конкретной записи или операции. Глобальная блокировка может ненужно ограничить независимые запросы.

Достаточно ли тысячи успешных запусков?

Нет. Управляемое воспроизведение опасного порядка дает более сильное подтверждение, а серия запусков дополняет его.

Материал библиотеки решений Ильи Тарасова / TarasovWeb.

Как построить управляемое чередование

Пусть два работника одновременно пытаются занять последнее свободное место. Оба читают значение «одно место», оба принимают решение о резервировании, после чего сохраняют результат. Обычный последовательный тест этого не покажет: второй вызов увидит уже обновлённое состояние. Нужно отдельно управлять моментом между чтением и записью.

На тестовом стенде поставьте барьер после чтения: дождитесь, пока оба вызова достигнут его, и только затем разрешите продолжение. Запишите порядок событий и идентификаторы операций. Критерий проверки — не число успешных HTTP-ответов, а инвариант: одно место не может принадлежать двум разным бронированиям.

После выбора механизма защиты повторите тот же порядок. Возможные стратегии включают атомарную условную запись, блокировку или проверку версии; их применимость зависит от хранилища и границ транзакции. При конфликте определите понятный ответ проигравшему вызову. Молчаливый повтор операции может быть неверным, если исходные условия уже изменились.

Добавьте ещё два сценария: один работник прерывается до фиксации, а второй получает повторный запрос от клиента. Они проверяют разные риски и не заменяют друг друга. Уберите тестовые барьеры из обычного режима, но сохраните управляемый сценарий в наборе проверок. Повторение случайной нагрузки полезно дополнительно, однако оно хуже объясняет причину, чем фиксированное чередование действий.

О проекте

Практическая библиотека Ильи Тарасова и TarasovWeb. Telegram-канал · Личная страница · ВКонтакте.

#tarasovweb #ИльяТарасов