package pgmonitor import ( "context" "errors" "fmt" "time" "github.com/jackc/pgx/v5/pgxpool" ) // Service provides PostgreSQL observability and maintenance helpers (control plane instance scope). type Service struct { pool *pgxpool.Pool cache *ttlCache } // NewService constructs a metrics service for the API PostgreSQL pool. func NewService(pool *pgxpool.Pool) *Service { if pool == nil { return nil } return &Service{ pool: pool, cache: newTTLCache(10 * time.Second), } } // Pool exposes the underlying pool for job workers. func (s *Service) Pool() *pgxpool.Pool { if s == nil { return nil } return s.pool } // Overview returns cached instance-level stats. func (s *Service) Overview(ctx context.Context) (Overview, error) { if s == nil || s.pool == nil { return Overview{}, errors.New("pgmonitor: postgres not configured") } if v, ok := s.cache.get("overview"); ok { if o, ok := v.(Overview); ok { return o, nil } } o, err := s.fetchOverview(ctx) if err != nil { return Overview{}, err } s.cache.set("overview", o) return o, nil } // Locks returns active / blocking locks. func (s *Service) Locks(ctx context.Context) ([]LockRow, error) { if s == nil || s.pool == nil { return nil, errors.New("pgmonitor: postgres not configured") } if v, ok := s.cache.get("locks"); ok { if rows, ok := v.([]LockRow); ok { return rows, nil } } rows, err := queryLocks(ctx, s.pool) if err != nil { return nil, err } s.cache.set("locks", rows) return rows, nil } // Tables returns top tables by size with I/O stats. func (s *Service) Tables(ctx context.Context, limit int) ([]TableStat, error) { if s == nil || s.pool == nil { return nil, errors.New("pgmonitor: postgres not configured") } key := fmt.Sprintf("tables:%d", limit) if v, ok := s.cache.get(key); ok { if rows, ok := v.([]TableStat); ok { return rows, nil } } rows, err := queryTables(ctx, s.pool, limit) if err != nil { return nil, err } s.cache.set(key, rows) return rows, nil }