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 }