package store import ( "fmt" ) // InsertContainerStatsSample appends a single container sample row. func (s *Store) InsertContainerStatsSample(sample ContainerStatsSample) error { _, err := s.db.Exec( `INSERT INTO container_stats_samples ( container_id, owner_type, owner_id, ts, cpu_percent, memory_usage, memory_limit, network_rx, network_tx, block_read, block_write ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, sample.ContainerID, sample.OwnerType, sample.OwnerID, sample.TS, sample.CPUPercent, sample.MemoryUsage, sample.MemoryLimit, sample.NetworkRxBytes, sample.NetworkTxBytes, sample.BlockReadBytes, sample.BlockWriteBytes, ) if err != nil { return fmt.Errorf("insert container stats sample: %w", err) } return nil } // InsertSystemStatsSample appends a single host-level sample row. func (s *Store) InsertSystemStatsSample(sample SystemStatsSample) error { _, err := s.db.Exec( `INSERT INTO system_stats_samples ( ts, ncpu, memory_total, workload_cpu_percent, workload_mem_usage, containers_running, disk_total_bytes ) VALUES (?, ?, ?, ?, ?, ?, ?)`, sample.TS, sample.NCPU, sample.MemoryTotal, sample.WorkloadCPUPercent, sample.WorkloadMemUsage, sample.ContainersRunning, sample.DiskTotalBytes, ) if err != nil { return fmt.Errorf("insert system stats sample: %w", err) } return nil } // ListContainerStatsSamples returns samples for the given owner since the // given unix timestamp (inclusive), ordered by ts ascending. func (s *Store) ListContainerStatsSamples(ownerType, ownerID string, sinceTS int64) ([]ContainerStatsSample, error) { rows, err := s.db.Query( `SELECT container_id, owner_type, owner_id, ts, cpu_percent, memory_usage, memory_limit, network_rx, network_tx, block_read, block_write FROM container_stats_samples WHERE owner_type = ? AND owner_id = ? AND ts >= ? ORDER BY ts ASC`, ownerType, ownerID, sinceTS, ) if err != nil { return nil, fmt.Errorf("list container stats samples: %w", err) } defer rows.Close() var out []ContainerStatsSample for rows.Next() { var s ContainerStatsSample if err := rows.Scan( &s.ContainerID, &s.OwnerType, &s.OwnerID, &s.TS, &s.CPUPercent, &s.MemoryUsage, &s.MemoryLimit, &s.NetworkRxBytes, &s.NetworkTxBytes, &s.BlockReadBytes, &s.BlockWriteBytes, ); err != nil { return nil, fmt.Errorf("scan container stats sample: %w", err) } out = append(out, s) } return out, rows.Err() } // ListAllRecentContainerStatsSamples returns samples across every owner since // the given unix timestamp, ordered by ts ascending. Used by the system // dashboard "top containers" widget where the UI wants a mixed pool. func (s *Store) ListAllRecentContainerStatsSamples(sinceTS int64) ([]ContainerStatsSample, error) { rows, err := s.db.Query( `SELECT container_id, owner_type, owner_id, ts, cpu_percent, memory_usage, memory_limit, network_rx, network_tx, block_read, block_write FROM container_stats_samples WHERE ts >= ? ORDER BY ts ASC`, sinceTS, ) if err != nil { return nil, fmt.Errorf("list all recent container stats samples: %w", err) } defer rows.Close() var out []ContainerStatsSample for rows.Next() { var s ContainerStatsSample if err := rows.Scan( &s.ContainerID, &s.OwnerType, &s.OwnerID, &s.TS, &s.CPUPercent, &s.MemoryUsage, &s.MemoryLimit, &s.NetworkRxBytes, &s.NetworkTxBytes, &s.BlockReadBytes, &s.BlockWriteBytes, ); err != nil { return nil, fmt.Errorf("scan container stats sample: %w", err) } out = append(out, s) } return out, rows.Err() } // ListSystemStatsSamples returns host samples since the given unix timestamp. func (s *Store) ListSystemStatsSamples(sinceTS int64) ([]SystemStatsSample, error) { rows, err := s.db.Query( `SELECT ts, ncpu, memory_total, workload_cpu_percent, workload_mem_usage, containers_running, disk_total_bytes FROM system_stats_samples WHERE ts >= ? ORDER BY ts ASC`, sinceTS, ) if err != nil { return nil, fmt.Errorf("list system stats samples: %w", err) } defer rows.Close() var out []SystemStatsSample for rows.Next() { var s SystemStatsSample if err := rows.Scan( &s.TS, &s.NCPU, &s.MemoryTotal, &s.WorkloadCPUPercent, &s.WorkloadMemUsage, &s.ContainersRunning, &s.DiskTotalBytes, ); err != nil { return nil, fmt.Errorf("scan system stats sample: %w", err) } out = append(out, s) } return out, rows.Err() } // PruneStatsSamplesBefore deletes all samples older than the given unix timestamp // from both the container and system stats tables. Returns rows deleted across // both tables. func (s *Store) PruneStatsSamplesBefore(ts int64) (int64, error) { r1, err := s.db.Exec(`DELETE FROM container_stats_samples WHERE ts < ?`, ts) if err != nil { return 0, fmt.Errorf("prune container stats samples: %w", err) } r2, err := s.db.Exec(`DELETE FROM system_stats_samples WHERE ts < ?`, ts) if err != nil { return 0, fmt.Errorf("prune system stats samples: %w", err) } n1, _ := r1.RowsAffected() n2, _ := r2.RowsAffected() return n1 + n2, nil }