From 8fbe53406afeb78ca838bd51aec71fde1e2b70ea Mon Sep 17 00:00:00 2001 From: Pulse Monitor Date: Tue, 9 Sep 2025 21:35:09 +0000 Subject: [PATCH] feat: improve memory reporting by using available memory instead of free (addresses #435) - Add Available field to MemoryStatus struct to capture memory available for allocation - Update node memory calculation to use Available memory when present - This excludes non-reclaimable cache/buffers from used memory calculation - Provides more accurate memory pressure indication, avoiding false alerts - Falls back to traditional used memory if Available field is missing (older Proxmox versions) --- internal/monitoring/monitor.go | 33 +++++++++++++++++++++++++++++++++ pkg/proxmox/client.go | 7 ++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/internal/monitoring/monitor.go b/internal/monitoring/monitor.go index bc5657779..0970103fc 100644 --- a/internal/monitoring/monitor.go +++ b/internal/monitoring/monitor.go @@ -832,6 +832,39 @@ func (m *Monitor) pollPVEInstance(ctx context.Context, instanceName string, clie Msg("No valid disk metrics available for node") } + // Update memory metrics to use Available field for more accurate usage + if nodeInfo.Memory != nil && nodeInfo.Memory.Total > 0 { + // Calculate memory usage based on available memory (which excludes non-reclaimable cache) + // This provides a more accurate picture of actual memory pressure + var actualUsed uint64 + if nodeInfo.Memory.Available > 0 { + // Use available memory for calculation (preferred method) + actualUsed = nodeInfo.Memory.Total - nodeInfo.Memory.Available + log.Debug(). + Str("node", node.Node). + Uint64("total", nodeInfo.Memory.Total). + Uint64("available", nodeInfo.Memory.Available). + Uint64("actualUsed", actualUsed). + Float64("usage", safePercentage(float64(actualUsed), float64(nodeInfo.Memory.Total))). + Msg("Using available memory for accurate usage calculation") + } else { + // Fallback to traditional used memory if available field is missing + actualUsed = nodeInfo.Memory.Used + log.Debug(). + Str("node", node.Node). + Uint64("total", nodeInfo.Memory.Total). + Uint64("used", nodeInfo.Memory.Used). + Msg("Available memory field missing, using traditional used memory") + } + + modelNode.Memory = models.Memory{ + Total: int64(nodeInfo.Memory.Total), + Used: int64(actualUsed), + Free: int64(nodeInfo.Memory.Total - actualUsed), + Usage: safePercentage(float64(actualUsed), float64(nodeInfo.Memory.Total)), + } + } + if nodeInfo.CPUInfo != nil { // Use MaxCPU from node data for logical CPU count (includes hyperthreading) // If MaxCPU is not available or 0, fall back to physical cores diff --git a/pkg/proxmox/client.go b/pkg/proxmox/client.go index cf67ebf3d..8df0c2a38 100644 --- a/pkg/proxmox/client.go +++ b/pkg/proxmox/client.go @@ -349,9 +349,10 @@ type NodeStatus struct { // MemoryStatus represents real-time memory information type MemoryStatus struct { - Total uint64 `json:"total"` - Used uint64 `json:"used"` - Free uint64 `json:"free"` + Total uint64 `json:"total"` + Used uint64 `json:"used"` + Free uint64 `json:"free"` + Available uint64 `json:"available"` // Memory available for allocation (excludes non-reclaimable cache) } // SwapStatus represents swap information