From fe66af273e50187d07b60a71998c1185748dfc0f Mon Sep 17 00:00:00 2001 From: rcourtman Date: Fri, 27 Mar 2026 14:02:58 +0000 Subject: [PATCH] Use freemem when Proxmox VM status memory disagrees (#1319) --- internal/monitoring/vm_memory_selection.go | 13 +++++++++--- .../monitoring/vm_memory_selection_test.go | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/internal/monitoring/vm_memory_selection.go b/internal/monitoring/vm_memory_selection.go index ad055ca41..5730a00d9 100644 --- a/internal/monitoring/vm_memory_selection.go +++ b/internal/monitoring/vm_memory_selection.go @@ -6,7 +6,8 @@ import ( "github.com/rcourtman/pulse-go-rewrite/pkg/proxmox" ) -const vmMemInfoGapTolerance uint64 = 16 * 1024 * 1024 // 16 MiB +const vmMemInfoGapTolerance uint64 = 16 * 1024 * 1024 // 16 MiB +const vmStatusMemoryMismatchTolerance uint64 = 128 * 1024 * 1024 // 128 MiB type vmMemAvailableSelection struct { Available uint64 @@ -102,8 +103,14 @@ func selectVMLowTrustUsedMemory(memTotal uint64, status *proxmox.VMStatus) vmLow } if status.Mem > 0 { - if status.Mem >= memTotal && hasFreeFallback && freeDerivedUsed < memTotal { - return vmLowTrustUsedSelection{Used: freeDerivedUsed, Source: "status-freemem"} + if hasFreeFallback && freeDerivedUsed < status.Mem { + statusMemPlusFree := saturatingAddUint64(status.Mem, status.FreeMem) + if status.Mem >= memTotal && freeDerivedUsed < memTotal { + return vmLowTrustUsedSelection{Used: freeDerivedUsed, Source: "status-freemem"} + } + if statusMemPlusFree > memTotal+vmStatusMemoryMismatchTolerance { + return vmLowTrustUsedSelection{Used: freeDerivedUsed, Source: "status-freemem"} + } } return vmLowTrustUsedSelection{Used: status.Mem, Source: "status-mem"} } diff --git a/internal/monitoring/vm_memory_selection_test.go b/internal/monitoring/vm_memory_selection_test.go index e1db16f8f..5021122cb 100644 --- a/internal/monitoring/vm_memory_selection_test.go +++ b/internal/monitoring/vm_memory_selection_test.go @@ -132,6 +132,16 @@ func TestSelectVMLowTrustUsedMemory(t *testing.T) { wantUsed: 3 * giB, wantSource: "status-freemem", }, + { + name: "falls back to status freemem when status mem and freemem are materially inconsistent", + memTotal: 8 * giB, + status: &proxmox.VMStatus{ + Mem: 7920 * 1024 * 1024, + FreeMem: 5 * giB, + }, + wantUsed: 3 * giB, + wantSource: "status-freemem", + }, { name: "uses status freemem when status mem is absent", memTotal: 8 * giB, @@ -148,6 +158,16 @@ func TestSelectVMLowTrustUsedMemory(t *testing.T) { wantUsed: 0, wantSource: "", }, + { + name: "keeps status mem when freemem only shows tiny headroom", + memTotal: 8 * giB, + status: &proxmox.VMStatus{ + Mem: 8*giB - (64 * 1024 * 1024), + FreeMem: 64 * 1024 * 1024, + }, + wantUsed: 8*giB - (64 * 1024 * 1024), + wantSource: "status-mem", + }, } for _, tt := range tests {