Files
EvoBGP/docs/backend-improvement-plan.md
T
DenozordecandCursor 240d83fc61 docs(docs): describe durable jobs, telemetry, and bundle limits
Зафиксированы очередь job, семантика метрик, лимиты бандла и исходящих запросов, а также чеклист продакшена для пула соединений.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-02 17:24:18 +07:00

86 KiB
Raw Blame History

EvoBGP · План развития backend

Дата: 26 октября 2026 · Основа анализа: checkout 924f111
Фокус: достоверная статистика, надёжное применение BGP-конфигураций, устойчивые фоновые задачи, экономное использование VPS.

1. Главный вывод

EvoBGP уже имеет содержательное ядро: сбор префиксов из AS/CDN/доменов/ручных списков, материализованные снимки, агрегацию CIDR, ревизии, подписанные бандлы, управление BIRD, удалённые агенты, PostgreSQL-мониторинг и обслуживание БД. Основная задача следующей итерации — укрепить гарантии между этими частями и сделать наблюдаемое состояние достоверным.

Рекомендуемый порядок: исправление статусов и выполнения задач → единый сбор телеметрии → снижение фоновой нагрузки → новые операторские функции.

Обязательное условие: обновление control plane не должно требовать одновременного обновления удалённых спикеров. Уже развёрнутые старые агенты/CLI должны продолжать получать, проверять и применять совместимые бандлы. Новые возможности включаются по capabilities конкретной ноды; ограничения наблюдаемости показываются явно. Политика перехода и проверки — B08.

Приоритет Значение Правило исполнения
P0 Неверное подтверждение сетевого изменения, небезопасное применение Выполнить до расширения автоматического deploy
P1 Потеря/повтор задач, недостоверная статистика, ошибки изоляции и защиты Ближайший инженерный этап
P2 Производительность, эксплуатация, полезные функции После стабилизации зависимостей
P3 Расширенные возможности По подтверждённой потребности

Границы исследования

Первоначальный анализ исходников, миграций, Compose и CI выполнен тремя субагентами Astra. Реализация ведётся субагентами Sol / medium, проверка — Astra; фактический прогресс отмечен ниже. Production, реальные RSS/CPU/IO, размер опубликованных образов и нагрузка операторов не измерялись. Docker CLI в локальной среде отсутствует. Поэтому проценты экономии и гарантированная вместимость VPS здесь не заявляются.

Найденные при анализе устаревшие описания очереди и Svelte UI в docs/architecture.md и docs/overview.md исправлены на этапе 0. План учитывает фактическую реализацию и подключённый ReUI PRO.

2. Что уже есть и что следует сохранить

Статус реализации · 26.10.2026

Этап 0 «Проверяемая основа» и первая очередь runtime-исправлений. Реализация: Sol / medium; независимая проверка: Astra. Старый формат запроса node/sync и формат бандла сохраняются. Полный этап 0 остаётся открытым до стендовых проверок ниже.

  • Добавлен CI job postgres-integration: временная PostgreSQL 16, последовательное применение миграций и repository tests с -race; проверяется успех каждого из 23 обязательных тестов, skip/отсутствие теста не даёт зелёный результат.
  • Добавлены тесты повторного применения миграций, конкурентного SQL claim, сохранения строки job при повторном открытии pool и idempotency row; исправлен невалидный tenant fixture в существующем CRUD-тесте.
  • Добавлены воспроизведения двух дефектов live-status: stale Established и ложный healthy без poll. После исправления S01 tag backend_repro удалён; проверки включены в обязательный набор.
  • Зафиксирован локальный baseline совместимости: исторические исходники v1.0.0, минимальные node/sync/health wire contracts, независимая проверка подписи/хешей и замороженный signed bundle текущего builder. Подробности: совместимость.
  • Добавлен read-only PowerShell collector ресурcных snapshots с whitelist полей и offline проверками multi-container stats, отсутствия утечки env и неполного результата: инструкция.
  • Уточнены architecture/overview: гибридная Registry + PostgreSQL очередь с ограничениями, реальный tenant-refresh endpoint и текущий React/ReUI UI.
  • Исправлен локальный Go lint runner: проверяет пакеты текущего модуля вместо посторонних вложенных worktree и возвращает ошибки vet/linter.
  • Подтвердить новый integration job реальным запуском CI/PostgreSQL. Локальный запуск без DSN компилирует тесты, но DB-проверки пропускаются; это не интеграционный успех.
  • Воспроизвести взаимодействие локального Registry и DB claim при ожидании дольше grace, повторном idempotency key и настоящем restart worker/jobs API. SQL-level тесты выше не доказывают эти гарантии.
  • Инвентаризировать реально установленные версии/digests speakers и прогнать матрицу на старых binaries/images. Source-derived контракт и текущая fixture не заменяют эту матрицу.
  • Снять effective Compose и реальные CPU/RAM/IO, выполнить workload/24h soak на целевом VPS; локально Docker отсутствует.

Результаты локальной валидации и ограничения среды приведены в backend-baseline.md.

