Files
felhom.eu/hub/internal/store/r1346_speed.go
T

72 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package store
import (
"fmt"
"time"
)
// R-1346 (2026-10-30): the statements the measurement named, in one place so the plan test reads the exact SQL the
// code runs (r1346_speed_test.go TestR1346_HeavyStatementsUseTheirIndex).
// queryNewestControllerVersion — the vouch save's R-120 fleet check (configs.go handleSetArtifacts).
const queryNewestControllerVersion = `SELECT DISTINCT controller_version FROM reports WHERE controller_version IS NOT NULL AND controller_version != ''`
// queryHostAgentVersionsSeen — the hourly OS alarm, for a box with no healthy OS report.
const queryHostAgentVersionsSeen = `SELECT COALESCE(agent_version, ''), MIN(received_at) FROM host_reports WHERE host_id = ? GROUP BY agent_version`
// queryPruneReports / queryPruneHostReports — the 04:30 retention prune (Prune).
const (
queryPruneReports = `DELETE FROM reports WHERE received_at < ?`
queryPruneHostReports = `DELETE FROM host_reports WHERE received_at < ?`
)
// queryFleetAppSummary — the apps page (GetFleetAppSummary).
const queryFleetAppSummary = `
SELECT app_name,
MAX(display_name) as display_name,
COUNT(DISTINCT customer_id) as deployment_count,
AVG(memory_avg_mb) as avg_memory_mb,
MAX(memory_peak_mb) as peak_memory_mb,
AVG(cpu_avg_percent) as avg_cpu,
SUM(log_errors) as total_errors,
SUM(log_warnings) as total_warnings,
MAX(catalog_estimate) as catalog_estimate,
MAX(catalog_limit) as catalog_limit
FROM app_telemetry INDEXED BY idx_app_telemetry_prune
WHERE reported_at > ?
GROUP BY app_name
ORDER BY deployment_count DESC, avg_memory_mb DESC`
// r1346Indexes lets the statements above read only what they need. controller_version and agent_version sit AFTER
// report_json in their rows, so without an index every read of them walks each row's whole JSON (overflow pages);
// received_at has no index of its own, so the prune scanned both tables INSIDE its write lock (39 s on 2026-10-30,
// and a controller report refused with „database is locked" meanwhile). Built once, at the first start that has them:
// 63 s measured on a copy of the live database in the hub pod (256 MiB) — inside the startup probe's 180 s with the
// measured 31–36 s start (R-1326).
var r1346Indexes = []struct{ name, ddl string }{
{"idx_reports_received", `CREATE INDEX IF NOT EXISTS idx_reports_received ON reports(received_at)`},
{"idx_host_reports_received", `CREATE INDEX IF NOT EXISTS idx_host_reports_received ON host_reports(received_at)`},
{"idx_reports_controller_version", `CREATE INDEX IF NOT EXISTS idx_reports_controller_version ON reports(controller_version)`},
{"idx_host_reports_agent_version", `CREATE INDEX IF NOT EXISTS idx_host_reports_agent_version ON host_reports(host_id, agent_version, received_at)`},
}
func (s *Store) migrateR1346Indexes() error {
for _, ix := range r1346Indexes {
var n int
if err := s.db.QueryRow(`SELECT COUNT(*) FROM sqlite_master WHERE type = 'index' AND name = ?`, ix.name).Scan(&n); err != nil {
return err
}
if n > 0 {
continue
}
began := time.Now()
if _, err := s.db.Exec(ix.ddl); err != nil {
return fmt.Errorf("%s: %w", ix.name, err)
}
if s.logger != nil {
s.logger.Printf("[INFO] store: index %s built in %s (R-1346, once)", ix.name, time.Since(began).Round(time.Millisecond))
}
}
return nil
}