From 330d375997c5defc9ea5e594b92009d9951e7c21 Mon Sep 17 00:00:00 2001 From: henrygd Date: Thu, 18 Dec 2025 15:02:59 -0500 Subject: [PATCH] change to atomic.bool for fetching details / smart --- internal/hub/systems/system.go | 81 ++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/internal/hub/systems/system.go b/internal/hub/systems/system.go index 10677ff2..ae2d0c08 100644 --- a/internal/hub/systems/system.go +++ b/internal/hub/systems/system.go @@ -9,7 +9,7 @@ import ( "math/rand" "net" "strings" - "sync" + "sync/atomic" "time" "github.com/henrygd/beszel/internal/common" @@ -29,20 +29,21 @@ import ( ) type System struct { - Id string `db:"id"` - Host string `db:"host"` - Port string `db:"port"` - Status string `db:"status"` - manager *SystemManager // Manager that this system belongs to - client *ssh.Client // SSH client for fetching data - data *system.CombinedData // system data from agent - ctx context.Context // Context for stopping the updater - cancel context.CancelFunc // Stops and removes system from updater - WsConn *ws.WsConn // Handler for agent WebSocket connection - agentVersion semver.Version // Agent version - updateTicker *time.Ticker // Ticker for updating the system - smartOnce sync.Once // Once for fetching and saving smart devices - detailsOnce sync.Once // Once for fetching and saving static system details + Id string `db:"id"` + Host string `db:"host"` + Port string `db:"port"` + Status string `db:"status"` + manager *SystemManager // Manager that this system belongs to + client *ssh.Client // SSH client for fetching data + data *system.CombinedData // system data from agent + ctx context.Context // Context for stopping the updater + cancel context.CancelFunc // Stops and removes system from updater + WsConn *ws.WsConn // Handler for agent WebSocket connection + 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 } func (sm *SystemManager) NewSystem(systemId string) *System { @@ -118,10 +119,10 @@ func (sys *System) update() error { options := common.DataRequestOptions{ CacheTimeMs: uint16(interval), } - // fetch system details only on the first update - sys.detailsOnce.Do(func() { + // fetch system details if not already fetched + if !sys.detailsFetched.Load() { options.IncludeDetails = true - }) + } data, err := sys.fetchDataFromAgent(options) if err == nil { _, err = sys.createRecords(data) @@ -150,17 +151,11 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error } hub := sys.manager.hub err = hub.RunInTransaction(func(txApp core.App) error { - if data.Details != nil { - if err := createSystemDetailsRecord(txApp, data.Details, sys.Id); err != nil { - return err - } - } - // add system_stats and container_stats records + // add system_stats record systemStatsCollection, err := txApp.FindCachedCollectionByNameOrId("system_stats") if err != nil { return err } - systemStatsRecord := core.NewRecord(systemStatsCollection) systemStatsRecord.Set("system", systemRecord.Id) systemStatsRecord.Set("stats", data.Stats) @@ -168,14 +163,14 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error if err := txApp.SaveNoValidate(systemStatsRecord); err != nil { return err } + + // add containers and container_stats records if len(data.Containers) > 0 { - // add / update containers records if data.Containers[0].Id != "" { if err := createContainerRecords(txApp, data.Containers, sys.Id); err != nil { return err } } - // add new container_stats record containerStatsCollection, err := txApp.FindCachedCollectionByNameOrId("container_stats") if err != nil { return err @@ -196,9 +191,16 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error } } + // add system details record + if data.Details != nil { + if err := createSystemDetailsRecord(txApp, data.Details, sys.Id); err != nil { + return err + } + sys.detailsFetched.Store(true) + } + // update system record (do this last because it triggers alerts and we need above records to be inserted first) systemRecord.Set("status", up) - systemRecord.Set("info", data.Info) if err := txApp.SaveNoValidate(systemRecord); err != nil { return err @@ -208,15 +210,21 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error // Fetch and save SMART devices when system first comes online if err == nil { - sys.smartOnce.Do(func() { - go sys.FetchAndSaveSmartDevices() - }) + if !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) + } + }() + } } return systemRecord, err } func createSystemDetailsRecord(app core.App, data *system.Details, systemId string) error { + collectionName := "system_details" params := dbx.Params{ "id": systemId, "system": systemId, @@ -232,12 +240,11 @@ func createSystemDetailsRecord(app core.App, data *system.Details, systemId stri "podman": data.Podman, "updated": time.Now().UTC(), } - queryString := `INSERT INTO system_details (id, system, hostname, kernel, cores, threads, cpu, os, os_name, arch, memory, podman, updated) - VALUES ({:id}, {:system}, {:hostname}, {:kernel}, {:cores}, {:threads}, {:cpu}, {:os}, {:os_name}, {:arch}, {:memory}, {:podman}, {:updated}) - ON CONFLICT(id) DO UPDATE SET system = excluded.system, hostname = excluded.hostname, kernel = excluded.kernel, cores = excluded.cores, - threads = excluded.threads, cpu = excluded.cpu, os = excluded.os, os_name = excluded.os_name, arch = excluded.arch, - memory = excluded.memory, podman = excluded.podman, updated = excluded.updated` - _, err := app.DB().NewQuery(queryString).Bind(params).Execute() + result, err := app.DB().Update(collectionName, params, dbx.HashExp{"id": systemId}).Execute() + rowsAffected, _ := result.RowsAffected() + if err != nil || rowsAffected == 0 { + _, err = app.DB().Insert(collectionName, params).Execute() + } return err }