Первая очередь runtime: B01 / B02 / S01 / B08. Отметки ниже относятся к реализованным частям, а не к закрытию пунктов целиком.

  • B01/B08: bare HTTP 2xx означает доставку без подтверждения (unconfirmed); applied обновляется только после явного ACK нужной опубликованной ревизии либо успешного локального apply. Формат запроса старому агенту сохранён.
  • B01: результат job содержит результаты по speakers и счётчики published/applied/unconfirmed/failed; ошибки доставки больше не превращаются в общий успешный apply.
  • S01: неопрошенное состояние и отсутствующие счётчики возвращаются как unknown/null; сохранённый Established не подменяет текущее наблюдение. Добавлены quality/source/timestamps и проверки назначенного speaker.
  • B02: реализованы атомарное переключение bird.conf внутри mounted ActiveDir, межпроцессная блокировка, проверяемые поколения и rollback с отдельным context. Astra проверила исправления inline/commented include bypass; переносимые тесты прошли.
  • B01: отмена останавливает публикацию следующим speakers, игнорирует поздний ACK и сохраняет частичный результат со статусом cancelled; добавлены регрессии отмены dispatch и legacy empty 204.
  • B02: выполнить Linux fault/process-lock тесты и реальный bird -p CI; проверить configure/recovery с настоящим daemon, ENOSPC и аварийное завершение между фазами. Cross-compilation и fake birdc не закрывают эти проверки.
  • S01/B01 UI: качество и время наблюдения, last-known подсказки, unknown вместо ложного down/0, результаты неподтверждённого deploy. Исправлены знаменатель deployment, disabled peers и явный unhealthy; Astra проверила. Typecheck/lint/build — exit 0, Vitest — 9/9. Визуальный browser review ещё не выполнен; полный F01 этим не закрыт.
  • B01: добавлены persistent progress по speakers и атомарные lease/generation проверки публикации/ACK; /jobs/{job_id}/report читает сохранённые результаты после перезапуска, включая связанные recovery jobs. Проверены восстановление отчёта, приоритет собственного прогресса recovery и tenant isolation.
  • B01: bounded reconciliation сохраняет порядок исходных намерений, дедуплицирует recovery и ограничивает его тремя раундами. Health observation token отклоняет запоздалые результаты; timeout/отсутствующее поле не восстанавливает устаревший статус. Memory tests порядка/retry budget прошли; SQL-регрессии добавлены в CI.
  • B01: выполнить PostgreSQL restart/fencing/health gate и стенд legacy agent. Двухминутная reservation после неоднозначного ответа 2.56.2 ограничивает повторы, но не доказывает остановку удалённой операции; exactly-once и состояние BIRD не заявляются.
  • B08 executables: обязательная legacy-версия 2.56.2; её pinned source-built CLI прошёл pull/auth/signature против текущего CP, агент serve — текущий dispatch/ACK/health/protocols с подписанным bundle и fake BIRD. Добавлена CI-матрица 2.56.2 и дополнительного CLI 1.0.0; реальный BIRD этим не проверен.
  • B08: реальные image digests 2.56.2, agent/BIRD apply/rollback и смешанный парк по-прежнему требуют стендовой проверки; source-built CLI не закрывает всю матрицу.
  • B05 transport/proxy: CDN URL проверяется при dial/redirect, DNS-ответ проверяется целиком, соединение выполняется с проверенным IP; CDN proxy отключён. Приватный node transport отделён. Forwarded-заголовки доверены только EVOBGP_TRUSTED_PROXY_CIDRS; auth limiter ограничен 4096 активными IP без вытеснения действующих счётчиков. Astra проверила; полный Go test и lint прошли.
  • B05 CDN body: ingest/preview отклоняют распакованный ответ больше 8 MiB вместо обрезания; ограничено чтение error-body при retry. Проверены точный предел/+1, gzip и реальный TLS redirect с контролируемым DNS на private target.
  • B05 CSV: превышение 8 MiB, 10000 строк данных или 64 полей в записи возвращает 413 до создания записей. Убрано тихое обрезание, ограничено усиление памяти CSV-парсером, отмена останавливает последующие записи. Тесты exact/+1, неизвестной длины, корректного префикса перед превышением и quoted/multiline полей прошли.
  • B05 bundle: ограничены compressed/expanded/logical bytes, payload, manifest, число файлов и пути; закрыты sparse tar, gzip trailing expansion, дубликаты/неподписанные файлы и выход через ссылки. HTTP retry читает не более 64 KiB error-body. Astra проверила; bundle/nodecli/httpclient/pipeline tests и повторные legacy 2.56.2 tests прошли. Пределы и настройки.
  • B05 HTTP: общий предел входного тела 8 MiB проверяется после auth до любых handler side effects; до 4 body-запросов одновременно с удержанием памяти до завершения handler. Добавлены локальные tenant/global concurrency и minute quotas preview/lookup, 429 + CORS-visible Retry-After. Refresh/retry сохраняют replay и shared queue quotas; legacy GET не занимают body slots. Astra review и focused HTTP tests прошли.
  • B05 внешние ответы: RIPE 32/4 MiB и DoH 2/1 MiB читаются полностью с проверкой +1, включая gzip; введены default deadlines без ослабления caller/profile deadline. После явного разрешения пользователя восстановлены внутренние DoH/RIPE через отдельный pool; CDN остаётся защищённым и переиспользует соединения. Bootstrap private-DoH и TLS connection-reuse tests прошли; политика.
  • B05/B06 circuit cache: не более 1024 hostname, canonical keys, отказ до dial при заполнении, idle expiry закрытых записей и удаление Prometheus label set. Активные/open записи не вытесняются. Тесты capacity, aliases, lease/ref lifecycle и metric cleanup прошли.
  • B05: выполнить symlink extraction regression в Linux CI (локально Windows пропускает из-за привилегий) и стендовые проверки нагрузки. Локальные квоты не доказывают экономию RSS или общую кластерную квоту; старые правила redirects/proxy для DoH сохранены по разрешённой политике.
  • S06 основа: добавлены явные inventory/stored метрики, сохранены старые имена и raw labels. При ошибке чтения БД выдаётся collection_success=0 без ложных нулевых агрегатов; добавлены alert и описание семантики.
  • S06: desired/published/confirmed applied/observed live группы и общий статус обязательных размещений ещё не завершены.
  • S02 реализация: persistent snapshots, bounded collector с lease/fencing, независимые BGP/health, local authority и atomic discoveries; GET не опрашивает ноды. Coalesced POST refresh подключён к UI. expires_at, ошибки GET и offline скрывают устаревшие значения, включая memoized графики. Astra не обнаружила блокеров; 30 web tests, typecheck/lint/build и scoped backend tests прошли. Протокол 2.56.2 сохранён, source-built binary smoke пройден.
  • S02 PostgreSQL: lease/fencing, discovery rollback и coalesced refresh исполнены на временном PostgreSQL 16.15; все 23 DB/repository tests пройдены без skip (состояние до manifest migration 000037).
  • S02 release gate: Linux/race и реальный mixed-speaker/BIRD стенд остаются проверками B08.
  • B04 миграции: закреплённое соединение, advisory lock до создания schema_migrations, SQL+version в одной транзакции, bounded cleanup и отмена ожидания. Проверка SQL закрывает comment/dollar-identifier/строковые обходы transaction control; Astra повторно проверила.
  • B04 acquisition: отменяемое ожидание и явные ошибки перенесены в атомарный snapshot RMW; отдельные lock sessions заменены transaction advisory lock на том же соединении, что выполняет запись.
  • B04 scheduler scope: reference Compose использует trusted DB enqueue; HTTP-режим ограничен явным tenant. Проверены два tenants, idempotency, system-tenant exclusion и отмена до работы; Astra review и полный Go suite прошли. ListTenantIDs/ListModules ещё требуют context, поэтому общий deadline пока не ограничивает все DB reads.
  • B04 snapshot I/O/RMW: context и ошибки чтения/записи проходят в pipeline/lookup; snapshot read-modify-write и module hash атомарны на одном соединении. Memory concurrency/rollback и обработка partial reads проверены; полный Go test, vet/lint и независимый Astra review прошли.
  • B04 manifest: фиксировать единую версию входов tenant-refresh и не допускать публикации результата по устаревшей конфигурации. PostgreSQL connection-loss/rollback/lock-contention tests прошли на изолированной БД; новые manifest-сценарии потребуют отдельного прогона.
  • B04 PostgreSQL до 000036: 23 обязательных DB/repository tests прошли на изолированном PostgreSQL 16.15 под Windows, без skips; race не запускался. Протокол проверки.
  • B04 PostgreSQL 000037+: после manifest изменений повторить SQL runtime gate; отдельно выполнить Linux/race CI.
  • B03: единый durable enqueue/claim, lease/heartbeat/fencing, shared jobs API, квоты и graceful shutdown; replay исходного запроса предшествует динамическому подбору модулей и rate limit. Scoped Go tests прошли, Astra выявленные ошибки replay/seed/метрик устранены.
  • B03: при одинаковом CreatedAt выбор и список задач используют стабильные RequestSequence/ID. Добавлена регрессия с 32 одинаковыми timestamps; jobs/store прошли 20 повторов, независимая проверка Astra — 50 повторов regression. Это не обещание порядка завершения параллельных workers или snapshot-пагинации.
  • B03: выполнить SQL/race/restart gate и закрыть разрыв между CRUD и enqueue transactional outbox. Заголовок enqueue-failed делает ошибку видимой, но не устраняет crash gap.
  • D02/D04 конфигурация: NATS вынесен в optional профиль, Prometheus получил named volume/retention, deploy worker переведён в static runtime, Go build context сужен, зеркало передаёт digest и обновляется явно без фиксированных пауз. Существующие Docker log caps сохранены. Static review Astra и YAML-проверки прошли; перенос истории Prometheus описан.
  • D02 CLI flags: агент принимает флаги до/после subcommand и отклоняет неизвестные/лишние аргументы watch/serve. В трёх Compose watch-командах флаги идут перед subcommand для совместимости с 2.56.2; argv-тесты и YAML-проверка прошли.
  • D02/D04 runtime: Docker build/config engine, реальные замеры и перенос данных ещё не выполнены. Watch hash-gating, host log replay/rotation, согласование BIRD и SBOM остаются следующими частями Docker-этапа.
