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,130 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (s *Server) registerPostgresMonitoringRoutes(m *http.ServeMux) {
|
||||
m.HandleFunc("GET /monitoring/postgres/overview", s.handlePostgresOverview)
|
||||
m.HandleFunc("GET /monitoring/postgres/queries", s.handlePostgresQueries)
|
||||
m.HandleFunc("GET /monitoring/postgres/locks", s.handlePostgresLocks)
|
||||
m.HandleFunc("GET /monitoring/postgres/tables", s.handlePostgresTables)
|
||||
m.HandleFunc("GET /monitoring/postgres/recommendations", s.handlePostgresRecommendations)
|
||||
m.HandleFunc("GET /monitoring/correlation", s.handleMonitoringCorrelation)
|
||||
}
|
||||
|
||||
func (s *Server) requirePostgres(w http.ResponseWriter) bool {
|
||||
if s.pgMonitor == nil {
|
||||
writeProblem(w, http.StatusServiceUnavailable, "Unavailable", "postgresql backend required")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func parseLimitQuery(r *http.Request, def, max int) int {
|
||||
if v := r.URL.Query().Get("limit"); v != "" {
|
||||
if n, err := strconv.Atoi(v); err == nil {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func (s *Server) handlePostgresOverview(w http.ResponseWriter, r *http.Request) {
|
||||
a, ok := authFromContext(r.Context())
|
||||
if !ok || !s.requireAtLeast(w, a, "viewer") || !s.requirePostgres(w) {
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
|
||||
defer cancel()
|
||||
out, err := s.pgMonitor.Overview(ctx)
|
||||
if err != nil {
|
||||
writeInternalError(w, "postgres_overview", err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, out)
|
||||
}
|
||||
|
||||
func (s *Server) handlePostgresQueries(w http.ResponseWriter, r *http.Request) {
|
||||
a, ok := authFromContext(r.Context())
|
||||
if !ok || !s.requireAtLeast(w, a, "viewer") || !s.requirePostgres(w) {
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
|
||||
defer cancel()
|
||||
out, err := s.pgMonitor.TopQueries(ctx, parseLimitQuery(r, 20, 100))
|
||||
if err != nil {
|
||||
writeInternalError(w, "postgres_queries", err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, out)
|
||||
}
|
||||
|
||||
func (s *Server) handlePostgresLocks(w http.ResponseWriter, r *http.Request) {
|
||||
a, ok := authFromContext(r.Context())
|
||||
if !ok || !s.requireAtLeast(w, a, "viewer") || !s.requirePostgres(w) {
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
|
||||
defer cancel()
|
||||
out, err := s.pgMonitor.Locks(ctx)
|
||||
if err != nil {
|
||||
writeInternalError(w, "postgres_locks", err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": out})
|
||||
}
|
||||
|
||||
func (s *Server) handlePostgresTables(w http.ResponseWriter, r *http.Request) {
|
||||
a, ok := authFromContext(r.Context())
|
||||
if !ok || !s.requireAtLeast(w, a, "viewer") || !s.requirePostgres(w) {
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
|
||||
defer cancel()
|
||||
out, err := s.pgMonitor.Tables(ctx, parseLimitQuery(r, 20, 100))
|
||||
if err != nil {
|
||||
writeInternalError(w, "postgres_tables", err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": out})
|
||||
}
|
||||
|
||||
func (s *Server) handlePostgresRecommendations(w http.ResponseWriter, r *http.Request) {
|
||||
a, ok := authFromContext(r.Context())
|
||||
if !ok || !s.requireAtLeast(w, a, "viewer") || !s.requirePostgres(w) {
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
||||
defer cancel()
|
||||
out, err := s.pgMonitor.Recommendations(ctx)
|
||||
if err != nil {
|
||||
writeInternalError(w, "postgres_recommendations", err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, out)
|
||||
}
|
||||
|
||||
func (s *Server) handleMonitoringCorrelation(w http.ResponseWriter, r *http.Request) {
|
||||
a, ok := authFromContext(r.Context())
|
||||
if !ok || !s.requireAtLeast(w, a, "viewer") || !s.requirePostgres(w) {
|
||||
return
|
||||
}
|
||||
window := 60
|
||||
if v := r.URL.Query().Get("window"); v != "" {
|
||||
if n, err := strconv.Atoi(v); err == nil {
|
||||
window = n
|
||||
}
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
||||
defer cancel()
|
||||
out, err := s.pgMonitor.Correlation(ctx, window)
|
||||
if err != nil {
|
||||
writeInternalError(w, "monitoring_correlation", err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, out)
|
||||
}
|
||||
Reference in New Issue
Block a user