a284a64816
caching of monitoring summary data on th server side
78 lines
2.2 KiB
Go
78 lines
2.2 KiB
Go
package monitor
|
|
|
|
import "context"
|
|
import "sync"
|
|
import "time"
|
|
|
|
import "codit/internal/db"
|
|
|
|
const summaryRefreshInterval time.Duration = 30 * time.Second
|
|
const summaryTTL time.Duration = 90 * time.Second
|
|
|
|
// MonitorSummary holds the expensive derived per-monitor field: uptime over the
|
|
// 24h/30d windows, keyed by public id as [24h, 30d]. (Recent-beats bars were
|
|
// dropped from the list payload; the detail view fetches full checks instead.)
|
|
type MonitorSummary struct {
|
|
Uptime map[string][2]float64
|
|
}
|
|
|
|
// SummaryCache serves the derived monitor summary on a slower cadence than the
|
|
// list is polled: a background loop refreshes it every summaryRefreshInterval
|
|
// (proactive), and a read that finds it older than summaryTTL triggers a
|
|
// single-flight refresh (lazy safety net). Fresh per-request fields (state,
|
|
// latency) are NOT here — only the costly aggregates.
|
|
type SummaryCache struct {
|
|
store *db.Store
|
|
ttl time.Duration
|
|
mu sync.RWMutex
|
|
snap MonitorSummary
|
|
computedAt time.Time
|
|
refreshMu sync.Mutex
|
|
}
|
|
|
|
func NewSummaryCache(store *db.Store, ttl time.Duration) *SummaryCache {
|
|
return &SummaryCache{store: store, ttl: ttl}
|
|
}
|
|
|
|
// Refresh recomputes the summary from the store and replaces the snapshot.
|
|
func (c *SummaryCache) Refresh(ctx context.Context) error {
|
|
var now int64
|
|
var uptime map[string][2]float64
|
|
var err error
|
|
|
|
now = time.Now().UTC().Unix()
|
|
uptime, err = c.store.MonitorUptimeAll(now)
|
|
if err != nil { return err }
|
|
|
|
c.mu.Lock()
|
|
c.snap = MonitorSummary{Uptime: uptime}
|
|
c.computedAt = time.Now()
|
|
c.mu.Unlock()
|
|
return nil
|
|
}
|
|
|
|
// Snapshot returns the cached summary, refreshing synchronously (single-flight)
|
|
// when the cache is empty or older than the TTL. If another goroutine is already
|
|
// refreshing, the current (possibly slightly stale) snapshot is returned.
|
|
func (c *SummaryCache) Snapshot(ctx context.Context) MonitorSummary {
|
|
var snap MonitorSummary
|
|
var age time.Duration
|
|
|
|
c.mu.RLock()
|
|
snap = c.snap
|
|
age = time.Since(c.computedAt)
|
|
c.mu.RUnlock()
|
|
|
|
if snap.Uptime != nil && age <= c.ttl {
|
|
return snap
|
|
}
|
|
if c.refreshMu.TryLock() {
|
|
_ = c.Refresh(ctx)
|
|
c.refreshMu.Unlock()
|
|
c.mu.RLock()
|
|
snap = c.snap
|
|
c.mu.RUnlock()
|
|
}
|
|
return snap
|
|
}
|