fix(ci): прод-деплои в одну группу concurrency — прун одного убивал pull другого (#2950) #2952
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#2952
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/2950-deploy-docker-lock"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Закрывает #2950.
Что случилось
20.08 деплой ПТИЦЫ (run 8083, коммит
6bca4f7e) упал за 5 секунд:Причина — по секундам
docker compose pull— 7 образовPullingInterrupted,unable to lease contentDeleted Images:— отработалdocker image prune -afОба деплоя ходят по SSH в один докер-демон. Проверено
docker psна VM: стекиgendesign-*,tradein-*и самforgejo-runnerживут на одном хосте.Группы concurrency были разные —
deploy-prodиdeploy-tradein-prod— Forgejo их параллелил.cancel-in-progress: falseне помогал: он сериализует раны внутри группы, а гонка была между группами.Почему отказ был тихим
Голова main показывала
success. Но зелёным был Trade-In'овый деплой этой головы: последний коммит трогал толькоtradein-mvp/**, поэтому у него запустился толькоDeploy Trade-In. Деплой ПТИЦЫ висел на своём, более раннем коммите — с исходомfailure. Прод остался на старом коде, #2946 и #2947 не доехали (выкачены отдельнымworkflow_dispatch).Почему общая группа, а не flock на хосте
Сначала сделал host-lock: он сериализует только докер-секцию, не заставляя деплой ждать чужих билдов (~6 мин). Потом проверил его на самой VM и отказался:
Дочерний процесс наследует fd лока; при аварийной смерти job'а (ENOSPC, OOM, отмена) лок остаётся занятым и блокирует прод-деплои на весь таймаут. Планировщик Forgejo сериализует без лок-файла — залипать нечему. Плачу ожиданием ~6 мин, покупаю отсутствие целого класса отказов.
Гейт и его проверка
backend/tests/ops/test_2950_deploy_concurrency_group.py— 5 тестов. Мутационно проверен, что умеет краснеть:cancel-in-progress: trueconcurrencyцеликомОтдельно — фильтр changes
Гейт стережёт
.forgejo/workflows/**, а фильтрbackendих не включал. Правка, разводящая группы обратно, не трогаетbackend/**→backend-testsпропускается → гейт не исполняется ровно на той правке, от которой стережёт. Добавил оба deploy-workflow'а в фильтр. Тот же класс, что #2587 (тамauth/roles.yamlлежал внеbackend/**, и правка ролей уехала в main без прогона).20.08 деплой ПТИЦЫ (run 8083) упал за 5 секунд: unable to lease content: lease does not exist: not found Все 7 образов ушли в Interrupted через 0.8с после старта пула. Причина — не в ПТИЦЕ: через 0.4с после обрыва соседний деплой Trade-In напечатал «Deleted Images:» своего `docker image prune -af`. Оба деплоя ходят по SSH в ОДИН докер-демон: стеки gendesign-* и tradein-* плюс сам forgejo-runner живут на одной VM (проверено `docker ps` на хосте). Группы были разные — deploy-prod и deploy-tradein-prod — поэтому Forgejo запускал их параллельно. `cancel-in-progress: false` не спасал: он про раны ВНУТРИ группы, а гонка была МЕЖДУ группами. Последствие было тихим: голова main показывала success, но зелёным был Trade-In'овый деплой этой головы, а деплой ПТИЦЫ висел на своём, более раннем коммите с исходом failure. Прод остался на старом коде — #2946 и #2947 не доехали и были выкачены отдельным workflow_dispatch. Почему общая группа, а не flock на хосте. Host-lock сериализовал бы только докер-секцию, не заставляя деплои ждать чужих билдов (~6 мин). Но у него своя отказная мода: дочерний процесс наследует fd лока, и при аварийной смерти job'а лок залипает. Проверено на самой VM: после kill -9 держателя следующий претендент лок НЕ получил. Залипший лок блокирует прод-деплои на весь таймаут. Планировщик Forgejo сериализует без лок-файла, залипать там нечему — платим ожиданием, покупаем отсутствие целого класса отказов. Гейт backend/tests/ops/test_2950_deploy_concurrency_group.py фиксирует инвариант. Мутационно проверен: разводка групп обратно, cancel-in-progress: true и удаление секции concurrency краснят его (1, 1 и 3 упавших теста), контроль зелёный. В фильтр changes добавлены сами deploy-workflow'ы — иначе правка, разводящая группы, не трогает 'backend/**', backend-tests пропускается, и гейт не исполняется ровно на той правке, от которой стережёт. Тот же класс, что #2587. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>prune -afубиваетcompose pullдеплоя ПТИЦЫ (разные группы concurrency, один докер-демон) #2950prune -afубиваетcompose pullдеплоя ПТИЦЫ (разные группы concurrency, один докер-демон) #2950