Switched from Dockmon to Beszel
This commit is contained in:
172
dockmon/stats-service/cache.go
Normal file
172
dockmon/stats-service/cache.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ContainerStats holds real-time stats for a single container
|
||||
type ContainerStats struct {
|
||||
ContainerID string `json:"container_id"`
|
||||
ContainerName string `json:"container_name"`
|
||||
HostID string `json:"host_id"`
|
||||
CPUPercent float64 `json:"cpu_percent"`
|
||||
MemoryUsage uint64 `json:"memory_usage"`
|
||||
MemoryLimit uint64 `json:"memory_limit"`
|
||||
MemoryPercent float64 `json:"memory_percent"`
|
||||
NetworkRx uint64 `json:"network_rx"`
|
||||
NetworkTx uint64 `json:"network_tx"`
|
||||
DiskRead uint64 `json:"disk_read"`
|
||||
DiskWrite uint64 `json:"disk_write"`
|
||||
LastUpdate time.Time `json:"last_update"`
|
||||
}
|
||||
|
||||
// HostStats holds aggregated stats for a host
|
||||
type HostStats struct {
|
||||
HostID string `json:"host_id"`
|
||||
CPUPercent float64 `json:"cpu_percent"`
|
||||
MemoryPercent float64 `json:"memory_percent"`
|
||||
MemoryUsedBytes uint64 `json:"memory_used_bytes"`
|
||||
MemoryLimitBytes uint64 `json:"memory_limit_bytes"`
|
||||
NetworkRxBytes uint64 `json:"network_rx_bytes"`
|
||||
NetworkTxBytes uint64 `json:"network_tx_bytes"`
|
||||
ContainerCount int `json:"container_count"`
|
||||
LastUpdate time.Time `json:"last_update"`
|
||||
}
|
||||
|
||||
// StatsCache is a thread-safe cache for container and host stats
|
||||
type StatsCache struct {
|
||||
mu sync.RWMutex
|
||||
containerStats map[string]*ContainerStats // key: composite key (hostID:containerID)
|
||||
hostStats map[string]*HostStats // key: hostID
|
||||
}
|
||||
|
||||
// NewStatsCache creates a new stats cache
|
||||
func NewStatsCache() *StatsCache {
|
||||
return &StatsCache{
|
||||
containerStats: make(map[string]*ContainerStats),
|
||||
hostStats: make(map[string]*HostStats),
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateContainerStats updates stats for a container
|
||||
func (c *StatsCache) UpdateContainerStats(stats *ContainerStats) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
stats.LastUpdate = time.Now()
|
||||
// Use composite key to support containers with duplicate IDs on different hosts
|
||||
compositeKey := stats.HostID + ":" + stats.ContainerID
|
||||
c.containerStats[compositeKey] = stats
|
||||
}
|
||||
|
||||
// GetContainerStats retrieves stats for a specific container
|
||||
func (c *StatsCache) GetContainerStats(containerID, hostID string) (*ContainerStats, bool) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
compositeKey := hostID + ":" + containerID
|
||||
stats, ok := c.containerStats[compositeKey]
|
||||
return stats, ok
|
||||
}
|
||||
|
||||
// GetAllContainerStats returns all container stats
|
||||
func (c *StatsCache) GetAllContainerStats() map[string]*ContainerStats {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
// Return a copy to avoid race conditions
|
||||
result := make(map[string]*ContainerStats, len(c.containerStats))
|
||||
for k, v := range c.containerStats {
|
||||
statsCopy := *v
|
||||
result[k] = &statsCopy
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// RemoveContainerStats removes stats for a container (when it stops)
|
||||
func (c *StatsCache) RemoveContainerStats(containerID, hostID string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
compositeKey := hostID + ":" + containerID
|
||||
delete(c.containerStats, compositeKey)
|
||||
}
|
||||
|
||||
// UpdateHostStats updates aggregated stats for a host
|
||||
func (c *StatsCache) UpdateHostStats(stats *HostStats) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
stats.LastUpdate = time.Now()
|
||||
c.hostStats[stats.HostID] = stats
|
||||
}
|
||||
|
||||
// GetHostStats retrieves stats for a specific host
|
||||
func (c *StatsCache) GetHostStats(hostID string) (*HostStats, bool) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
stats, ok := c.hostStats[hostID]
|
||||
return stats, ok
|
||||
}
|
||||
|
||||
// GetAllHostStats returns all host stats
|
||||
func (c *StatsCache) GetAllHostStats() map[string]*HostStats {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
// Return a copy to avoid race conditions
|
||||
result := make(map[string]*HostStats, len(c.hostStats))
|
||||
for k, v := range c.hostStats {
|
||||
statsCopy := *v
|
||||
result[k] = &statsCopy
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// RemoveHostStats removes all stats for a specific host
|
||||
func (c *StatsCache) RemoveHostStats(hostID string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
// Remove host stats
|
||||
delete(c.hostStats, hostID)
|
||||
|
||||
// Remove all container stats for this host
|
||||
for id, stats := range c.containerStats {
|
||||
if stats.HostID == hostID {
|
||||
delete(c.containerStats, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CleanStaleStats removes stats older than maxAge
|
||||
func (c *StatsCache) CleanStaleStats(maxAge time.Duration) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// Clean container stats
|
||||
for id, stats := range c.containerStats {
|
||||
if now.Sub(stats.LastUpdate) > maxAge {
|
||||
delete(c.containerStats, id)
|
||||
}
|
||||
}
|
||||
|
||||
// Clean host stats
|
||||
for id, stats := range c.hostStats {
|
||||
if now.Sub(stats.LastUpdate) > maxAge {
|
||||
delete(c.hostStats, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetStats returns a summary of cache state
|
||||
func (c *StatsCache) GetStats() (containerCount, hostCount int) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return len(c.containerStats), len(c.hostStats)
|
||||
}
|
||||
Reference in New Issue
Block a user