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)
This commit is contained in:
Pulse Monitor
2025-09-09 21:35:09 +00:00
parent 4f45238f9c
commit 8fbe53406a
2 changed files with 37 additions and 3 deletions
+33
View File
@@ -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
+4 -3
View File
@@ -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