Подсистема Существующая реализация Направление развития
Источники префиксов AS_PREFIXES, CDN_CIDRS, DOMAINS, IP_RANGES; кэши, conditional HTTP, снимки Контроль свежести, происхождения и качества данных
Pipeline Batch-запись снимков, input hash, агрегация CIDR, ревизии и diff Согласованный снимок tenant, отмена, лимиты ресурсов
BIRD / узлы Parse-check, LKG, подписанные бандлы, pull/apply, dispatch Подтверждённый applied, сериализация и надёжное восстановление
Jobs Локальный Registry, ограничение активных workers, PostgreSQL audit/claim/reclaim Одна авторитетная очередь, lease, идемпотентность, чтение после restart
Наблюдаемость Prometheus, live BGP polling, PostgreSQL snapshots, runtime logs Фоновый collector, история BGP, качество измерений
Доступ API keys, роли, JWT permissions, tenant context Проверка доверенных прокси, URL transport policy, интеграционные проверки изоляции
Эксплуатация Maintenance policies, dry-run, pruning, аудит обслуживания Предсказуемые бюджеты, backup/restore, контроль деградации
UI React, shadcn/ui, ReUI PRO, Frame kit Использовать существующие блоки для новых backend-возможностей

Не нужно заново создавать очередь, CDN-кэш, revision diff, rollback, lookup, maintenance или мониторинг PostgreSQL: они уже существуют. Нужно усилить их контракт и завершить недостающие части.

3. План улучшения backend

B01 · Достоверный жизненный цикл deploy — P0

Основание: internal/jobs/worker.go:493 вызывает SetLastAppliedRevision до удалённого dispatch. Ошибка dispatch сохраняется в metadata, однако job завершается Succeed (:545, :573). Это позволяет считать удалённую ревизию применённой без подтверждения ноды.

Работы:

  1. Разделить desired_revision, published_revision, dispatched_revision, applied_revision, verified_revision.
  2. Ввести deployment и результаты по каждому speaker: pending → dispatched → applying → applied → verified, отдельно failed, unknown, timed_out.
  3. Обновлять applied_revision только по явному аутентифицированному сообщению ноды о конкретной revision ID. Для новых агентов дополнительно проверять deployment ID/generation и хеш фактической конфигурации; хеш обязателен для уровня verified, но не становится новым обязательным полем старого протокола. Для legacy-агента сохранять уровень подтверждения agent_reported; без явного revision ID оставлять результат unconfirmed, продолжая совместимую доставку. Старое подтверждение не должно перезаписывать новое поколение deploy (см. B08).
  4. Dispatch трактовать как доставку запроса. Для недоступной ноды сохранять desired state и повторять доставку с ограниченным backoff.
  5. В результате массовой операции показывать частичный успех и ошибки по узлам. Не переопределять молча семантику существующего 202: обновить OpenAPI и UI согласованно.

Приёмка: недоступная нода никогда не получает новый applied; потеря ответа и повторный dispatch не применяют один и тот же deployment дважды; после перезапуска CP корректно восстанавливает прогресс; 1 ошибка из 5 узлов явно видна в результате.

B02 · Безопасное применение BIRD и восстановление — P0

Основание: internal/birddeploy/apply.go:69–97 игнорирует ошибки создания LKG, меняет файлы по одному, а восстановление после ошибки использует исходный context. При отменённом context повторный configure также может не выполниться. Локальная функция не содержит блокировки конкурирующих apply.

Работы: сериализовать операции на один BIRD instance, включая несколько процессов; использовать отдельный staging на deployment; проверять полный набор файлов и реальные include paths; выполнять переключение согласованного дерева на одном filesystem; не продолжать без пригодного LKG; удалять исключённые из новой ревизии фрагменты; выполнять rollback с отдельным ограниченным recovery-context; сохранять отдельно исходную ошибку и результат восстановления.

Приёмка: тесты двух конкурентных apply, отмены, заполненного диска, ошибки parse/configure и падения между фазами; после каждого сценария BIRD использует целиком старую или целиком новую проверенную конфигурацию. Статус restored выдаётся только после подтверждённого восстановления.

B03 · Довести PostgreSQL-очередь до надёжного контракта — P1

Основание: internal/jobs/job.go:351–410, internal/jobs/durable.go:62–131, internal/repository/job_audit.go:23–70. Enqueue запускает локального worker; сохранение в БД — best-effort. Повтор idempotency key создаёт новый UUID локально, а SQL conflict сохраняет старый ID строки. Ошибки SQL игнорируются. Reclaim ориентируется на возраст running, без полноценного протокола lease.

Работы:

  1. Для PostgreSQL сделать INSERT/claim авторитетным источником выполнения; HTTP 202 возвращать после успешной фиксации задания. Memory-режим оставить явным режимом разработки.
  2. Определить семантику idempotency: одинаковый ключ и payload возвращают одну операцию в пределах retention; другой payload с тем же ключом — конфликт; намеренный повтор — новый ключ.
  3. Добавить owner_id, lease_until, heartbeat, номер попытки и fencing token. Claim выполнять только при наличии свободной worker capacity.
  4. Реализовать ограниченные retries для временных ошибок, terminal failure и управляемый повтор оператором. Модель доставки — at-least-once с идемпотентными эффектами, без обещания exactly-once.
  5. Ограничить размер ожидающей очереди и число заданий tenant. Текущий semaphore ограничивает активных workers, но не число ожидающих goroutine.
  6. List/get/cancel/report читать из общего хранилища: сейчас routes.go:870–944 обращается к локальному Registry.
  7. При shutdown прекратить claim, завершить либо отпустить lease и закрыть БД после остановки workers.

Приёмка: две API-реплики, restart и повтор запроса дают один логический job; живой долгий worker не перехватывается; сбой записи в БД не даёт ложного 202; job виден и отменяется через любую реплику; нагрузочный burst не вызывает неограниченный рост памяти.

SKIP LOCKED подходит для конкурирующих потребителей очереди, но сам по себе не решает lease и идемпотентность: официальная документация PostgreSQL.

