Added PostgreSQL monitoring and maintenance capabilities to the API, including new endpoints for instance-level metrics, maintenance operations, and job scheduling. Updated the HTTP API to support PostgreSQL monitoring routes and integrated a background scheduler for metrics collection. Enhanced the CLI with database commands for maintenance tasks. Updated documentation to reflect these changes.
Полный технический аудит EvoBGP для production-сценария (10+ клиентов, нестабильная сеть). Архитектура — hybrid control plane; сильные стороны: stale fallback, CDN/RIPEstat resilience, Ed25519 bundles. Критичные риски: in-process jobs, DoH без retry, misconfiguration demo-seed, отсутствие HA API.
Добавить DoWithRetry для DoH в internal/pipeline/refresh.go
pending
id
content
status
fix-job-meta-race
Исправить чтение j.Meta в worker.go через Snapshot() или locked accessor
pending
id
content
status
cdn-preview-resilience
Перевести CDN preview на upstreamHTTPDo в routes_crud.go
pending
id
content
status
partial-cdn-failure
Partial CDN source failure: skip/degrade вместо fail всего модуля
pending
id
content
status
graceful-shutdown
Cancel/drain jobs при SIGTERM в cmd/evobgp-api и evobgp-all
pending
id
content
status
ha-job-queue
Roadmap: распределённая очередь jobs (PG claim или NATS) для HA API
pending
false
Технический аудит EvoBGP
Executive summary
EvoBGP — hybrid control plane: один процесс evobgp-all (monolith) или reference Compose с разделёнными воркерами (docs/architecture.md). Data plane (BIRD + agent) отделён от control plane (API + PostgreSQL + jobs).
ETag conditional GET, ASN TTL-кэш, parallel collect с cap
Подписанные бандлы Ed25519, verify перед apply
Главные риски для 10+ клиентов:
jobs.Registry — in-memory, только в процессе API (ARCH-04)
DoH — без retry/breaker (критично при блокировках провайдеров)
Один failed CDN source без stale cache валит весь модуль
Production misconfiguration: Bearer dev, HTTP API, ephemeral bundle key
Data race на Job.Meta и alias pointers в store.Memory
1. Архитектура
Стиль
flowchart TB
subgraph hybrid [Hybrid deployment]
All[evobgp_all monolith]
Split[evobgp_api + workers]
end
subgraph cp [Control plane]
API[HTTP API]
Jobs[jobs.Registry in-process]
PG[(PostgreSQL)]
end
subgraph dp [Data plane per speaker]
Agent[evobgp_agent]
BIRD[BIRD2]
NodeCLI[evobgp_node]
end
All --> API
Split --> API
API --> Jobs
API --> PG
NodeCLI --> API
Agent --> API
Agent --> BIRD
Горизонтальная API:не поддерживается — два evobgp-api = два независимых Registry; job_audit в PG — audit only, не очередь исполнения
Workers (ingest/render/deploy): координируются через общую БД, не через jobs — OK для prefetch/drift
Отказоустойчивость
Сценарий
Поведение
Оценка
CDN/RIPEstat недоступен
Stale snapshot + circuit breaker
Хорошо (если был prior snapshot)
DoH недоступен
Fail модуля или stale domain snapshot
Средне (нет HTTP retry)
API restart mid-job
Job теряется из Registry; audit может быть inconsistent
Плохо
PG недоступен
API /v1/ready → 503
OK
Agent unreachable
Deploy job succeed, drift в evobgp-deploy
Частичный fail (by design)
Рекомендация: для 10+ клиентов — evobgp-all на каждом CP или один CP + tuning; HA API требует распределённой очереди (NATS/Redis + worker pool) — задокументировано как future work.
2. Анализ кода
Антипаттерны
ID
Проблема
Файл
Критичность
A1
Concurrent map read/write — worker читает j.Meta без lock, handler пишет через mergeMeta/Snapshot
Tenant refresh:aggregateTenantPrefixRowsAll — parallel по модулям, но каждый модуль может refetch все CDN/ASN/DoH — aggregate.go:28+. Snapshot skip есть через module_hash — проверять hit rate в meta.
ASN resolve:PolitePause() 150ms между AS — asnresolve/ripestat.go — при 50 AS = +7.5s minimum.
GetModulePrefixSnapshot вызывается многократно в одном refresh (cdn_snapshot, collect_parallel) — potential duplicate DB reads.
Auth TouchAPIKeyLastUsed: UPDATE на каждый request (async) — load на PG при high RPS.
Кэширование
Кэш
TTL
Gap
ASN prefix cache
1800s (EVOBGP_ASN_CACHE_TTL_SEC)
OK
CDN ETag in DB
Until 304/change
OK
Module prefix snapshot
Content-hash based skip
OK
peerLiveCache
In-memory, per-process
Не shared между API replicas; нет defensive copy
Circuit breaker state
Per-process
Не shared
Конкретные улучшения
// 1. CDN preview — использовать upstreamHTTPDo вместо прямого Doresp,err:=pipeline.UpstreamHTTPDo(r.Context(),s.cdnHTTP,req)// extract upstreamHTTPDo// 2. Job.Meta — читать под lock или через Snapshot()st:=j.Snapshot()mid,_:=st["meta"].(map[string]any)["module_id"].(string)// 3. Memory store — возвращать копии (как Postgres)modCopy:=*modreturn&modCopy,nil
4. Сетевое взаимодействие (критично)
Текущее состояние
flowchart LR
subgraph resilient [Resilient path]
CDN[CDN fetch]
RIPE[RIPEstat]
CDN --> Breaker[Circuit breaker]
RIPE --> Breaker
Breaker --> Retry[DoWithRetry 3x linear 2s]
end
subgraph fragile [Fragile path]
DoH[DoH resolve]
Preview[CDN preview API]
AgentHealth[Agent health/bird]
DoH --> SingleDo[Single hc.Do]
Preview --> SingleDo
AgentHealth --> SingleDo
end
subgraph fallback [App-level fallback]
Stale[Stale snapshot]
SysDNS[System DNS]
DoH --> SysDNS
CDN --> Stale
RIPE --> Stale
end
Upstream
Timeout
Retry
Breaker
Stale fallback
CDN ingest
45s
3× linear
per-host
yes
RIPEstat
45s
3×
per-host
yes + cache
DoH
10s/profile
no
no
domain snapshot
CDN preview
45s
no
no
N/A
Scheduler→API
45s
3×
no
N/A
Node dispatch
30s
inline 3×
no
N/A
Пробелы для блокировок провайдеров
DoH без retry — transient timeout = fail; failover между profiles есть, но каждый profile — single shot
429/408 не ретраятся — только >= 500
Нет jitter — thundering herd при mass tenant refresh
DNS rebinding TOCTOU — SSRF check до fetch, HTTP dial без pinned IP — cdn_url.go:75-115
Circuit breaker без half-open — после 30s cooldown сразу full traffic — circuit.go:29-33
Breaker per-process — ingest container ≠ API container
DoH retry — 5–10 строк в refresh.go, reuse existing DoWithRetry
CDN preview → upstreamHTTPDo — 1 line change in handler
Job.Meta read fix — replace 4 reads in worker.go with Snapshot() parsing
Log prefetch failures — visibility без изменения behavior
Document DoH failover playbook — multiple profiles (Cloudflare, Google, Quad9) + failover policy for censored regions
Run go test -race ./internal/jobs/... in CI — catch Meta race
Prefer evobgp-all over split reference for <20 tenants — eliminates Registry split bug
Диаграмма: refresh под сетевым stress
sequenceDiagram
participant Op as Operator
participant API as evobgp_api
participant Job as module_refresh
participant CDN as CDN_upstream
participant PG as PostgreSQL
Op->>API: POST /modules/id/refresh
API->>Job: Enqueue
Job->>CDN: GET with ETag
alt CDN timeout or 5xx
CDN-->>Job: error after 3 retries
Job->>PG: load prior snapshot
alt stale exists
Job->>PG: CreateRenderRevision stale
Job-->>API: succeeded degraded
else no stale
Job-->>API: failed
end
else CDN 200
CDN-->>Job: new prefixes
Job->>PG: CreateRenderRevision
end
Итоговая оценка зрелости
Область
Оценка
Комментарий
Архитектура
7/10
Чистые слои; HA/API scaling — слабое место
Сеть/resilience
6/10
CDN/ASN хорошо; DoH/preview — пробелы
Concurrency
6/10
Registry продуман; Meta race, shutdown
Performance
7/10
Parallel collect, caching; tuning needed at scale
Security
6/10
Crypto OK; ops/config risks dominate
Maintainability
8/10
Docs, rules, OpenAPI, tests
Вердикт: проект готов для 10+ клиентов в single-CP deployment (evobgp-all + PostgreSQL + production checklist) при условии ops discipline. Для multi-CP HA и агрессивных сетевых блокировок — приоритет: DoH retry, partial CDN failure, distributed job queue, HTTP proxy.