chore(tradein/deploy): scraper deploy-decouple phase 0+1 — no recreate on infra-only deploys + 120s graceful stop #2065

Merged
lekss361 merged 1 commit from chore/tradein-scraper-deploy-decouple-p01 into main 2026-06-28 16:35:39 +00:00
Owner

Проблема

tradein-scraper (тот же образ, что backend; python -m app.scheduler_main) пере-создаётся на каждом деплое, где infra или scraper изменены, и SIGKILL'ится через 10с (нет stop_grace_period). Браузерная карточка обогащается 15-27с → бегущий sweep/backfill гибнет на середине. Сегодня прод-прогоны (detail-backfill) умирали через 4-7 мин от деплоев авто-пайплайна каждые ~50 мин.

Это фаза 0+1 плана развязки scraper-деплоя (design-панель). Фундамент; фазы 2-3 (cooperative-drain + resume-cursor) — отдельным PR.

Изменения (только 2 infra-файла, без app-кода)

Phase 0 — .forgejo/workflows/deploy-tradein.yml: убран член || needs.changes.outputs.infra == 'true' из SCRAPER_CHANGED. Теперь scraper пере-создаётся только при реальных scraper-путях (scraper paths-filter уже покрывает app/services/scrapers/**, scrape_pipeline.py, scheduler.py, scheduler_main.py, app/tasks/**) или ручном workflow_dispatch. Generic infra-правки (compose/workflow/deploy/**) больше не трогают scraper.

Phase 1 — tradein-mvp/docker-compose.prod.yml (scraper service): добавлены stop_grace_period: 120s + stop_signal: SIGTERM. Когда scraper всё-таки пере-создаётся, Docker ждёт до 120с вместо SIGKILL на 10с — окно убийства расширено с 10с до 120с (mid-card kill уходит).

Честный caveat

До фазы 2 (cooperative SIGTERM-drain в scheduler_main) планировщик ещё не трапит SIGTERM — поэтому рекреация может прервать карточку на границе 120с. Но (а) infra-деплои больше вообще не рекреируют scraper, (б) окно с 10с→120с. Полная безвредность рекреаций — в фазе 2-3.

Test plan

  • CI зелёный (YAML валиден — проверено yaml.safe_load локально).
  • После merge+deploy: infra-only trade-in правка прогоняет деплой, но tradein-scraper не пере-создаётся (uptime сохраняется) — проверить docker inspect tradein-scraper --format {{.State.StartedAt}} до/после.
  • Реальная scraper-code правка ИЛИ workflow_dispatch — scraper пере-создаётся как раньше.

Refs: design-панель scraper-extraction (монорепа, не дробим; фундамент развязки деплоя).

## Проблема `tradein-scraper` (тот же образ, что backend; `python -m app.scheduler_main`) пере-создаётся на каждом деплое, где `infra` или `scraper` изменены, и **SIGKILL'ится через 10с** (нет `stop_grace_period`). Браузерная карточка обогащается 15-27с → бегущий sweep/backfill гибнет на середине. Сегодня прод-прогоны (detail-backfill) умирали через 4-7 мин от деплоев авто-пайплайна каждые ~50 мин. Это **фаза 0+1** плана развязки scraper-деплоя (design-панель). Фундамент; фазы 2-3 (cooperative-drain + resume-cursor) — отдельным PR. ## Изменения (только 2 infra-файла, без app-кода) **Phase 0 — `.forgejo/workflows/deploy-tradein.yml`:** убран член `|| needs.changes.outputs.infra == 'true'` из `SCRAPER_CHANGED`. Теперь scraper пере-создаётся только при **реальных scraper-путях** (`scraper` paths-filter уже покрывает `app/services/scrapers/**`, `scrape_pipeline.py`, `scheduler.py`, `scheduler_main.py`, `app/tasks/**`) или ручном `workflow_dispatch`. Generic infra-правки (compose/workflow/`deploy/**`) больше **не** трогают scraper. **Phase 1 — `tradein-mvp/docker-compose.prod.yml` (scraper service):** добавлены `stop_grace_period: 120s` + `stop_signal: SIGTERM`. Когда scraper всё-таки пере-создаётся, Docker ждёт до 120с вместо SIGKILL на 10с — окно убийства расширено с 10с до 120с (mid-card kill уходит). ## Честный caveat До фазы 2 (cooperative SIGTERM-drain в `scheduler_main`) планировщик ещё **не** трапит SIGTERM — поэтому рекреация может прервать карточку на границе 120с. Но (а) infra-деплои больше вообще не рекреируют scraper, (б) окно с 10с→120с. Полная безвредность рекреаций — в фазе 2-3. ## Test plan - [ ] CI зелёный (YAML валиден — проверено `yaml.safe_load` локально). - [ ] После merge+deploy: infra-only trade-in правка прогоняет деплой, но `tradein-scraper` **не** пере-создаётся (uptime сохраняется) — проверить `docker inspect tradein-scraper --format {{.State.StartedAt}}` до/после. - [ ] Реальная scraper-code правка ИЛИ `workflow_dispatch` — scraper пере-создаётся как раньше. Refs: design-панель scraper-extraction (монорепа, не дробим; фундамент развязки деплоя).
lekss361 added 1 commit 2026-06-28 16:32:12 +00:00
chore(tradein/deploy): don't recreate scraper on infra-only deploys + 120s graceful stop (phase 0+1)
All checks were successful
CI / changes (pull_request) Successful in 9s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
8aaa42f101
The tradein-scraper container runs the same image as backend
(python -m app.scheduler_main, in-app scheduler) and owns the in-flight
scrape sweeps (a browser card takes ~15-27s). Two problems compounded to
kill running jobs on every deploy:

Phase 0 (deploy-tradein.yml): SCRAPER_CHANGED also fired on the `infra`
paths-filter (compose / workflow / deploy/**), so any generic infra edit
recreated the scraper container and SIGKILLed the running job. Dropped the
`|| infra == 'true'` term — only real scraper-code paths (already covered
by the `scraper` paths-filter) or a manual workflow_dispatch recreate it.

Phase 1 (docker-compose.prod.yml): the scraper had no stop_grace_period,
so Docker's default 10s window SIGKILLed an in-flight card (15-27s) before
it could finish. Added stop_grace_period: 120s + explicit stop_signal:
SIGTERM so a running unit can finish + checkpoint. The cooperative-drain
handler that consumes SIGTERM lands in a later phase; this is the
foundation.
lekss361 merged commit 77b4f7e64c into main 2026-06-28 16:35:39 +00:00
lekss361 deleted branch chore/tradein-scraper-deploy-decouple-p01 2026-06-28 16:35:39 +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#2065
No description provided.