B04 · Миграции, блокировки и tenant isolation — P1

Основание: internal/db/apply.go:34–50 выполняет проверку версии, SQL и запись версии отдельно; миграции запускаются при открытии pool. В internal/repository/module_snapshot.go:177 используется advisory lock с background-context. Standalone scheduler обходит tenants, но HTTP refresh отправляет с одним bearer (internal/scheduler/run.go:70–116).

Работы:

  • Один владелец миграций: dedicated migrate step либо advisory lock на закреплённом соединении; транзакция на поддерживаемую миграцию, явный отдельный путь для non-transactional DDL; проверка совместимости schema при readiness.
  • Передавать context через store/repository I/O; lock acquisition обязан иметь timeout и возвращать ошибку, а не продолжать без блокировки.
  • Сделать tenant-refresh согласованным: сериализация render на tenant и фиксация версий входных снимков. Начать с транзакционного manifest входных версий, затем оптимизировать.
  • Для scheduler выбрать явную модель: доверенный DB worker либо строго ограниченная service identity с tenant-scoped запросами. Не использовать пользовательский bearer как неявный доступ ко всем tenants.
  • Проверить tenant scope для jobs, revisions, node acknowledgements, monitoring и maintenance. Глобальный мониторинг БД должен быть доступен только соответствующей административной роли.

Приёмка: параллельный старт API/workers не ломает миграции; неудачная миграция не записана как успешная; отмена операции прерывает ожидание lock; тесты минимум с двумя tenants доказывают отсутствие смешивания данных.

B05 · Защита исходящих запросов и ограничение нагрузки — P1

Основание: CDN URL уже проверяется, но DNS precheck с кэшем (internal/pipeline/cdn_url.go:96–132) отделён от фактического соединения, а общий HTTP transport не проверяет redirect/dial (internal/httpclient/httpclient.go:18–25). clientIP доверяет X-Forwarded-For (routes_audit.go:134), а rate limiter не удаляет старые IP-ключи (ratelimit.go:36–60). Это подтверждённые пробелы защиты; эксплуатация в текущей сети не проверялась.

Работы: проверять целевые адреса при соединении и каждом redirect; определить поведение при HTTP proxy; разделить публичные CDN/DoH и разрешённые приватные адреса агентов; ограничить размеры скачивания/распаковки/CSV и длительность операций; добавить квоты дорогостоящих refresh/preview/lookup; доверять forwarded headers только от настроенных proxy; ограничить и очищать rate-limit cache.

Приёмка: тесты redirect на private address, смены DNS-ответа, IPv4-mapped IPv6, oversized/decompression payload; корректный доступ к явно разрешённому private agent; поддельный XFF не обходит лимит; cardinality IP не увеличивает память без границы.

B06 · Ускорить lookup, API и pipeline на больших наборах — P2

Основание: internal/lookup/lookup.go:193 перебирает модули и snapshots; для домена поиск повторяется по каждому resolved IP (:173–185). Ошибка DNS сейчас не отражена отдельно от отсутствия IP-совпадений. Размер выигрыша требует benchmark.

Работы:

  • Добавить repository-операцию membership lookup с tenant predicate и индексом по сетевым префиксам; выбрать PostgreSQL inet/cidr и индекс по результатам EXPLAIN. Для memory backend — компактный префиксный индекс, если профиль оправдает сложность.
  • Batch lookup всех resolved IP; лимиты/пагинация совпадений; resolution_status и используемый resolver; единая семантика с DoH pipeline.
  • Для list endpoints — пагинация в SQL, а не после загрузки всей коллекции; summary DTO без тяжёлых snapshots и BIRD-конфига.
  • Профилировать allocations, COPY/merge, render и bundle; уменьшать копирование больших срезов и повторную сериализацию по конкретным pprof/benchmark данным.
  • Повтор внешних HTTP: учитывать 429/Retry-After, jitter и общий deadline; сохранять существующий circuit breaker, добавив наблюдаемость и ограничение его кэша.
  • Не менять все store-интерфейсы за один раз: начать с горячих I/O путей и cancellable методов.

Приёмка: baseline и сравнение на 10k/100k/1M префиксов в подходящем профиле; одинаковый результат IPv4/IPv6 до и после; отсутствие full snapshot scan в DB lookup; API latency не растёт пропорционально числу открытых браузеров.

B07 · Интеграционные проверки и эксплуатационная готовность — P1/P2

Основание: CI запускает go test -race, но в .gitea/workflows/quality.yaml нет отдельного PostgreSQL service/DSN для repository tests. Эти тесты вызывают t.Skip без EVOBGP_TEST_DATABASE_URL (internal/repository/backend_test.go:17). Таким образом зелёный общий Go job не доказывает работу PostgreSQL-сценариев.

Работы: добавить обязательный integration job с временной PostgreSQL и применением миграций; отдельные сценарии durable queue, cancel, two-tenant, restart, prune и concurrent migration; стенд CP + два агента + BIRD для apply/rollback. Добавить backup/restore runbook и периодическое восстановление в изолированное окружение: БД, signing key, node credentials, конфигурации и последние рабочие ревизии. Согласовать RPO/RTO по реальным требованиям.

OpenTelemetry сейчас является stub (internal/observability/otel.go:11). Сначала завершить структурированные логи и request/job/deployment correlation IDs; полноценный tracing включать опционально с sampling, когда он нужен для расследований.

Приёмка: CI явно подтверждает исполнение DB-тестов, а не skip; аварийный сценарий воспроизводим; восстановленный стенд проверяет подписи и применяет известную ревизию; все изменения проходят существующие Go/OpenAPI/BIRD quality gates.

B08 · Обратная совместимость и постепенное обновление спикеров — P1, обязательный release gate

Цель: новый CP обслуживает смешанный парк старых и новых speakers. Отсутствие нового telemetry/ACK API не должно лишать старый speaker возможности работать по существующему pull/sync протоколу. Совместимость доставки и полнота подтверждения — отдельные характеристики.

Основание в текущем коде: internal/nodedispatch/dispatch.go уже читает applied_revision_id из sync-ответа, а health.go — last_applied_revision_id и last_sync_at. При этом sync-клиент подставляет запрошенную revision ID, если ответ её не содержит. Такая подстановка не является подтверждением ноды и должна быть устранена без отказа от legacy dispatch. Текущий формат бандла описан в internal/bundle/pack.go и проверяется в verify.go.

Контракт совместимости

Поверхность Что сохранить Как вводить новое
Node API control plane Существующие /v1/speakers/{id}/revisions/latest, /bundle/{revision_id}, получение signing public key и используемые механизмы auth Новые обязательные поля/семантика — через согласованную capability или отдельную версию API; старые запросы остаются допустимыми
Agent API Текущие sync/health запросы и известные поля ответа Не отправлять новые поля старым strict-decoders; расширенный ACK и telemetry — только после определения возможностей
Signed bundle Для legacy сохранить tar.gz, layout файлов, manifest, Ed25519/SHA-256 и проверку доверенного ключа Новый формат выдавать только совместимому клиенту; legacy-вариант создавать и подписывать отдельно, не менять уже подписанные байты
BIRD-конфигурация Синтаксис и capabilities реально установленной на speaker версии Рендерить по профилю совместимости узла; новую директиву не отправлять неподдерживающему BIRD
Credentials / TLS Действующие ключи доступа, сохранённый signing key и проверка TLS Ротация через отдельный переход с периодом перекрытия там, где протокол его поддерживает; не обходить подпись/auth ради совместимости
UI / monitoring Старые узлы остаются управляемыми и видимыми Явные legacy, unsupported, unconfirmed; отсутствие новой метрики не означает неисправность BGP

