[2026-10-15 09:52:43] taiga-vault: family/documents/vault-sync/2026-10-15-http402-balance-outage-9d.md

This commit is contained in:
Taiga committed 2026-10-15 09:52:43 +00:00
1 parent 43676802ab
commit 26271498d9
1 file changed
+101
@@ -0,0 +1,101 @@
---
tags: [vault-sync, incident, cron, llm-provider]
date: 2026-10-15
severity: high
status: resolved
---
# Инцидент: vault-sync стоял 8,93 суток — HTTP 402 Insufficient Balance (2026-10-06 → 2026-10-15)
## Итог одной строкой
Задача `sync-vault` (cron `*/5`, id `29851c413903`) **не выполнялась 8 дней 22,4 часа**:
2572 тика подряд упали с `RuntimeError: HTTP 402: Insufficient Balance` (провайдер
DeepSeek). Баланс пополнен ~2026-10-15 09:50 UTC — синк восстановился сам на очередном
тике, бэклог влит fast-forward, потерь данных нет.
## Хронология (UTC)
| Время | Событие |
|---|---|
| 2026-10-06 11:25:44 | последний успешный тик (в логе `[syncthing] Pushed ✓` 11:25:30) |
| 2026-10-06 ~11:30 | первый 402; за сутки 10-06 — 150 отказов, 138 успехов |
| 10-07 … 10-14 | ровно **288 отказов в сутки** = 100 % тиков, ни одного запуска скрипта |
| 10-15 00:00 … 09:45 | 118 отказов (все слоты до 09:45) |
| 2026-10-15 09:50:21 | тик **completed**, скрипт отработал все три фазы, бэклог влит |
| 2026-10-15 09:51 | `failure_streak` сброшен, /vault, bare, /obsidian-syncthing на одном SHA `43676802` |
Длительность: 10-06 11:25:44 → 10-15 09:50:21 = **12 864,9 мин = 8,93 суток**.
Число подряд упавших тиков: **2572** (= `failure_streak` в `/opt/data/cron/jobs.json`).
## Диагностика (что и где смотреть)
1. **Разрыв в `/tmp/vault-sync.log`**: 10-06 11:25:30 → 10-15 09:50:21. Лог **не**
обнулялся (первая строка 2026-09-15 11:35:19) — значит контейнер не пересоздавался,
это отказ LLM-петли, а не потеря лога.
2. **`/opt/data/cron/jobs.json`** — самое быстрое доказательство:
`"failure_streak": 2572`, `"last_status": "error"`,
`"last_error": "RuntimeError: HTTP 402: Insufficient Balance (request_id: …)"`.
`failure_streak × 5 мин` = длительность простоя.
3. **`/opt/data/cron/executions.db`** — окно только за последние 1001 запись (≈3,5 суток):
в нём 1000 × `failed` с тем же текстом 402 и одна `running` (текущий тик).
Данные старее обрезаны, поэтому длительность по БД **не** измеряется.
4. **`/opt/data/cron/usage_audit.jsonl`** — полная хронология без обрезки (12 231 строка,
с 2026-09-02): `error` есть → тик не запускал скрипт; `error: null` → запускал.
Разбивка 402 по суткам: 10-06 — 150, 10-07…10-14 — по 288, 10-15 — 118.
## Причина
Задача — **prompt-задача** (`"prompt": "bash /opt/data/sync-vault.sh >> …"`), её исполняет
LLM-раунд. Нет баланса у провайдера → нет LLM-раунда → скрипт не вызывается вообще
(не «git упал», а «скрипт не стартовал»). Git-инфраструктура и bare-репозиторий при этом
были полностью живы: Eagle пушил в bare всё это время (12 коммитов 10-07, 18 — 10-08,
14 — 10-09, …, 42 — 10-14, 49 — 10-15).
## Что не потеряно
- bare всё это время принимал коммиты Eagle напрямую — история целая.
- Телефон (Syncthing) держал свои правки локально и отдал их сегодня коммитом
`57061535` (`personal/documents/*`, `personal/shopping-order.md`, `Расходы руб.md`).
- Первый удачный тик 10-15 09:50 влил бэклог: **178 коммитов** с 10-06 11:00,
`Merge … origin/main` + fast-forward, без конфликтов.
- Проверка: три SHA равны (`43676802…`) в `/vault`, `/vault.git`, `/obsidian-syncthing`;
`git status --porcelain` и `git ls-files -u` пусты в обоих клонах; новых
`*.sync-conflict-*` / `*.bak-*` за период нет.
## Корневая проблема: стоимость prompt-тика
Один тик этой «пустой» задачи съедает десятки-сотни тысяч prompt-токенов, потому что
агент собирает весь системный промпт и навыки на каждый запуск:
- замер по `usage_audit.jsonl` за последние полные сутки (10-06): **138 успешных тиков,
18 503 331 токен суммарно**, максимум **378 455 токенов за один тик**;
- при полной каденции 288 тиков/сутки это ≈ **38 млн токенов в сутки** ради
`git add/commit/merge/push`, который сам по себе стоит 0 токенов.
Отсюда и опустошение баланса: 402-класс уже наблюдался 09-14, 09-21, 09-25, 09-28 —
это не разовый сбой, а режим, в котором счёт провайдера выедается задачей-синхронизатором.
## Рекомендации (решение владельца)
1. **Убрать LLM из петли** — основной фикс:
`hermes cron edit 29851c413903 --no-agent --script <path>`.
Ловушка: `--script` ждёт файл под `~/.hermes/scripts/`, `/root/.hermes` в контейнере
не переживает рекреацию — нужен персистентный путь (симлинк на `/opt/data/`).
Тогда тик не зависит ни от баланса, ни от доступности провайдера.
2. Как минимум — зафиксировать модель/провайдера подешевле и урезать системный промпт
для этой задачи (`enabled_toolsets`, `skills: []` уже пусто, но промпт всё равно
собирается целиком).
3. Мониторинг: алерт при `failure_streak > 12` (час простоя) в `jobs.json` —
иначе отказ виден только по дырке в логе, а он «локальный» (`deliver: local`),
Алексею ничего не приходит.
4. `deliver: local` для этой задачи — правильно (не спамить), но именно поэтому
9-дневный отказ прошёл незамеченным. Стоит сделать отдельный health-check-тик,
который при простое шлёт одно сообщение в Telegram.
## Оценка класса
Предыдущие эпизоды 402: 09-21 — 6 тиков (35 мин), 09-25 — 53, 09-28 — 14.
Текущий — **2572 тика / 8,93 суток**, на два порядка больше. Разница не в механизме,
а в том, что баланс не пополняли: провайдер отвечает 402 мгновенно (1,7–7,6 с на тик),
поэтому самовосстановления нет по определению — нужен внешний сигнал.