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.
|
||||
@@ -21,6 +21,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever
|
||||
- [windows-recovery-host](entities/windows-recovery-host.md) — Windows Recovery Host (рабочий PC пользователя)
|
||||
|
||||
## Concepts
|
||||
- [sched-runtime-to-file-managed-migration](concepts/sched-runtime-to-file-managed-migration.md) — перевод sched slovo+bookva на file-managed (durable config, #991): почему runtime хрупкий (расписания в памяти демона, сироты без data), рецепт миграции (файл in-place → live-sync НЕ конвертирует runtime → DELETE /tasks+/schedules → verify), gotchas (id=taskName vs UUID, ghost-таски, task-runner config кэш→рестарт, SMTP from==auth, URL-encode)
|
||||
- [sched-ozon-creds-stub-contract](concepts/sched-ozon-creds-stub-contract.md) — контракт кредов Ozon slovo/bookva: мисматч-пары (client_id ↔ чужой ключ) — НАМЕРЕННЫЕ заглушки (анти-утечка после tenant-split), матрица 4 sellers, безопасность через break-on-error (2a38fb5), verify по SHA2, НЕ «чинить»
|
||||
- [omniroute-combo-deepseek-web-tuning](concepts/omniroute-combo-deepseek-web-tuning.md) — настройка web-комбо под агента: system_message убивает скилы pi, tool_filter_regex режет тулы, search-режим ломает tool-calling, 503 chat_admission_busy, эмуляция `<tool>` работает, план кастомного провайдера, qwen-web baxia-WAF стена
|
||||
- [forgetmeai-free-web-chat-proxies](concepts/forgetmeai-free-web-chat-proxies.md) — семейство бесплатных web-chat прокси ForgetMeAI (DeepSeek/Qwen/GLM/Kimi): паттерн «обход капчи через реальную браузерную сессию», PoW-солвер DeepSeek, браузерный путь через baxia-WAF (fetch в page.evaluate), CloakBrowser, классификаторы капчи; карта «что берём в lwp»
|
||||
|
||||
@@ -144,3 +144,5 @@ Append-only log of wiki operations (ingests, promotions, lints, migrations).
|
||||
## [2026-08-22] ingest | NEW concepts/omniroute-combo-deepseek-web-tuning.md + sources/omniroute-session-2026-08-22.md — сессия настройки web-комбо omniroute (deepseek-v4-flash/pro-web). Корни вылетов агента: (1) system_message override режет скилы pi, (2) tool_filter_regex ^(bash|computer)$ режет 337/339 тулов, (3) search-модель генерит DEEP_SEARCH-мусор + «Link reading unavailable», (4) quality validation режет стрим на type:error, (5) 503 chat_admission_busy = capacity веб-чата. Фиксы применены: system_message="", regex расширен (базовые+mcp__*), flash think-search→think, maxRetries 2→4, retryDelayMs→4000. Эмуляция tool-calling проверена тестами — исправна. План кастомного провайдера (вытащить executor+translator, без quality validation, persistSession). index.md updated (+2).
|
||||
## [2026-08-22] ingest | UPDATE concepts/omniroute-combo-deepseek-web-tuning.md — qwen-web стена baxia-WAF (подзадача llm-web-proxy-qwen-web): FAIL_SYS_USER_VALIDATE/RGV587 punish-капча на любой не-браузерный запрос (Node-fetch все варианты заголовков + свежий cookie-jar; даже Chrome-CDP chats/new проходит, completion виснет). Капча привязана к device-фингерпринту (bx-umidtoken из sg-wum.alibaba.com), не к cookie. Зацепка: в контейнере omniroute рабочая версия executors/qwen-web.ts ≠ GitHub main. Подзадача 🟡 paused. index.md hook обновлён.
|
||||
## [2026-08-22] ingest | NEW concepts/forgetmeai-free-web-chat-proxies.md + raw/forgetmeai-repo-analysis-2026-08-22.md — анализ аккаунта ForgetMeAI (8 репо, все — бесплатные web-chat прокси, та же ниша lwp). Главное: (1) паттерн «не решать капчу, а обходить реальной браузерной сессией» (Chrome-расширение-экспортёр кук/localStorage, cookie export, persistent-профиль); (2) PoW-солвер DeepSeek (lib/pow.js, WASM compile-cache) — в lwp уже портирован; (3) qwen через baxia-WAF работает ТОЛЬКО браузерным путём: fetch внутри page.evaluate (FreeQwenApi) — настоящий umidtoken+canvas-фингерпринт; наш вывод подтверждён; (4) CloakBrowser-заметки (патченый Chromium, humanize, persistent-профили, geoip), классификаторы капчи (Aliyun WAF/FRONTEND_CAPTCHA_REQUIRED/punish/purecaptcha). Референс-код закоммичен в lwp docs/reference/forgetmeai/. index.md updated (+1).
|
||||
|
||||
## [2026-08-23] ingest | NEW concepts/sched-runtime-to-file-managed-migration.md — sched slovo+bookva переведены на file-managed (durable config, #991 closed): инцидент 2026-08-23 (спин, runtime-расписания в памяти демона) → миграционный рецепт (файл in-place из git, live-sync ≤60с НЕ конвертирует runtime-таски, DELETE /api/tasks+/schedules, verify fileManaged:true/дубли 0/ручной run), gotchas (id=taskName vs UUID, ghost-таски, task-runner config кэш→рестарт, SMTP from==auth Yandex, URL-encode имён). SMTP-уроки линкуются на snolla-smtp-mail-delivery-2026-08 (не дублируются). index.md updated (+1).
|
||||
|
||||
Reference in New Issue
Block a user