Определение возможностей и fallback

  1. Перед изменением протокола инвентаризировать реальные версии/дигесты evobgp-agent, evobgp-node, BIRD и режимы работы: agent serve, pull по расписанию/CLI, иные используемые варианты. Зафиксировать поддерживаемый legacy baseline, а не предполагать, что все ноды равны текущему checkout.
  2. Новые агенты объявляют protocol version и capabilities, например deployment_ack, config_hash, telemetry_v2, bundle_v2. Конкретные имена закрепить в OpenAPI. Возможности важнее сравнения строк версии.
  3. Отсутствие capability metadata в корректном старом ответе либо документированный 404/405 capability endpoint означает legacy. Timeout, 401/403, TLS error и некорректный ответ означают ошибку связи/доступа; они не должны автоматически запускать downgrade или обход проверки безопасности.
  4. Для legacy использовать существующие request/response shapes и signed bundle. Если старый агент явно сообщает совпадающую revision ID, записывать agent_reported, но не утверждать проверку config hash. Голый 2xx подтверждает только запрос; без подтверждения ревизии показывать dispatched/unconfirmed. Если старый health сообщает revision ID без времени наблюдения, показывать ограниченную свежесть, не выдавая его за ACK нового deployment.
  5. Для нод без generation-aware ACK сериализовать deploy на speaker, не допускать неоднозначных перекрывающихся операций. Устаревший ответ не меняет текущий desired state; неоднозначность остаётся unconfirmed до новой проверки.
  6. Старые pull-only узлы продолжают получать published revision без обязательного push/callback. Отсутствие ACK не вызывает бесконечный повтор apply: bounded retry относится к доставке, наблюдение — к reconciliation с backoff.
  7. Новые health-gated rollout/auto-rollback допускают только узлы с достаточным уровнем подтверждения. Legacy-узел можно обновить прежним способом с явным ограничением автоматической верификации; его не блокируют глобально из-за отсутствующей новой функции.

Порядок перехода

  • Сначала CP: добавить совместимые адаптеры, schema expansion и распознавание возможностей; новые флаги включать по speaker/cohort. Старый путь доставки остаётся рабочим.
  • Затем canary-агент: проверить новый протокол на одной ноде, затем обновлять остальные поочерёдно. Смешанный парк — поддерживаемое рабочее состояние, а не только короткий момент установки.
  • Откат: проверить новый CP со старым агентом и новый агент с предыдущим поддерживаемым CP. Сохранить старый digest, совместимый bundle generation и предыдущий протокол. Миграции БД — expand/contract: удаление/переименование полей откладывается за окно rollback; обратная совместимость схемы проверяется запуском предыдущего CP на обновлённой схеме.
  • Срок поддержки: поддерживать инвентаризированные развёрнутые legacy-версии в рамках этого плана. Точный minimum supported version и окно deprecation зафиксировать после инвентаризации. Удаление legacy-пути — отдельное решение владельца проекта после миграции нод; не отключать его автоматически при первом новом релизе. Для опасного неподдерживаемого релиза — адресный план обновления, без бессрочного сохранения уязвимого поведения.

Приёмка: integration matrix использует реальные сохранённые старые binaries/images и golden bundles, а не только mocks текущего агента. Обязательные пары: новый CP + самый старый поддерживаемый агент/CLI; новый CP + текущий агент; предыдущий CP + новый агент; смешанный парк. Проверить pull/sync, подпись, auth, parse/apply/rollback, отсутствующие capabilities/ACK, reconnect после offline и поздний ответ. Обновление только CP не требует изменений на legacy-ноде и не вызывает BGP flap; недоступные новые функции явно обозначены. Проверка совместимости входит в CI/release gate каждого изменения node API, bundle, auth и BIRD render.

4. Сбор статистики: целевая модель

S01 · Отделить текущее состояние от последнего известного — P1

Основание: internal/httpapi/peers_live.go:275–283 не заменяет сохранённый session state, если live-проверка не нашла состояние. В speakers_live.go:117–126 отсутствующий результат превращается в пустой view, а :24–48 считает отсутствие ошибки успешным polling.

Для каждого измерения хранить observed_at, received_at, last_success_at, source, quality, error_code. Состояния качества: ok, stale, unreachable, unknown, disabled. Отдельно хранить последнее подтверждённое BGP state. Неизвестное значение — null/gap, а не ноль и не зелёный статус.

Приёмка: выключение агента переводит данные в stale/unreachable; удалённая сессия не остаётся Established; не настроенный collector не считается успешным; UI показывает возраст измерения и причину отсутствия данных.

S02 · Перенести polling из чтения API в фоновый collector — P1

Исходно live-опрос был привязан к HTTP-чтению и короткому BGP cache, а health опрашивался в collectSpeakerLiveStatus. В реализации S02 эти пути удалены: GET читает persistent snapshots, collector выполняет ограниченные фоновые опросы.

Целевая цепочка:

flowchart LR
    N[Agent / BIRD] --> C[Collector: jitter, limits, deadlines]
    C --> L[Последнее состояние и качество]
    C --> E[История событий]
    C --> M[Prometheus metrics]
    L --> API[Read API]
    E --> API
    API --> UI[ReUI PRO: dashboard и диагностика]

Начальный интервал — 15–30 секунд как проверяемая настройка, не гарантированный SLA. Добавить ограничение параллельных опросов, jitter, backoff для недоступных узлов и единственного владельца опроса в нескольких CP-репликах. Обычный GET отдаёт snapshot сразу. Кнопка «Обновить» ставит объединяемый запрос, не создаёт отдельный fan-out на каждого клиента.

Приёмка: при переходе с 1 на 20 UI-клиентов число poll к одному speaker практически неизменно; просроченные данные отмечаются явно; недоступный speaker не задерживает всю страницу; очередь collector ограничена.

S03 · История BGP и событий — P2

Добавить историю переходов BGP state, длительности недоступности, flap count, времени последнего established, prefix counts по AFI/SAFI там, где их надёжно отдаёт BIRD. Отделить BGP FSM от состояния самого агента и транспорта управления.

Рекомендуемое разделение: PostgreSQL — события, deployment, аудит и ограниченные агрегаты; Prometheus — временные ряды с ограниченной cardinality. На минимальном VPS разрешить режим только событий и последнего snapshot. Не записывать каждый неизменившийся poll как полноценное событие.

Начальные retention-параметры для проверки: детальные события 30 дней, пятиминутные агрегаты 90 дней; это настраиваемые предложения, не требование к любому VPS. Оценивать объём по число рядов × 86400 / интервал × дни, затем измерять фактический размер с индексами/WAL. Partitioning вводить при подтверждённом объёме, не автоматически.

