docs(wiki): sched runtime→file-managed migration concept (#991) + handoff update
This commit is contained in:
78
.wiki/concepts/sched-runtime-to-file-managed-migration.md
Normal file
78
.wiki/concepts/sched-runtime-to-file-managed-migration.md
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: sched-runtime-to-file-managed-migration
|
||||
type: concept
|
||||
status: live
|
||||
tags: [books, sched, file-managed, durable-config, incident, live-sync]
|
||||
related: "[[sched-ozon-creds-stub-contract]], [[snolla-smtp-mail-delivery-2026-08]], [[portainer-stack-management-books-vds]]"
|
||||
updated: 2026-08-23
|
||||
---
|
||||
|
||||
# Sched runtime → file-managed миграция (durable config)
|
||||
|
||||
## TL;DR
|
||||
|
||||
После инцидента 2026-08-23 (спин `ozonFbsPostingsSyncronization`, ~75 req/сек) оба тенанта books
|
||||
(slovo + bookva) переведены на **file-managed** расписания sched: декларатив в
|
||||
`deploy/sched-tasks.json` (git-tracked), демон подхватывает файл live-sync'ом. Runtime-путь
|
||||
(таски в сторе, `fileManaged:false`) хрупкий — расписания живут в памяти демона и слетают
|
||||
при pause/перезапуске, таски остаются сиротами без `data`.
|
||||
|
||||
**Ключевой факт миграции: live-sync НЕ конвертирует runtime-таски** (sync создаёт только
|
||||
новые из файла, существующие runtime НЕ перезаписывает). Перевод = явный `DELETE` runtime-
|
||||
тасок и расписаний через admin API, после чего sync доставляет file-managed версии.
|
||||
|
||||
## Почему runtime хрупкий (контекст инцидента)
|
||||
|
||||
- Расписания runtime-тасок держатся **в памяти демона** → при `pause`/`restart` слетают.
|
||||
- Таски остаются сиротами: `fileManaged:false`, `schedule:null`, `data:undefined`.
|
||||
- `deploy/sched-tasks.json` был **gitignored** (прод-файл на хосте) — конфиг не версионировался,
|
||||
восстановление = ручные миграции из `agendaJobs`.
|
||||
- Runtime-таска могла нести **чужой URL runner'а** (cross-tenant leak: `books-task-runner` в
|
||||
bookva-конфиге) — файл чинит (bookva → `bookva-task-runner`).
|
||||
|
||||
## Миграционный рецепт (по тенанту)
|
||||
|
||||
1. **Файл** — из git-репо в прод-расположение:
|
||||
- slovo: `/opt/books/sched/tasks.json` (bind-директория `/opt/books/sched:/app/config:ro`).
|
||||
- bookva: volume `/var/lib/docker/volumes/bookva-sched-config/_data/tasks.json` (→ `/app/config`).
|
||||
- Заливка **in-place**: `cp /tmp/new /path/tasks.json` (не mv — сохранить inode/perms).
|
||||
Бэкап: `cp tasks.json tasks.json.bak-<tag>`.
|
||||
- Перед заливкой: diff против live — допускаются только целевые изменения (books собирает
|
||||
файл байт-идентичным live + новые таски).
|
||||
2. **Live-sync** — демон подхватывает файл ≤60с, рестарт НЕ нужен. Верифицировать через
|
||||
`GET /api/tasks` (новые таски появляются как `fileManaged:true`).
|
||||
3. **DELETE runtime** (sync их не убирает — только API, runs-история сохраняется):
|
||||
- `DELETE /api/tasks/:name` ×N — имена URL-encode (`%20`).
|
||||
- `DELETE /api/schedules/:id` ×N — **не каскадятся**, удалять отдельно. Все → 204.
|
||||
4. **Verify**:
|
||||
- `fileManaged:true` у всех целевых, `disabled:false`, `paused:false`.
|
||||
- Дублей 0: `[].taskName | group_by(.) | map(select(length>1))` пуст.
|
||||
- `data` на расписании (не на таске): slovo `{idSeller:2}` / bookva `{idSeller:1}`,
|
||||
отчёт — `{recipients:[...], idSeller}`.
|
||||
- Ручной раунд: `POST /api/tasks/:name/run` → `succeeded {sellersProcessed:N}`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
| Гоча | Деталь |
|
||||
|---|---|
|
||||
| id-паттерн | file-managed schedule `id` = taskName; runtime = UUID |
|
||||
| ghost-таски | `fileManaged:true` сироты (выпилены из файла) висят `disabled:true` — удалять `DELETE /api/tasks/:name` |
|
||||
| task-runner config | `/opt/books/task-runner/config/default.json` кэшируется при первом send (lazy init `sendReportsByEmail.js`) → после правки **рестарт обязателен** |
|
||||
| SMTP | `from` == auth-user обязателен (Yandex: 550 not owned / 525 disabled). Креды `e-16513832@yandex.ru` (app-pass, `pass show snolla-smtp/full-env`). См. [[snolla-smtp-mail-delivery-2026-08]] |
|
||||
| имена тасок | «ozon fbs postings syncronization» — пробелы, в DELETE URL-encode |
|
||||
| шаблон vs прод | конвертер из шаблона может давать лишние trigger-only таски — в деплой-файл не включать без причины |
|
||||
|
||||
## Текущее состояние (2026-08-23, #991 closed)
|
||||
|
||||
- slovo: 25 тасок, file-managed, отчёт `{idSeller:2, recipients:[3]}` (recipients восстановлены,
|
||||
commit `854e381`).
|
||||
- bookva: 23 таски, file-managed, `data {idSeller:1}` (commit `6d4f163` bookva-overlay).
|
||||
- Ghost `system-cleanup-task-runs` удалён на обоих тенантах.
|
||||
- Доступ: sched admin API — books-sched `172.20.0.20:3031`, bookva-sched `172.20.0.12:3031`,
|
||||
Bearer `SCHED_ADMIN_KEY` (env контейнера), через SSH books-vds. Роуты — `packages/admin-api/src/admin-api.ts` (репо sched).
|
||||
|
||||
## Related
|
||||
|
||||
- [[sched-ozon-creds-stub-contract]] — почему мисматч-пары кредов намеренные (тот же инцидент).
|
||||
- [[snolla-smtp-mail-delivery-2026-08]] — SMTP-уроки (525/554/550), перенос на e-16513832@yandex.ru.
|
||||
- [[portainer-stack-management-books-vds]] — управление стеками books VDS.
|
||||
Reference in New Issue
Block a user