From a9068a11a964b71ad4bc201af283d9140e3cb3df Mon Sep 17 00:00:00 2001 From: henrygd Date: Fri, 19 Dec 2025 16:14:31 -0500 Subject: [PATCH] add SMART_INTERVAL env var with background smart data fetching --- agent/agent.go | 11 +++++++++++ internal/entities/system/system.go | 21 +++++++++++---------- internal/hub/systems/system.go | 29 ++++++++++++++++++++--------- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index a8d0482d..c6af9102 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -84,6 +84,7 @@ func NewAgent(dataDir ...string) (agent *Agent, err error) { slog.Warn("Invalid DISK_USAGE_CACHE", "err", err) } } + // Set up slog with a log level determined by the LOG_LEVEL env var if logLevelStr, exists := GetEnv("LOG_LEVEL"); exists { switch strings.ToLower(logLevelStr) { @@ -105,6 +106,16 @@ func NewAgent(dataDir ...string) (agent *Agent, err error) { // initialize system info agent.refreshSystemDetails() + // SMART_INTERVAL env var to update smart data at this interval + if smartIntervalEnv, exists := GetEnv("SMART_INTERVAL"); exists { + if duration, err := time.ParseDuration(smartIntervalEnv); err == nil && duration > 0 { + agent.systemDetails.SmartInterval = duration + slog.Info("SMART_INTERVAL", "duration", duration) + } else { + slog.Warn("Invalid SMART_INTERVAL", "err", err) + } + } + // initialize connection manager agent.connectionManager = newConnectionManager(agent) diff --git a/internal/entities/system/system.go b/internal/entities/system/system.go index 1c7b9679..3ccd9cb4 100644 --- a/internal/entities/system/system.go +++ b/internal/entities/system/system.go @@ -155,16 +155,17 @@ type Info struct { // Data that does not change during process lifetime and is not needed in All Systems table type Details struct { - Hostname string `cbor:"0,keyasint"` - Kernel string `cbor:"1,keyasint,omitempty"` - Cores int `cbor:"2,keyasint"` - Threads int `cbor:"3,keyasint"` - CpuModel string `cbor:"4,keyasint"` - Os Os `cbor:"5,keyasint"` - OsName string `cbor:"6,keyasint"` - Arch string `cbor:"7,keyasint"` - Podman bool `cbor:"8,keyasint,omitempty"` - MemoryTotal uint64 `cbor:"9,keyasint"` + Hostname string `cbor:"0,keyasint"` + Kernel string `cbor:"1,keyasint,omitempty"` + Cores int `cbor:"2,keyasint"` + Threads int `cbor:"3,keyasint"` + CpuModel string `cbor:"4,keyasint"` + Os Os `cbor:"5,keyasint"` + OsName string `cbor:"6,keyasint"` + Arch string `cbor:"7,keyasint"` + Podman bool `cbor:"8,keyasint,omitempty"` + MemoryTotal uint64 `cbor:"9,keyasint"` + SmartInterval time.Duration `cbor:"10,keyasint,omitempty"` } // Final data structure to return to the hub diff --git a/internal/hub/systems/system.go b/internal/hub/systems/system.go index 265286de..a5ba1243 100644 --- a/internal/hub/systems/system.go +++ b/internal/hub/systems/system.go @@ -42,8 +42,9 @@ type System struct { agentVersion semver.Version // Agent version updateTicker *time.Ticker // Ticker for updating the system detailsFetched atomic.Bool // True if static system details have been fetched and saved - smartFetched atomic.Bool // True if SMART devices 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 { @@ -132,14 +133,20 @@ func (sys *System) update() error { // create system records _, err = sys.createRecords(data) - // Fetch and save SMART devices when system first comes online - if backgroundSmartFetchEnabled() && !sys.smartFetched.Load() && sys.smartFetching.CompareAndSwap(false, true) { - go func() { - defer sys.smartFetching.Store(false) - if err := sys.FetchAndSaveSmartDevices(); err == nil { - sys.smartFetched.Store(true) - } - }() + // Fetch and save SMART devices when system first comes online or at intervals + if backgroundSmartFetchEnabled() { + if sys.smartInterval <= 0 { + sys.smartInterval = time.Hour + } + lastFetch := sys.lastSmartFetch.Load() + if time.Since(time.UnixMilli(lastFetch)) >= sys.smartInterval && sys.smartFetching.CompareAndSwap(false, true) { + go func() { + defer sys.smartFetching.Store(false) + // Throttle retries even on failure. + sys.lastSmartFetch.Store(time.Now().UnixMilli()) + _ = sys.FetchAndSaveSmartDevices() + }() + } } return err @@ -212,6 +219,10 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error return err } sys.detailsFetched.Store(true) + // update smart interval if it's set on the agent side + if data.Details.SmartInterval > 0 { + sys.smartInterval = data.Details.SmartInterval + } } // update system record (do this last because it triggers alerts and we need above records to be inserted first)