Приёмка: разрывы данных видны на графике; restart collector не создаёт ложный flap; повторные события дедуплицируются; очистка истории ограничена по времени/пакету; экспорт не блокирует ingest.

S04 · Статистика качества источников и pipeline — P1/P2

Для каждого источника: время последней попытки и успеха, latency, HTTP status, ETag/cache hit, valid/invalid/duplicate prefix counts, возраст использованных данных, число последовательных ошибок. Для каждой ревизии — manifest источников и их версий, raw/aggregated counts, добавленные/удалённые префиксы, предупреждения о stale fallback.

Настроить пороги изменений: внезапный пустой ответ или массовое удаление префиксов переводят ревизию в needs_review; ручное исключение оставляет audit trail. Порог зависит от модуля, а не от одной глобальной константы.

Приёмка: оператор видит, почему данные не обновились; временная ошибка upstream не выдаётся за новый успешный ingest; опасное сокращение списка не публикуется автоматически без установленной политики.

S05 · Правильная аналитика PostgreSQL и алерты — P2

Не смешивать накопительные статистики PostgreSQL с показателями за окно. Для rate/delta учитывать stats_reset, временной интервал и исчезновение query ID. В корреляции показывать только реально измеренные точки: текущий internal/pgmonitor/correlation.go объединяет историю jobs с одной текущей точкой cache hit, а RecordCorrelationSnapshot — заглушка.

Расширить существующие Prometheus alerts на потерю телеметрии, stale источники, очередь/возраст jobs, lease failure, deployment drift и ошибки БД. Предусмотреть окно обслуживания, задержку срабатывания, дедупликацию и восстановление. Правило «нет Established» отдельно от «не удалось опросить BIRD».

Не использовать job ID, revision ID, полный URL, домен или префикс как неограниченные labels. Это соответствует рекомендациям Prometheus по cardinality. Детали хранить в событиях и логах.

Приёмка: outage collector вызывает отдельный alert; графики не рисуют отсутствующие значения как нули; периодические snapshots не дублируются каждой API-репликой; количество рядов имеет проверяемый бюджет.

S06 · Разделить смысл метрик — P1

materialized_prefixes_max/sum агрегируют сохранённые ревизии (internal/repository/postgres.go:78–89), а bgp_peer_sessions берёт сохранённый meta_json.session_state (:100–116). Локальная метрика Established не описывает все remote speakers. Это разные показатели: их нельзя объединять в один KPI «маршруты сейчас» или «сеть здорова».

Добавить явные группы inventory / desired / published / confirmed applied / observed live; для multi-speaker считать отдельно «есть Established хотя бы где-то» и «здоровы все обязательные размещения». Общий статус — healthy/degraded/down/unknown по задокументированной политике. При смене метрик сохранить переходный период и обновить dashboards/alerts. Ошибка DB-агрегации должна иметь collection error, а не выглядеть достоверным нулём.

Приёмка: добавление старой ревизии в историю не увеличивает KPI текущих маршрутов; outage одного обязательного speaker даёт degraded даже при Established на другом; fresh live и сохранённая конфигурация явно различаются в API.

5. Отдельный план улучшения и добавления функций

ID / приоритет Функция Польза и объём MVP Зависимости / критерий готовности
F01 · P1 Центр состояния сети Пиры, speakers, stale источники, deployment drift и очередь проблем на одном экране S01–S02, B01; каждый статус имеет источник и возраст
F02 · P1 Паспорт ревизии Продолжение существующих preview/diff: происхождение данных, impact по узлам, warnings и подтверждения apply B01, S04; можно объяснить каждое существенное изменение
F03 · P2 Управляемая раскатка Canary speaker, этапы rollout, лимит параллелизма, pause/resume, окно обслуживания B01–B03/B08; ошибка canary останавливает продвижение; legacy-путь доступен отдельно от автоматической верификации
F04 · P2 История инцидентов и уведомления Flap, outage, stale, deploy failure; acknowledge, silence, webhook и один выбранный канал уведомлений S03–S05; дедупликация, recovery notification и повтор доставки
F05 · P2 Объяснение принадлежности IP/домена Развитие existing lookup: источник → модуль → community → ревизия → desired/applied speaker B06, F02; DNS failure отделён от «совпадений нет»
F06 · P2 Проверка источника до включения Развитие CDN preview: тип ответа, нормализация, ошибки строк, ожидаемый diff, тест расписания S04, B05; тест не меняет опубликованную ревизию
F07 · P2 Backup и восстановление из панели Статус последних backup/restore drill, экспорт manifest, запуск разрешённой backup job B03/B07; секреты не попадают в обычный экспорт UI
F08 · P2 Диагностический пакет Версии CP/agent/BIRD, job/deploy timeline, redacted logs, config hashes B07; токены и приватные ключи исключены; лимит размера
F09 · P3 RPKI и защитные BGP-политики Warning-only проверка origin, max-prefix guard, затем опциональное применение политики Отдельный дизайн и BIRD-тесты; отсутствие валидатора не меняет маршрутизацию молча
F10 · P3 Трафик по интерфейсам/потокам Если нужен именно трафик: SNMP/agent counters для bps/pps; NetFlow/IPFIX — отдельный сервис с sampling/retention Отдельный бюджет и потребность; BGP counters нельзя выдавать за объём пользовательского трафика

ReUI PRO уже подключён. Новые экраны строить на существующем surface: frame и apps/web/src/components/reui-kit/: OpsDashboard для F01, ResourcePage для событий/источников, DetailPanel для ревизии, SettingsShell для политик. Сохранять DataGrid/Filters и семантические Badge. Показывать stale/unknown/partial failure, а не только success/error. Переустановка UI-библиотеки или редизайн shell не нужны. Реализация UI должна следовать контракту проекта и обязательным typecheck/lint/build.

6. Отдельный план оптимизации Docker и ресурсов хоста

Меньший образ сокращает загрузку, распаковку и место на диске. Меньше RAM/CPU/IO достигается также сокращением процессов, работы collectors, повторных configure, логирования и конкуренции. Эти результаты измеряются отдельно.

Что уже оптимизировано

В deploy/docker/gobinary/Dockerfile:26 уже используются multi-stage, CGO_ENABLED=0, -trimpath, -s -w; scheduler/ingest/render используют distroless targets. BuildKit registry cache и общая Go/web сборка присутствуют в docker-bake.hcl. .dockerignore исключает тяжёлые лишние каталоги. nginx уже работает с одним worker; Docker stdout rotation настроена. Повторное добавление этих механизмов не даст заявляемого нового выигрыша.

D00 · Измеримый baseline — P1, до изменения лимитов

Зафиксировать image digests, effective Compose, vCPU/RAM, filesystem/cgroup, число tenants/modules/prefixes/speakers/peers. Собрать следующие сценарии на отдельном стенде:

