From 33fe8fdd1845b1f1734a63325feee2c293fc41fb Mon Sep 17 00:00:00 2001 From: Denozordec Date: Wed, 20 May 2026 14:49:26 +0700 Subject: [PATCH] refactor(httpapi): readiness ping via store backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARCH-09: Ping на store.Backend; readiness без прямого pgxpool.Ping в handler. Co-authored-by: Cursor --- internal/httpapi/routes.go | 11 +++++++---- internal/repository/postgres.go | 5 +++++ internal/store/backend.go | 8 +++++++- internal/store/memory.go | 7 +++++++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/internal/httpapi/routes.go b/internal/httpapi/routes.go index 1fe9d4c..5130861 100644 --- a/internal/httpapi/routes.go +++ b/internal/httpapi/routes.go @@ -84,12 +84,15 @@ func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) { checks := map[string]string{"store": "ok", "jobs": "memory"} ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second) defer cancel() - if s.pgPool != nil { - if err := s.pgPool.Ping(ctx); err != nil { + if err := s.store.Ping(ctx); err != nil { + checks["store"] = "unavailable" + if s.pgPool != nil { checks["postgres"] = "unavailable" - writeJSON(w, http.StatusServiceUnavailable, map[string]any{"status": "not_ready", "checks": checks}) - return } + writeJSON(w, http.StatusServiceUnavailable, map[string]any{"status": "not_ready", "checks": checks}) + return + } + if s.pgPool != nil { checks["postgres"] = "ok" } else { checks["store_backend"] = "memory" diff --git a/internal/repository/postgres.go b/internal/repository/postgres.go index f4386e4..ca5b986 100644 --- a/internal/repository/postgres.go +++ b/internal/repository/postgres.go @@ -71,6 +71,11 @@ func (p *Postgres) DemoIDs() (tenant, moduleCDN, moduleIP, revision, speaker str return p.demoTenant, p.demoCDN, p.demoIP, p.demoRev, p.demoSpk } +// Ping checks PostgreSQL connectivity. +func (p *Postgres) Ping(ctx context.Context) error { + return p.pool.Ping(ctx) +} + func (p *Postgres) MaterializedPrefixStats() (max int, sum int) { ctx := context.Background() // Агрегация в БД — не тащим все строки config_revision в память. diff --git a/internal/store/backend.go b/internal/store/backend.go index 87b6d09..42ca694 100644 --- a/internal/store/backend.go +++ b/internal/store/backend.go @@ -1,6 +1,9 @@ package store -import "time" +import ( + "context" + "time" +) // Backend is the persistence abstraction for the control plane (memory, PostgreSQL, SQLite). type Backend interface { @@ -90,6 +93,9 @@ type Backend interface { // ASNPrefixCache stores RIPEstat announced-prefixes per ASN (global TTL cache). GetASNPrefixCache(asn int64) (*ASNPrefixCacheEntry, bool, error) SetASNPrefixCache(asn int64, holder string, prefixes []string) error + + // Ping verifies backend connectivity (no-op for in-memory). + Ping(ctx context.Context) error } // ASNPrefixCacheEntry is a cached RIPEstat response for one ASN. diff --git a/internal/store/memory.go b/internal/store/memory.go index de875ee..42eb005 100644 --- a/internal/store/memory.go +++ b/internal/store/memory.go @@ -1,6 +1,7 @@ package store import ( + "context" "errors" "fmt" "sort" @@ -291,6 +292,12 @@ func (m *Memory) DemoIDs() (tenant, moduleCDN, moduleIP, revision, speaker strin return m.demoTenantID, m.demoModuleCDN, m.demoModuleIP, m.demoRevisionID, m.demoSpeakerID } +// Ping is a no-op for the in-memory backend. +func (m *Memory) Ping(ctx context.Context) error { + _ = ctx + return nil +} + // ListTenantIDs returns tenant ids sorted lexicographically. func (m *Memory) ListTenantIDs() ([]string, error) { m.mu.RLock()