package metrics import ( "log" "time" ) // ContainerTelemetry holds aggregated resource stats for one container. type ContainerTelemetry struct { ContainerName string `json:"container_name"` MemoryCurrentMB float64 `json:"memory_current_mb"` MemoryAvgMB float64 `json:"memory_avg_mb"` MemoryPeakMB float64 `json:"memory_peak_mb"` CPUAvgPercent float64 `json:"cpu_avg_percent"` SampleCount int `json:"sample_count"` } // GetContainerTelemetry queries the metrics DB for per-container resource // summaries since the given time. Returns empty slice (not error) if no data. func (s *MetricsStore) GetContainerTelemetry(since time.Time) ([]ContainerTelemetry, error) { sinceUnix := since.Unix() rows, err := s.db.Query(` SELECT container_name, AVG(mem_usage_mb), MAX(mem_usage_mb), AVG(cpu_percent), COUNT(*) FROM container_metrics WHERE ts > ? GROUP BY container_name`, sinceUnix) if err != nil { return nil, err } defer rows.Close() var results []ContainerTelemetry for rows.Next() { var ct ContainerTelemetry if err := rows.Scan(&ct.ContainerName, &ct.MemoryAvgMB, &ct.MemoryPeakMB, &ct.CPUAvgPercent, &ct.SampleCount); err != nil { log.Printf("[WARN] telemetry row scan failed: %v", err) continue } results = append(results, ct) } if err := rows.Err(); err != nil { return nil, err } // Get current (most recent) memory per container if stats, err := s.QueryContainerSummary(); err == nil { currentMap := make(map[string]float64, len(stats)) for _, st := range stats { currentMap[st.ContainerName] = st.MemUsageMB } for i := range results { if cur, ok := currentMap[results[i].ContainerName]; ok { results[i].MemoryCurrentMB = cur } } } if results == nil { results = []ContainerTelemetry{} } return results, nil }