Сценарий Что измерять Зачем
Idle 15–30 минут CPU, RSS/cgroup memory, SQL connections, число poll/configure, bytes logs Найти постоянную лишнюю работу
Refresh → render → apply Длительность, peak memory, allocations/GC, DB waits, BIRD parse/configure Не оптимизировать idle ценой пикового OOM
Несколько refresh + UI p95/p99 API, queue wait, CPU throttling, IO PSI Проверить backpressure
Недоступные upstream/agents Retries, goroutines, stale age, рост очереди Убедиться в ограниченности деградации
Restart и 24h soak Recovery, leaks, WAL/log growth, OOM/restarts, BGP flaps Проверить эксплуатационный результат
Cold/warm image build и pull Compressed bytes, unique/shared layers, build/pull/extract/start time Отделить CI, сеть, диск и runtime

Использовать effective docker compose config, docker inspect для фактических cgroup limits, docker stats, docker system df -v, cgroup memory/events/cpu.stat, PostgreSQL statistics и BIRD counters. shm_size — допустимая ёмкость, не автоматически занятая RAM. Shared layers не считать несколько раз.

D01 · Исправить ресурсную модель профилей — P1

docker-compose.microvps-full.yaml:9 обещает около 800 MiB суммы лимитов. С учётом наследуемого edge получается 1032M: all 280 + PostgreSQL 256 + BIRD 96 + agent 48 + NATS 48 + web 48 + Prometheus 128 + Traefik 128. Это сумма верхних границ, не измеренная RAM, но заявленного резерва для ОС на 1 GiB VPS она не обеспечивает.

Production example и remote-speaker Compose не имеют аналогичных CPU/RAM caps. Подготовить согласованную матрицу ресурсов:

Профиль Предлагаемый состав Политика
microvps-core — новый вариант all + PostgreSQL + нужные BIRD/agent/edge; UI по потребности Минимум фоновых процессов; внешний scrape либо ограниченный локальный мониторинг
microvps-full Core + UI + локальная история/мониторинг Допуск на 1 GiB только после workload-теста; если бюджет не сходится — честно поднять минимальные требования
reference API и отдельные workers Сохранить изоляцию процессов, но суммировать pools и concurrency
remote-speaker BIRD + agent serve + защищённый endpoint Отдельный бюджет на маршруты, reconfigure и reconnect; CP workload сюда не переносить

Оставлять измеренный запас ОС/Docker и на пики BIRD/subprocesses. Не ужимать BIRD лимиты до появления flap/OOM. Согласовать значения с реальным cgroup, а не только YAML. Основа: Docker resource constraints.

D02 · Сократить лишние процессы и повторяющуюся работу — P1/P2

Изменение Основание Ожидаемый эффект Проверка
Вынести NATS в optional профиль internal/broker/connect.go:12–18 только логирует URL; Compose запускает сервис и зависимости Удаление лишнего процесса и его RAM/IO в минимальном профиле Внутренних потребителей нет; перед production проверить внешних клиентов; удалить зависимости/env согласованно
Change-driven или hash-gated BIRD configure cmd/evobgp-agent/main.go:86–94 безусловно configure в watch-цикле, обычно каждые 30s Меньше работы при неизменной конфигурации Реальный change применяется; restart/manual change покрываются reconciliation; учитывать, что remote serve — другой режим
Проверить CLI flags flag.Parse до positional subcommand, Compose передаёт флаги после watch Настройки interval/socket реально действуют Тест argv для watch/serve; неверный flag не игнорируется молча
Ограничить дублирование логов Production log collector делает docker logs -f в host-файлы; reconnect без cursor может повторять историю Меньше disk IO и роста host logs Cursor/rotation, dedup replay; Runtime Logs API продолжает работать
Использовать существующий cleanup осознанно internal/runtimelogs/policy.go:32–37 default disabled Быстрый контроль накопления Проверить политику/retention; cron threshold не выдавать за жёсткий byte cap

D03 · Настроить Go, PostgreSQL и мониторинг как общий бюджет — P2

  • На текущем Go 1.24 провести A/B с GOMAXPROCS по CPU quota и GOMEMLIMIT с запасом для не-Go памяти и subprocesses. GOMEMLIMIT — мягкий лимит runtime, не полный RSS контейнера. Отдельно оценить обновление toolchain, где изменено container-aware поведение; не смешивать обновление версии с первыми correctness fixes. См. Go GC guide и Go 1.25 runtime.
  • Настроить уже существующие EVOBGP_DB_MAX_CONNS/MIN_CONNS (internal/db/open.go:28–36) по роли. Сумма pools reference-профиля должна укладываться в PostgreSQL max_connections. work_mem оценивать с учётом числа одновременно выполняемых операций, а не как единственный буфер процесса.
  • Ограничить общий job concurrency и отдельные бюджеты сетевого ingest/render/deploy/maintenance. Maintenance не должен вытеснять live collection и подтверждение deploy.
  • Для Prometheus добавить явный named data volume, retention по времени и размеру с запасом для WAL; проверить recreate без потери истории. В текущем Compose mount /prometheus явно не закреплён. Для microvps проверить scrape 30–60s вместо 15s либо external scrape; оценить влияние на detection latency. Сокращение retention не означает пропорционального сокращения head RAM. См. Prometheus storage.

D04 · Уменьшить образы и ускорить сборку — P2

  1. Кандидат на облегчение deploy worker. Target evobgp-deploy наследует birdc runtime (docker-bake.hcl:238–240), хотя internal/deploy/run.go выполняет наблюдение drift. Проверить startup/dependency paths и перевести в уже существующий static runtime, если BIRD tools действительно не вызываются. Считать unique-layer disk отдельно от размера одного image.
  2. Согласовать BIRD toolchain. API/all/node используют сборку BIRD 2.14 на Debian, daemon/agent — Ubuntu package (gobinary/Dockerfile:50–58, bird2/Dockerfile:4–12). Сверить реальные версии/capabilities и parse-check на тех же конфигурациях. Затем унифицировать версию/источник и минимальные runtime libraries для новых образов, сохранив render/validation profile старых удалённых BIRD по B08. Обновление CP image не требует синхронного обновления всех speakers. Не переносить динамический BIRD в scratch без зависимостей.
  3. Сузить Go build inputs. Текущая общая сборка видит root context. Проверить инвалидирование при web-only изменении; отделить Go sources/go.mod/go.sum/migrations и необходимые embed assets. Сохранить общую компиляцию targets, если benchmark не докажет пользу другого подхода. Проверить cold ephemeral builder: layer cache и cache mounts — разные механизмы. См. Docker cache optimization.
  4. Обновляемые base digests. Mirror script пропускает существующий tag и содержит пять фиксированных sleep 8 (mirror-base-images.sh:46–69). Сделать контролируемый digest refresh и задержки только при retry/rate limit. Это улучшение CI/воспроизводимости, не экономия RAM приложения.
  5. Проверки поставки. Фиксировать digest, проверять зависимости и базовые образы, генерировать SBOM при сборке; не включать tooling сборки в runtime. Не применять UPX и случайную смену libc ради красивого image-size числа без benchmark и совместимости.

Приёмка Docker-этапа

