# План: наблюдаемость (сбои источников, здоровье, логи) Источник: `docs/analysis-2026-09-21.md` (наблюдения 2 и 5, риск «сбои RIPE/DNS не видны»), пункт 2 рекомендуемого порядка. **Метрики Prometheus (`/metrics`) в этот план не входят** (решение пользователя); всё, что описано ниже, доступно через `/health`, `/sources`, лог и базу. ## Проблема Недоступный RIPEstat или DNS пишется только в лог: задание считается успешным, `/health` показывает `ok`, а данные источника не обновляются (до истечения TTL - 90 дней). Сбой одного из шести ASN невидим, пока адреса не начнут пропадать. В логе после каждого запуска стоит «data saved» даже без изменений, а итог запуска по источникам не сводится. ## Дизайн ### 1. Состояние источников (SQLite, схема версии 3) - Таблица `source_status(kind, source, last_attempt, last_success, failures, error_kind)`, ключ `(kind, source)`. Хранится в базе, а не в `status.json`: переживает перезапуск демона, пишется в той же транзакции, что и данные, попадает в резервные копии. Миграция 2 -> 3 автоматическая (как 1 -> 2), данные не затрагиваются. - Сборщик записывает результат по каждому источнику при каждом запуске: успех - `last_success = last_attempt = сейчас`, `failures = 0`, `error_kind = NULL`; сбой - `last_attempt = сейчас`, `failures + 1`, `error_kind`; `last_success` не меняется. - `error_kind` - короткая категория без деталей: `timeout`, `connection`, `http_429`, `http_4xx`, `http_5xx`, `invalid_response` (нечитаемый ответ), `dns`, `no_global_addresses` (имя разрешилось, но все адреса отфильтрованы), `unknown`. Полный текст ошибки остаётся в логе; наружу он не отдаётся (как и пути в находке 6 ревью). - Пустой, но корректный ответ RIPEstat считается успехом (число адресов видно в `/sources`). - Строки состояния источников, которых уже нет в конфигурации, удаляются в очередном запуске сбора и при `purge`. ### 2. `/health` - В ответ добавляется блок `sources`: по типам (`asn`, `fqdn`) число источников (`total`) и список **только неисправных** (`failing`): `source`, `failures`, `last_success`, `error_kind`. Компактно и без лишних деталей. - Источник считается неисправным при `failures >= source_failure_threshold` (ключ в `config.json`, по умолчанию 3): один сбой RIPEstat не «краснит» систему. При наличии неисправных источников `status` = `degraded`. - Существующие поля и поведение (`collector_alive`, `jobs`, `db_recreated`, `last_restore`) не меняются. ### 3. `GET /sources` (чтение открыто, как `/asns`) Полная картина по всем источникам: `kind`, `source`, `addresses` (сколько значений в базе), `last_attempt`, `last_success`, `failures`, `error_kind`. Позволяет увидеть источник, который «успешно» вернул ноль адресов или давно не обновлялся. Существующие `/asns` и `/fqdns` не меняются. ### 4. Логи - Итог запуска одной строкой: «ASN collection finished: 6 sources, 5 ok, 1 failed, +12/-3 prefixes» (для FQDN так же). - Сообщение «... data saved to ...» после каждой транзакции убирается (наблюдение 5 анализа); подробности по источникам остаются на уровне INFO. ## Изменения 1. `db.py`: схема v3 и миграция, `record_source_result`, `source_statuses`, очистка состояния (`sweep`, `purge`). 2. `cidr_collector.py`: классификация ошибок (`error_kind`), запись результата по каждому источнику в обоих `run_collection`, итоговая строка лога, ключ `source_failure_threshold`. 3. `api_server.py`: блок `sources` и статус в `/health`, `GET /sources`. 4. `README.md`: `/health`, `/sources`, ключ конфигурации, лог, схема базы. 5. Тесты (2 новых, всего 30): сборщик (успех и сбой источников записываются, категории ошибок, сброс `failures` при успехе, очистка удалённого источника); API (`/health` `ok` до порога и `degraded` после, ответ без текста ошибок, `/sources` с числом адресов). ## Не входит - Метрики Prometheus и `/metrics` (исключены по решению пользователя). - Уведомления (webhook, Telegram) и алерты: `/health` и `/sources` пригодны для внешних проверок, сами уведомления - отдельная доработка. - Свежесть резервных копий как отдельный признак здоровья, команда CLI `status`, ограничение частоты `POST /collect`. - Проверка `docker healthcheck` не меняется: `degraded` по-прежнему HTTP 200, контейнер из-за внешнего сбоя перезапускать нечего. ## Проверка Тесты в контейнере. Вручную: миграция базы версии 2 (созданной кодом до изменения) с сохранением данных; локальный «RIPEstat» (как при проверке повторов) отвечает 503: после трёх запусков `/health` показывает `degraded` и источник в `failing`, после восстановления сервера `ok`; сравнение ответов существующих эндпоинтов до и после; Docker Compose (демон и API, ручной сбор). ## Риски и откат - Схема базы v3: таблица добавляется, существующие таблицы не меняются; откат кода оставляет лишнюю таблицу без последствий (старый код её не читает, `user_version` 3 у старого кода вызовет только пропуск миграции). - Порог 3 на редких расписаниях (сбор FQDN раз в сутки) означает три дня до `degraded`; порог настраивается.