Files
admin/.wiki/concepts/sched-runtime-to-file-managed-migration.md

5.8 KiB
Raw Blame History

title, type, status, tags, related, updated
title type status tags related updated
sched-runtime-to-file-managed-migration concept live
books
sched
file-managed
durable-config
incident
live-sync
sched-ozon-creds-stub-contract, snolla-smtp-mail-delivery-2026-08, portainer-stack-management-books-vds 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/runsucceeded {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).