Files
EvoBGP/internal/maintenance/db_stats_provider.go
T
DenozordecandCursor 6510a9ca22 feat(maintenance): add policy executor and config provider
ConfigProvider, PolicyExecutor, DBStatsProvider, scheduler и safety; зависимость robfig/cron/v3.

Co-authored-by: Cursor <[email protected]>
2026-06-12 13:21:32 +07:00

63 lines
1.8 KiB
Go

package maintenance
import (
"context"
"fmt"
"evobgp/internal/pgmonitor"
)
// TableHints are PostgreSQL statistics hints for UI recommendations.
type TableHints struct {
TableName string `json:"table_name"`
DeadTuples int64 `json:"n_dead_tup"`
BloatRatio float64 `json:"bloat_ratio,omitempty"`
LastAutovacuum string `json:"last_autovacuum,omitempty"`
RecommendVacuum bool `json:"recommend_vacuum"`
Detail string `json:"detail,omitempty"`
Refs []string `json:"refs,omitempty"`
}
// DBStatsProvider wraps pgmonitor for maintenance policy hints.
type DBStatsProvider struct {
pg *pgmonitor.Service
}
// NewDBStatsProvider constructs a stats provider.
func NewDBStatsProvider(pg *pgmonitor.Service) *DBStatsProvider {
return &DBStatsProvider{pg: pg}
}
// Hints returns table-level vacuum/bloat hints.
func (d *DBStatsProvider) Hints(ctx context.Context, tableName string) (TableHints, error) {
out := TableHints{TableName: tableName}
if d == nil || d.pg == nil {
return out, fmt.Errorf("maintenance: postgres monitoring not configured")
}
if err := ValidateTableName(tableName); err != nil {
return out, err
}
tables, err := d.pg.Tables(ctx, 100)
if err != nil {
return out, err
}
for _, t := range tables {
if t.Relname != tableName {
continue
}
out.DeadTuples = t.DeadTuples
out.BloatRatio = t.BloatRatio
if t.LastAutovacuum != nil {
out.LastAutovacuum = t.LastAutovacuum.UTC().Format("2006-01-02T15:04:05Z")
}
if t.BloatRatio > 0.2 && t.DeadTuples > 5000 {
out.RecommendVacuum = true
out.Detail = "Высокая доля n_dead_tup; рекомендуется VACUUM."
out.Refs = []string{t.Relname}
}
return out, nil
}
out.Detail = "Таблица не найдена в pg_stat_user_tables (top by size)."
return out, nil
}