fix(ci): докер-секции прод-деплоев исключают друг друга через host-lock (#2950) #2955

Merged
bot-backend merged 1 commit from fix/2950-host-lock into main 2026-08-20 08:13:56 +00:00
Collaborator

Поправка к #2952 по тому же issue #2950.

Что я утверждал и что оказалось

В #2952 я свёл обе группы concurrency к одной и написал, что это сериализует деплои. Не сериализует.

Коммит 2a01dea1 трогает оба деплой-workflow, поэтому запустил обе цепочки. При работающей общей группе вторая не стартовала бы вовсе. На раннере в этот момент:

TASK-21431_WORKFLOW-Deploy-Trade-In_JOB-build-frontend
TASK-21430_WORKFLOW-Deploy-Trade-In_JOB-test
TASK-21428_WORKFLOW-Deploy_JOB-build-frontend
TASK-21427_WORKFLOW-Deploy_JOB-build-worker
TASK-21426_WORKFLOW-Deploy_JOB-build-backend

Обе идут бок о бок. Forgejo 10.0.3+gitea-1.22.0, runner v6.3.1 — workflow-level concurrency здесь не исполняется.

Следствие шире, чем моя ошибка: прежние deploy-prod / deploy-tradein-prod тоже никогда ничего не делали, как и cancel-in-progress: false. Причиной гонки было не различие имён групп, а отсутствие взаимного исключения как такового.

flock я отверг по неверному основанию

В #2952 я написал, что host-lock не годится: «при аварийной смерти job'а лок залипает». Перепроверил на настоящем сценарии — обрыв ssh-сессии, а не kill -9 родителя в отрыве:

/run/lock/gendesign-docker-deploy.lock:
   gendesign  1968950 F.... bash
   gendesign  1968953 F.... sleep

Лок действительно держит живой потомок скрипта. Но ведь и докер-команды после обрыва сессии продолжают работать на хосте — отпустить лок в этот момент было бы как раз неправильно. Это не дефект, а нужная семантика: исключение действует ровно пока жив тот, кто мутирует докер. «Залипание» требует по-настоящему зависшего процесса, а ожидание ограничено flock -w с сообщением, где написано, чем посмотреть держателя.

Правка

Обе докер-секции берут общий лок /var/lock/gendesign-docker-deploy.lock перед работой. Второй ssh-шаг ПТИЦЫ (перезагрузка прокси) лок не берёт — он образов не тянет, пруну там нечего портить.

Секция concurrency оставлена: заработает при обновлении Forgejo. Но гейт больше не выдаёт её за действующий механизм — проверки разделены на обязательные (лок) и декларативные (группа), и в шапке файла написано, почему.

Мутационная проверка гейта

мутация результат
убрать flock из tradein-деплоя 2 failed
развести пути локов 2 failed
убрать сообщение об исчерпании ожидания 1 failed
развести группы concurrency 1 failed
контроль 7 passed, rc=0

Критерий приёмки (не закрываю #2950 до него)

Следующий раз, когда мерж в backend/** и мерж в tradein-mvp/** попадут в одно окно: оба деплоя завершаются success, ни одного unable to lease content в логах, и в логе второго видна строка ожидания лока — то есть он реально ждал первого, а не разошёлся с ним случайно.

Поправка к #2952 по тому же issue #2950. ## Что я утверждал и что оказалось В #2952 я свёл обе группы `concurrency` к одной и написал, что это сериализует деплои. **Не сериализует.** Коммит `2a01dea1` трогает оба деплой-workflow, поэтому запустил обе цепочки. При работающей общей группе вторая не стартовала бы вовсе. На раннере в этот момент: ``` TASK-21431_WORKFLOW-Deploy-Trade-In_JOB-build-frontend TASK-21430_WORKFLOW-Deploy-Trade-In_JOB-test TASK-21428_WORKFLOW-Deploy_JOB-build-frontend TASK-21427_WORKFLOW-Deploy_JOB-build-worker TASK-21426_WORKFLOW-Deploy_JOB-build-backend ``` Обе идут бок о бок. Forgejo `10.0.3+gitea-1.22.0`, runner `v6.3.1` — workflow-level `concurrency` здесь не исполняется. Следствие шире, чем моя ошибка: прежние `deploy-prod` / `deploy-tradein-prod` **тоже никогда ничего не делали**, как и `cancel-in-progress: false`. Причиной гонки было не различие имён групп, а отсутствие взаимного исключения как такового. ## flock я отверг по неверному основанию В #2952 я написал, что host-lock не годится: «при аварийной смерти job'а лок залипает». Перепроверил на настоящем сценарии — обрыв ssh-сессии, а не `kill -9` родителя в отрыве: ``` /run/lock/gendesign-docker-deploy.lock: gendesign 1968950 F.... bash gendesign 1968953 F.... sleep ``` Лок действительно держит живой потомок скрипта. Но ведь и докер-команды после обрыва сессии **продолжают работать на хосте** — отпустить лок в этот момент было бы как раз неправильно. Это не дефект, а нужная семантика: исключение действует ровно пока жив тот, кто мутирует докер. «Залипание» требует по-настоящему зависшего процесса, а ожидание ограничено `flock -w` с сообщением, где написано, чем посмотреть держателя. ## Правка Обе докер-секции берут общий лок `/var/lock/gendesign-docker-deploy.lock` перед работой. Второй ssh-шаг ПТИЦЫ (перезагрузка прокси) лок не берёт — он образов не тянет, пруну там нечего портить. Секция `concurrency` оставлена: заработает при обновлении Forgejo. Но гейт больше не выдаёт её за действующий механизм — проверки разделены на **обязательные** (лок) и **декларативные** (группа), и в шапке файла написано, почему. ## Мутационная проверка гейта | мутация | результат | |---|---| | убрать `flock` из tradein-деплоя | 2 failed | | развести пути локов | 2 failed | | убрать сообщение об исчерпании ожидания | 1 failed | | развести группы concurrency | 1 failed | | контроль | 7 passed, rc=0 | ## Критерий приёмки (не закрываю #2950 до него) Следующий раз, когда мерж в `backend/**` и мерж в `tradein-mvp/**` попадут в одно окно: оба деплоя завершаются `success`, ни одного `unable to lease content` в логах, и в логе второго видна строка ожидания лока — то есть он реально ждал первого, а не разошёлся с ним случайно.
bot-backend added 1 commit 2026-08-20 07:54:00 +00:00
fix(ci): докер-секции прод-деплоев исключают друг друга через host-lock (#2950)
All checks were successful
CI / changes (pull_request) Successful in 10s
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m1s
CI / backend-tests (pull_request) Successful in 17m17s
34fbac205e
Поправка к #2952. Там я свёл обе группы concurrency к одной и написал, что это
сериализует деплои. Проверил сразу после мержа — не сериализует.

Коммит 2a01dea1 трогает оба деплой-workflow, поэтому запустил обе цепочки. При
работающей общей группе вторая не стартовала бы вовсе. На раннере в этот момент:

  TASK-21431_WORKFLOW-Deploy-Trade-In_JOB-build-frontend
  TASK-21430_WORKFLOW-Deploy-Trade-In_JOB-test
  TASK-21428_WORKFLOW-Deploy_JOB-build-frontend
  TASK-21427_WORKFLOW-Deploy_JOB-build-worker
  TASK-21426_WORKFLOW-Deploy_JOB-build-backend

Обе идут бок о бок. Forgejo 10.0.3 (gitea-1.22), runner v6.3.1 — workflow-level
concurrency здесь не исполняется. Значит и прежние deploy-prod /
deploy-tradein-prod никогда ничего не делали: причиной гонки было не различие
имён групп, а отсутствие взаимного исключения как такового.

Отдельно — flock я в #2952 отверг по неверному основанию. Я написал, что при
аварийной смерти job'а лок залипает. Перепроверил на настоящем сценарии (обрыв
ssh-сессии, а не kill -9 родителя в отрыве): лок держит живой потомок скрипта —

  /run/lock/gendesign-docker-deploy.lock:
     gendesign  1968950 F.... bash
     gendesign  1968953 F.... sleep

но ведь и докер-команды после обрыва сессии продолжают работать на хосте, так
что отпускать лок в этот момент как раз НЕЛЬЗЯ. Это не дефект, а нужная
семантика: исключение действует ровно пока жив тот, кто мутирует докер.

Правка: обе докер-секции берут общий лок на хосте перед работой. Ожидание
ограничено 900с и падает с сообщением, где написано, чем посмотреть держателя.
Второй ssh-шаг (перезагрузка прокси) лок не берёт — он образов не тянет, пруну
там нечего портить.

Секция concurrency оставлена: заработает при обновлении Forgejo. Но гейт теперь
не выдаёт её за действующий механизм — проверки разделены на обязательные (лок)
и декларативные (группа), и в шапке написано, почему.

Гейт мутационно проверен: убрать flock → 2 failed, развести пути локов →
2 failed, убрать сообщение о таймауте → 1 failed, развести группы → 1 failed,
контроль → 7 passed rc=0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bot-backend merged commit feff8214f7 into main 2026-08-20 08:13:56 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2955
No description provided.