В CI два параллельных теста используют один платёжный стенд и конфликтуют. Какой механизм изоляции запуска следует применить?
Следует применить управление конкурентным доступом к ресурсам: объявить платёжный стенд ограниченным ресурсом и разрешить одновременно выполнять только столько тестов, сколько экземпляров стенда безопасно поддерживают. Если доступен один общий стенд, CI должен сериализировать зависимые тесты или распределять их по выделенным экземплярам.
Это устраняет конфликт на уровне планирования, а не маскирует его повторами или увеличением таймаутов.
Параллельный запуск появился как способ сократить время обратной связи: независимые задания выполняются одновременно на нескольких агентах. Однако часть тестов использует внешние ресурсы с ограниченной ёмкостью — тестовые стенды, устройства, лицензии, очереди или аккаунты.
Когда планировщик учитывает только сами тесты, но не занятые ими ресурсы, параллелизм превращается в источник ложных падений. Поэтому в CI-практиках появились ресурсные группы, семафоры и ограничения конкурентности.
Два теста могут одновременно изменить состояние одного платёжного стенда, использовать один тестовый аккаунт или отправить конкурирующие запросы в одну очередь. Каждый тест по отдельности будет проходить, но совместный запуск вызовет взаимное влияние: один тест увидит данные другого, получит отказ занятого ресурса или нарушит ожидаемый порядок операций.
Повторный запуск не устраняет первопричину. Он лишь иногда меняет расписание и создаёт видимость стабильности, а слишком сильное ограничение всех тестов снижает преимущество параллельного выполнения.
Сначала нужно явно описать ресурс, который нельзя безопасно делить. Для каждого теста или группы тестов указывают требуемый ресурс и количество его экземпляров, после чего планировщик CI выдаёт доступ через семафор или lock.
Если экземпляр стенда один, одновременно запускается только один тест, использующий этот стенд. Независимые тесты, работающие с другими ресурсами, продолжают выполняться параллельно. Если стендов несколько, каждому запуску можно выдать отдельный экземпляр через пул ресурсов.
Важно, чтобы блокировка охватывала весь критический участок: получение стенда, выполнение действий и освобождение ресурса. При аварийном завершении агент должен освобождать аренду автоматически; иначе зависший lock будет блокировать последующие сборки.
Ресурсная блокировка не заменяет изоляцию данных. Даже при последовательном запуске нужны очистка состояния, уникальные идентификаторы и корректное завершение теста. Также следует измерять время ожидания ресурса: если оно велико, проблема может быть не в CI, а в недостаточном количестве стендов или слишком широком наборе тестов, требующих эксклюзивного доступа.
Компромисс заключается в выборе между скоростью и изоляцией. Грубая блокировка всей тестовой группы проста, но уменьшает параллелизм; более точное распределение ресурсов сложнее, зато позволяет одновременно выполнять независимые проверки.
Команда запускала четыре интеграционных теста параллельно на одном платёжном стенде. Каждый тест создавал заказ для общего тестового клиента, поэтому иногда проверка статуса одного заказа находила данные другого.
Рассматривались три варианта. Повторы скрывали часть падений, но делали результат недостоверным. Полная последовательность всех интеграционных тестов устраняла конфликт, но увеличивала время сборки. Создание отдельного стенда для каждого теста давало лучшую изоляцию, однако было слишком дорого и сложно для поддержки.
Выбрали ресурсную группу с одним слотом для тестов, использующих общий стенд, и оставили остальные тесты параллельными. Дополнительно тесты получили уникальные идентификаторы заказов и обязательную очистку данных. Ложные падения исчезли, а общее время CI сократилось по сравнению с полной сериализацией.
1. Достаточно ли просто пометить конфликтующие тесты и запускать их последовательно?
Нет, если планировщик не учитывает эту пометку при распределении заданий. Ограничение должно действовать на уровне общего ресурса, включая разные job или разные агенты CI; локальная блокировка внутри одного процесса не защитит от конкурирующего запуска на другом агенте.
2. Почему блокировка не решает проблему общего состояния стенда?
Она предотвращает одновременный доступ, но не очищает результаты предыдущего теста. После освобождения ресурса следующий тест может получить изменённые настройки, сохранённые платежи или незавершённую операцию. Поэтому блокировку нужно сочетать с подготовкой, проверкой готовности и очисткой стенда.
3. Когда лучше масштабировать ресурсы, а не усиливать блокировки?
Масштабирование оправдано, если ресурс является постоянным узким местом CI и его экземпляры можно создавать с приемлемой стоимостью и одинаковым состоянием. Если стенд дорог, нестабилен или не поддерживает независимое клонирование, разумнее оставить ограниченную конкуренцию и перенести часть проверок на более изолированный уровень, например на сервисные или компонентные тесты.