refactor(monitor): encapsulate engine state, add graceful shutdown and tests
Replace all monitor package-level mutable state with Engine struct. All state (liveState, logStore, histories, tokenIndex, HTTP clients) is now encapsulated in Engine, created via NewEngine(store). Key changes: - Engine struct holds all monitor state with proper mutex protection - Engine.Start(ctx) and monitorRoutine respect context cancellation for graceful shutdown — no more leaked goroutines - cluster.runFollowerLoop also respects context for clean exit - Token index (map[string]int) for O(1) push heartbeat lookup, replacing O(n) linear scan through LiveState - UpdateSiteConfig preserves 8 runtime fields instead of copying 17 config fields individually - triggerAlert goroutines get 30s timeout context - All consumers (TUI, server, cluster, main) receive *Engine via constructor/parameter — no package-level state access - main.go creates context.WithCancel, passes to engine and cluster First test suite: 12 tests across store and alert packages - Store: CRUD for sites/alerts/users, push token generation, import/export round-trip, check history persistence - Alert: Discord/Slack/Webhook payload format, HTTP 4xx error propagation, Ntfy headers, unknown provider returns nil
This commit is contained in:
+22
-33
@@ -1,10 +1,6 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"go-upkeep/internal/store"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
import "time"
|
||||
|
||||
const maxHistoryLen = 30
|
||||
|
||||
@@ -15,19 +11,14 @@ type SiteHistory struct {
|
||||
UpChecks int
|
||||
}
|
||||
|
||||
var (
|
||||
histories = make(map[int]*SiteHistory)
|
||||
historyMu sync.RWMutex
|
||||
)
|
||||
|
||||
func InitHistoryFromStore(s store.Store) {
|
||||
all, err := s.LoadAllHistory(maxHistoryLen)
|
||||
func (e *Engine) InitHistory() {
|
||||
all, err := e.db.LoadAllHistory(maxHistoryLen)
|
||||
if err != nil {
|
||||
AddLog("Failed to load check history: " + err.Error())
|
||||
e.AddLog("Failed to load check history: " + err.Error())
|
||||
return
|
||||
}
|
||||
historyMu.Lock()
|
||||
defer historyMu.Unlock()
|
||||
e.histMu.Lock()
|
||||
defer e.histMu.Unlock()
|
||||
for siteID, records := range all {
|
||||
h := &SiteHistory{}
|
||||
for _, r := range records {
|
||||
@@ -38,21 +29,21 @@ func InitHistoryFromStore(s store.Store) {
|
||||
h.Latencies = append(h.Latencies, time.Duration(r.LatencyNs))
|
||||
h.Statuses = append(h.Statuses, r.IsUp)
|
||||
}
|
||||
histories[siteID] = h
|
||||
e.histories[siteID] = h
|
||||
}
|
||||
if len(all) > 0 {
|
||||
AddLog("Loaded check history from database")
|
||||
e.AddLog("Loaded check history from database")
|
||||
}
|
||||
}
|
||||
|
||||
func RecordCheck(siteID int, latency time.Duration, isUp bool) {
|
||||
historyMu.Lock()
|
||||
defer historyMu.Unlock()
|
||||
func (e *Engine) recordCheck(siteID int, latency time.Duration, isUp bool) {
|
||||
e.histMu.Lock()
|
||||
defer e.histMu.Unlock()
|
||||
|
||||
h, ok := histories[siteID]
|
||||
h, ok := e.histories[siteID]
|
||||
if !ok {
|
||||
h = &SiteHistory{}
|
||||
histories[siteID] = h
|
||||
e.histories[siteID] = h
|
||||
}
|
||||
|
||||
h.TotalChecks++
|
||||
@@ -70,15 +61,13 @@ func RecordCheck(siteID int, latency time.Duration, isUp bool) {
|
||||
h.Statuses = h.Statuses[len(h.Statuses)-maxHistoryLen:]
|
||||
}
|
||||
|
||||
if db != nil {
|
||||
go func() { _ = db.SaveCheck(siteID, latency.Nanoseconds(), isUp) }()
|
||||
}
|
||||
go func() { _ = e.db.SaveCheck(siteID, latency.Nanoseconds(), isUp) }()
|
||||
}
|
||||
|
||||
func GetHistory(siteID int) (SiteHistory, bool) {
|
||||
historyMu.RLock()
|
||||
defer historyMu.RUnlock()
|
||||
h, ok := histories[siteID]
|
||||
func (e *Engine) GetHistory(siteID int) (SiteHistory, bool) {
|
||||
e.histMu.RLock()
|
||||
defer e.histMu.RUnlock()
|
||||
h, ok := e.histories[siteID]
|
||||
if !ok {
|
||||
return SiteHistory{}, false
|
||||
}
|
||||
@@ -93,8 +82,8 @@ func GetHistory(siteID int) (SiteHistory, bool) {
|
||||
return cp, true
|
||||
}
|
||||
|
||||
func RemoveHistory(siteID int) {
|
||||
historyMu.Lock()
|
||||
defer historyMu.Unlock()
|
||||
delete(histories, siteID)
|
||||
func (e *Engine) removeHistory(siteID int) {
|
||||
e.histMu.Lock()
|
||||
defer e.histMu.Unlock()
|
||||
delete(e.histories, siteID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user