feat(db): implement PostgreSQL monitoring and maintenance features
CI / changes (push) Successful in 9s
CI / commitlint (push) Has been skipped
CI / openapi (push) Successful in 26s
CI / web (push) Successful in 33s
CI / go (push) Successful in 2m11s
CI / bird2 (push) Successful in 16s
CI / release (push) Successful in 3m27s
CI / changes (push) Successful in 9s
CI / commitlint (push) Has been skipped
CI / openapi (push) Successful in 26s
CI / web (push) Successful in 33s
CI / go (push) Successful in 2m11s
CI / bird2 (push) Successful in 16s
CI / release (push) Successful in 3m27s
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.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
package pgmonitor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
// Correlation builds aligned timeline points from job_audit and overview cache.
|
||||
func (s *Service) Correlation(ctx context.Context, windowMinutes int) (CorrelationResponse, error) {
|
||||
if s == nil || s.pool == nil {
|
||||
return CorrelationResponse{}, fmt.Errorf("pgmonitor: postgres not configured")
|
||||
}
|
||||
if windowMinutes <= 0 {
|
||||
windowMinutes = 60
|
||||
}
|
||||
if windowMinutes > 1440 {
|
||||
windowMinutes = 1440
|
||||
}
|
||||
since := time.Now().UTC().Add(-time.Duration(windowMinutes) * time.Minute)
|
||||
|
||||
rows, err := s.pool.Query(ctx, `
|
||||
SELECT date_trunc('minute', finished_at) AS bucket,
|
||||
percentile_cont(0.99) WITHIN GROUP (ORDER BY
|
||||
EXTRACT(EPOCH FROM (finished_at - started_at)) * 1000)
|
||||
FROM job_audit
|
||||
WHERE finished_at >= $1 AND kind IN ('module_refresh', 'tenant_refresh')
|
||||
AND status = 'succeeded' AND started_at IS NOT NULL
|
||||
GROUP BY 1
|
||||
ORDER BY 1`, since)
|
||||
if err != nil {
|
||||
return CorrelationResponse{}, fmt.Errorf("pgmonitor: correlation jobs: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
points := make(map[time.Time]*CorrelationPoint)
|
||||
for rows.Next() {
|
||||
var bucket time.Time
|
||||
var p99 *float64
|
||||
if err := rows.Scan(&bucket, &p99); err != nil {
|
||||
return CorrelationResponse{}, err
|
||||
}
|
||||
bucket = bucket.UTC()
|
||||
pt := points[bucket]
|
||||
if pt == nil {
|
||||
pt = &CorrelationPoint{Timestamp: bucket}
|
||||
points[bucket] = pt
|
||||
}
|
||||
if p99 != nil {
|
||||
pt.PipelineRefreshP99Ms = *p99
|
||||
}
|
||||
}
|
||||
|
||||
ov, err := s.Overview(ctx)
|
||||
if err == nil && ov.Database.CacheHitPct > 0 {
|
||||
now := time.Now().UTC().Truncate(time.Minute)
|
||||
pt := points[now]
|
||||
if pt == nil {
|
||||
pt = &CorrelationPoint{Timestamp: now}
|
||||
points[now] = pt
|
||||
}
|
||||
pt.CacheHitPct = ov.Database.CacheHitPct
|
||||
}
|
||||
|
||||
out := make([]CorrelationPoint, 0, len(points))
|
||||
for _, p := range points {
|
||||
out = append(out, *p)
|
||||
}
|
||||
// simple sort by time
|
||||
for i := 0; i < len(out); i++ {
|
||||
for j := i + 1; j < len(out); j++ {
|
||||
if out[j].Timestamp.Before(out[i].Timestamp) {
|
||||
out[i], out[j] = out[j], out[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return CorrelationResponse{WindowMinutes: windowMinutes, Points: out}, nil
|
||||
}
|
||||
|
||||
// RecordCorrelationSnapshot is a hook for future Prometheus samples (no-op placeholder).
|
||||
func RecordCorrelationSnapshot(_ *pgxpool.Pool) {}
|
||||
Reference in New Issue
Block a user