[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:
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 с на тик),
|
||||
поэтому самовосстановления нет по определению — нужен внешний сигнал.
|
||||
Reference in new issue
Block a user