До/после используют одинаковый workload и аппаратный профиль. Уменьшение compressed image size, unique disk, steady/peak memory, CPU и logs/day отражается раздельно. Обязательные ограничения: нет новых OOM/restarts и BGP flaps; наборы prefix/community совпадают; подпись, apply и rollback работают; p95/p99 и время refresh не ухудшены сверх согласованного допуска (начальная гипотеза — 5%, уточнить по шуму стенда).

Rollout: лаборатория → canary → остальные узлы; immutable tag/digest и предыдущий рабочий digest готовы. Пересоздавать только изменяемые сервисы с сохранением volumes. Экономия места не должна включать удаление рабочих данных или истории без retention-политики.

7. Очерёдность реализации и оценка объёма

Оценки ниже — предварительные человеко-дни одного опытного инженера, включая тесты и документацию. Это не календарное обещание. После первых воспроизведённых сценариев уточнить оценки; заложить резерв 25–35% на совместимость, миграции и fault injection. Работы могут идти параллельно только при разделении областей и соблюдении зависимостей.

Этап Результат Задачи Оценка
0 · Проверяемая основа PostgreSQL integration CI, воспроизведения проблем, baseline ресурсов, инвентаризация legacy B07, D00, начало B08, актуализация architecture 3–5 дней
1 · Доверие и защита Честные live/applied статусы, безопасный BIRD recovery, закрытие URL/proxy gaps S01/S06, минимальный B01, B02, B05 8–14 дней
2 · Устойчивое исполнение Общая durable очередь и jobs API, миграции/locks, multi-tenant scheduler B03–B04, завершение B01 10–16 дней
3 · Статистика и ресурсный бюджет Независимый collector, история, alerts, измеренная Docker-конфигурация S02–S05, D01–D04, B06 10–18 дней
4 · Операторские функции Dashboard, паспорт ревизии, rollout, incidents F01–F04/F06, по очереди 8–14 дней

Основной объём: ориентировочно 39–67 человеко-дней до резерва. F05/F07/F08 и особенно F09/F10 оценивать отдельно после уточнения глубины; они не должны задерживать correctness и эксплуатационные исправления. Наличие ReUI PRO сокращает работу над типовыми экранами, но не заменяет backend-контракты и проверки отказов.

Дополнение по совместимости B08: предварительно ещё 4–8 человеко-дней на adapters, version matrix и тесты реальных legacy-артефактов; итого 43–75 человеко-дней до резерва. Уточнить после инвентаризации. B08 выполняется сквозным потоком этапов 0–3 и обязателен до выпуска изменённых протоколов B01/S02 или BIRD toolchain D04.

Первая итерация: конкретный backlog

  1. Добавить PostgreSQL integration job и regression tests: повтор idempotency key, queued > grace при двух workers, restart/history, stale Established, непроведённый poll.
  2. Исправить модель live-status; добавить freshness/quality в OpenAPI и UI.
  3. Прекратить преждевременную запись remote applied; показывать pending/unknown до ACK.
  4. Сериализовать BIRD apply и сделать ошибки LKG/recovery явными.
  5. Исправить transport-level CDN URL policy и trusted-proxy обработку.
  6. Снять baseline effective Compose/RAM/CPU/IO, подтвердить бюджет microvps; подготовить optional NATS и bounded logs изменения.
  7. Зафиксировать legacy baseline и golden bundles; добавить проверки «обновили только CP — старый speaker продолжает pull/sync/apply». Выполнить до выпуска изменений подтверждения apply и telemetry.

На первой итерации допустимо временно ограничить конкурентный deploy и число API-writers, пока lease/fencing не готовы. Это должно быть явным ограничением конфигурации и runbook, а не скрытой надеждой на один процесс.

Зависимости, которые нельзя пропускать

flowchart TD
    A[PG integration и воспроизведение отказов] --> B[Миграции и transactional enqueue]
    B --> C[Lease / fencing / durable jobs API]
    C --> D[Надёжный deployment workflow]
    E[Сериализация BIRD и recovery] --> D
    D --> F[Canary / автоматический rollout]
    G[Честные статусы и timestamps] --> H[Фоновый collector]
    H --> I[История / alerts / операторская аналитика]
    J[Baseline ресурсов] --> K[Docker и runtime tuning]
    H --> K

8. Измеримые результаты и правила готовности

Направление Контрольная проверка
Достоверность Unknown/stale не отображаются как Established/healthy; applied всегда имеет подтверждение
Очередь При DB outage запрос не принят ложно; restart восстанавливает задания; lease и эффект защищены от повторов
Сеть Конкурентный apply и сбой rollback не скрываются; old/new поколения не смешиваются
Статистика Число UI клиентов не определяет частоту polling; история имеет coverage/gaps и ограниченный retention
Производительность Сохранён baseline; p95/p99, peak memory и GC сравниваются на фиксированном наборе данных
VPS Effective limits проверены, достаточный запас памяти подтверждён пиковым workload и 24h soak
Доступ Два tenant, несколько ролей, node ACK, redirect и forged forwarded headers покрыты тестами
Восстановление Успешно выполнен restore drill со схемой БД, ключами и проверкой бандла
Контракт OpenAPI обновлён до реализации; docs соответствуют реальному startup и режимам работы
Обратная совместимость Новый CP обслуживает поддерживаемые старые speakers; смешанный парк проходит version matrix; отсутствие новых ACK/метрик не ломает доставку и не создаёт ложный verified

Для Go-изменений — gofmt, go vet ./..., scripts/lint-go.ps1, go test ./... -race -count=1; для API — OpenAPI lint/codegen; для BIRD — scenarios и bird -p; для UI — обязательные typecheck/lint/build и релевантные тесты. Фактически выполненные проверки приведены в backend-baseline.md; наличие команды в плане не означает её успешный прогон.

9. Решения, которые стоит отложить до измерений

  • Переход на микросервисы, Kafka/NATS/Redis как обязательную инфраструктуру: текущая PostgreSQL-очередь сначала должна получить корректные гарантии.
  • Выделенный time-series кластер: сначала посчитать ряды, частоту и retention; для малого профиля PostgreSQL events + Prometheus достаточно как стартовая архитектура.
  • Переписывание Go backend или замена PostgreSQL: аудит не выявил оснований для такого масштаба изменений.
  • Автоматический rollback по одному неуспешному poll: сначала нужны quality/coverage, hysteresis и проверенный recovery.
  • RPKI enforcement и сбор NetFlow: отдельный продуктовый этап с сетевым тестированием и самостоятельным ресурсным бюджетом.

10. Навигация по источникам проекта

Область Основные файлы
Контракт API / доступ openapi.yaml, access.md, permissions.go
Queue / persistence job.go, durable.go, job_audit.go, bootstrap.go
Deploy / recovery worker.go, apply.go, nodedispatch
BGP live peers_live.go, speakers_live.go, bgp_sessions.go
Метрики / PostgreSQL metrics.go, pgmonitor, alerts.yml
Источники / lookup pipeline, lookup.go, httpclient.go
Docker / CI docker-bake.hcl, Compose, microvps override, quality.yaml

Номера строк в тексте относятся к исследованному checkout; при реализации ориентироваться также на имена функций. Приоритеты — инженерная оценка по последствиям возможного отказа, а не утверждение о произошедших production-инцидентах.