From 654cd06b1906e1bbfd52924a998a51686d990ddc Mon Sep 17 00:00:00 2001 From: henrygd Date: Sun, 8 Mar 2026 19:03:50 -0400 Subject: [PATCH] respect SMART_INTERVAL across agent reconnects (#1800) Move tracking of the last SMART data fetch from individual System instances to the SystemManager using a TTL-based ExpiryMap. This ensures that the SMART_INTERVAL is respected even if an agent connection is dropped and re-established, preventing redundant data collection on every reconnect. --- internal/hub/systems/system.go | 5 ++--- internal/hub/systems/system_manager.go | 13 ++++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/internal/hub/systems/system.go b/internal/hub/systems/system.go index 109db35a..31e2c11e 100644 --- a/internal/hub/systems/system.go +++ b/internal/hub/systems/system.go @@ -48,7 +48,6 @@ type System struct { detailsFetched atomic.Bool // True if static system details have been fetched and saved smartFetching atomic.Bool // True if SMART devices are currently being fetched smartInterval time.Duration // Interval for periodic SMART data updates - lastSmartFetch atomic.Int64 // Unix milliseconds of last SMART data fetch } func (sm *SystemManager) NewSystem(systemId string) *System { @@ -142,11 +141,11 @@ func (sys *System) update() error { if sys.smartInterval <= 0 { sys.smartInterval = time.Hour } - lastFetch := sys.lastSmartFetch.Load() + lastFetch, _ := sys.manager.smartFetchMap.GetOk(sys.Id) if time.Since(time.UnixMilli(lastFetch)) >= sys.smartInterval && sys.smartFetching.CompareAndSwap(false, true) { go func() { defer sys.smartFetching.Store(false) - sys.lastSmartFetch.Store(time.Now().UnixMilli()) + sys.manager.smartFetchMap.Set(sys.Id, time.Now().UnixMilli(), sys.smartInterval+time.Minute) _ = sys.FetchAndSaveSmartDevices() }() } diff --git a/internal/hub/systems/system_manager.go b/internal/hub/systems/system_manager.go index 9dbe4b14..ca94b52c 100644 --- a/internal/hub/systems/system_manager.go +++ b/internal/hub/systems/system_manager.go @@ -8,6 +8,7 @@ import ( "github.com/henrygd/beszel/internal/hub/ws" "github.com/henrygd/beszel/internal/entities/system" + "github.com/henrygd/beszel/internal/hub/expirymap" "github.com/henrygd/beszel/internal/common" @@ -40,9 +41,10 @@ var errSystemExists = errors.New("system exists") // SystemManager manages a collection of monitored systems and their connections. // It handles system lifecycle, status updates, and maintains both SSH and WebSocket connections. type SystemManager struct { - hub hubLike // Hub interface for database and alert operations - systems *store.Store[string, *System] // Thread-safe store of active systems - sshConfig *ssh.ClientConfig // SSH client configuration for system connections + hub hubLike // Hub interface for database and alert operations + systems *store.Store[string, *System] // Thread-safe store of active systems + sshConfig *ssh.ClientConfig // SSH client configuration for system connections + smartFetchMap *expirymap.ExpiryMap[int64] // Stores last SMART fetch time per system ID } // hubLike defines the interface requirements for the hub dependency. @@ -58,8 +60,9 @@ type hubLike interface { // The hub must implement the hubLike interface to provide database and alert functionality. func NewSystemManager(hub hubLike) *SystemManager { return &SystemManager{ - systems: store.New(map[string]*System{}), - hub: hub, + systems: store.New(map[string]*System{}), + hub: hub, + smartFetchMap: expirymap.New[int64](time.Hour), } }