Add infrastructure reliability monitoring
This commit is contained in:
47
store.go
47
store.go
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
@@ -58,7 +59,13 @@ type HistoryPoint struct {
|
||||
GuestsMemory float64 `json:"guestsMemory"`
|
||||
}
|
||||
|
||||
type Store struct{ db *sql.DB }
|
||||
type Store struct {
|
||||
db *sql.DB
|
||||
path string
|
||||
healthMu sync.RWMutex
|
||||
lastHistoryAt time.Time
|
||||
lastHistoryError string
|
||||
}
|
||||
|
||||
type EmailSettings struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
@@ -130,7 +137,7 @@ func openStore(path string) (*Store, error) {
|
||||
_, _ = db.Exec(`ALTER TABLE alert_events ADD COLUMN recovery_sent_at INTEGER`)
|
||||
_, _ = db.Exec(`ALTER TABLE service_checks ADD COLUMN endpoint TEXT NOT NULL DEFAULT 'public'`)
|
||||
_, _ = db.Exec(`PRAGMA foreign_keys=ON`)
|
||||
return &Store{db: db}, nil
|
||||
return &Store{db: db, path: path}, nil
|
||||
}
|
||||
|
||||
type EntityHistoryPoint struct {
|
||||
@@ -285,9 +292,45 @@ func (s *Store) AddHistory(point HistoryPoint) error {
|
||||
_, err := s.db.Exec(`INSERT OR REPLACE INTO history(ts,cpu_usage,cpu_temp,memory_usage,swap_usage,root_usage,network_rx,network_tx,ups_charge,ups_load,zfs_usage,guests_cpu,guests_memory) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
||||
point.Timestamp, point.CPUUsage, point.CPUTemperature, point.MemoryUsage, point.SwapUsage, point.RootUsage, point.NetworkReceive, point.NetworkTransmit, point.UPSCharge, point.UPSLoad, point.ZFSUsage, point.GuestsCPU, point.GuestsMemory)
|
||||
if err != nil {
|
||||
s.setHistoryHealth(time.Time{}, err)
|
||||
return err
|
||||
}
|
||||
_, err = s.db.Exec(`DELETE FROM history WHERE ts < ?`, time.Now().Add(-historyRetention).Unix())
|
||||
s.setHistoryHealth(time.Now(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) setHistoryHealth(at time.Time, err error) {
|
||||
s.healthMu.Lock()
|
||||
defer s.healthMu.Unlock()
|
||||
if !at.IsZero() {
|
||||
s.lastHistoryAt = at
|
||||
}
|
||||
if err != nil {
|
||||
s.lastHistoryError = err.Error()
|
||||
} else {
|
||||
s.lastHistoryError = ""
|
||||
}
|
||||
}
|
||||
func (s *Store) historyHealth() (time.Time, string) {
|
||||
s.healthMu.RLock()
|
||||
defer s.healthMu.RUnlock()
|
||||
return s.lastHistoryAt, s.lastHistoryError
|
||||
}
|
||||
|
||||
func (s *Store) loadJSONSetting(key string, target any) bool {
|
||||
var value string
|
||||
if s.db.QueryRow(`SELECT value FROM settings WHERE key=?`, key).Scan(&value) != nil {
|
||||
return false
|
||||
}
|
||||
return json.Unmarshal([]byte(value), target) == nil
|
||||
}
|
||||
func (s *Store) saveJSONSetting(key string, value any) error {
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = s.db.Exec(`INSERT INTO settings(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value`, key, string(data))
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user