change to atomic.bool for fetching details / smart

This commit is contained in:
henrygd
2025-12-18 15:02:59 -05:00
parent 8627e3ee97
commit 330d375997

View File

@@ -9,7 +9,7 @@ import (
"math/rand" "math/rand"
"net" "net"
"strings" "strings"
"sync" "sync/atomic"
"time" "time"
"github.com/henrygd/beszel/internal/common" "github.com/henrygd/beszel/internal/common"
@@ -29,20 +29,21 @@ import (
) )
type System struct { type System struct {
Id string `db:"id"` Id string `db:"id"`
Host string `db:"host"` Host string `db:"host"`
Port string `db:"port"` Port string `db:"port"`
Status string `db:"status"` Status string `db:"status"`
manager *SystemManager // Manager that this system belongs to manager *SystemManager // Manager that this system belongs to
client *ssh.Client // SSH client for fetching data client *ssh.Client // SSH client for fetching data
data *system.CombinedData // system data from agent data *system.CombinedData // system data from agent
ctx context.Context // Context for stopping the updater ctx context.Context // Context for stopping the updater
cancel context.CancelFunc // Stops and removes system from updater cancel context.CancelFunc // Stops and removes system from updater
WsConn *ws.WsConn // Handler for agent WebSocket connection WsConn *ws.WsConn // Handler for agent WebSocket connection
agentVersion semver.Version // Agent version agentVersion semver.Version // Agent version
updateTicker *time.Ticker // Ticker for updating the system updateTicker *time.Ticker // Ticker for updating the system
smartOnce sync.Once // Once for fetching and saving smart devices detailsFetched atomic.Bool // True if static system details have been fetched and saved
detailsOnce sync.Once // Once for fetching and saving static system details 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 { func (sm *SystemManager) NewSystem(systemId string) *System {
@@ -118,10 +119,10 @@ func (sys *System) update() error {
options := common.DataRequestOptions{ options := common.DataRequestOptions{
CacheTimeMs: uint16(interval), CacheTimeMs: uint16(interval),
} }
// fetch system details only on the first update // fetch system details if not already fetched
sys.detailsOnce.Do(func() { if !sys.detailsFetched.Load() {
options.IncludeDetails = true options.IncludeDetails = true
}) }
data, err := sys.fetchDataFromAgent(options) data, err := sys.fetchDataFromAgent(options)
if err == nil { if err == nil {
_, err = sys.createRecords(data) _, err = sys.createRecords(data)
@@ -150,17 +151,11 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
} }
hub := sys.manager.hub hub := sys.manager.hub
err = hub.RunInTransaction(func(txApp core.App) error { err = hub.RunInTransaction(func(txApp core.App) error {
if data.Details != nil { // add system_stats record
if err := createSystemDetailsRecord(txApp, data.Details, sys.Id); err != nil {
return err
}
}
// add system_stats and container_stats records
systemStatsCollection, err := txApp.FindCachedCollectionByNameOrId("system_stats") systemStatsCollection, err := txApp.FindCachedCollectionByNameOrId("system_stats")
if err != nil { if err != nil {
return err return err
} }
systemStatsRecord := core.NewRecord(systemStatsCollection) systemStatsRecord := core.NewRecord(systemStatsCollection)
systemStatsRecord.Set("system", systemRecord.Id) systemStatsRecord.Set("system", systemRecord.Id)
systemStatsRecord.Set("stats", data.Stats) 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 { if err := txApp.SaveNoValidate(systemStatsRecord); err != nil {
return err return err
} }
// add containers and container_stats records
if len(data.Containers) > 0 { if len(data.Containers) > 0 {
// add / update containers records
if data.Containers[0].Id != "" { if data.Containers[0].Id != "" {
if err := createContainerRecords(txApp, data.Containers, sys.Id); err != nil { if err := createContainerRecords(txApp, data.Containers, sys.Id); err != nil {
return err return err
} }
} }
// add new container_stats record
containerStatsCollection, err := txApp.FindCachedCollectionByNameOrId("container_stats") containerStatsCollection, err := txApp.FindCachedCollectionByNameOrId("container_stats")
if err != nil { if err != nil {
return err 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) // 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("status", up)
systemRecord.Set("info", data.Info) systemRecord.Set("info", data.Info)
if err := txApp.SaveNoValidate(systemRecord); err != nil { if err := txApp.SaveNoValidate(systemRecord); err != nil {
return err 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 // Fetch and save SMART devices when system first comes online
if err == nil { if err == nil {
sys.smartOnce.Do(func() { if !sys.smartFetched.Load() && sys.smartFetching.CompareAndSwap(false, true) {
go sys.FetchAndSaveSmartDevices() go func() {
}) defer sys.smartFetching.Store(false)
if err := sys.FetchAndSaveSmartDevices(); err == nil {
sys.smartFetched.Store(true)
}
}()
}
} }
return systemRecord, err return systemRecord, err
} }
func createSystemDetailsRecord(app core.App, data *system.Details, systemId string) error { func createSystemDetailsRecord(app core.App, data *system.Details, systemId string) error {
collectionName := "system_details"
params := dbx.Params{ params := dbx.Params{
"id": systemId, "id": systemId,
"system": systemId, "system": systemId,
@@ -232,12 +240,11 @@ func createSystemDetailsRecord(app core.App, data *system.Details, systemId stri
"podman": data.Podman, "podman": data.Podman,
"updated": time.Now().UTC(), "updated": time.Now().UTC(),
} }
queryString := `INSERT INTO system_details (id, system, hostname, kernel, cores, threads, cpu, os, os_name, arch, memory, podman, updated) result, err := app.DB().Update(collectionName, params, dbx.HashExp{"id": systemId}).Execute()
VALUES ({:id}, {:system}, {:hostname}, {:kernel}, {:cores}, {:threads}, {:cpu}, {:os}, {:os_name}, {:arch}, {:memory}, {:podman}, {:updated}) rowsAffected, _ := result.RowsAffected()
ON CONFLICT(id) DO UPDATE SET system = excluded.system, hostname = excluded.hostname, kernel = excluded.kernel, cores = excluded.cores, if err != nil || rowsAffected == 0 {
threads = excluded.threads, cpu = excluded.cpu, os = excluded.os, os_name = excluded.os_name, arch = excluded.arch, _, err = app.DB().Insert(collectionName, params).Execute()
memory = excluded.memory, podman = excluded.podman, updated = excluded.updated` }
_, err := app.DB().NewQuery(queryString).Bind(params).Execute()
return err return err
} }