prometheus/rules/store.go
Mustafain Ali Khan 1ae0a35e33 Store alert state using file storage
Signed-off-by: Mustafain Ali Khan <mustalik@amazon.com>
2024-12-20 09:34:57 -08:00

102 lines
2.4 KiB
Go

package rules
import (
"encoding/json"
"log/slog"
"os"
"sync"
)
// AlertStore provides persistent storage of alert state.
type AlertStore interface {
// SetAlerts stores the provided list of alerts for a rule.
SetAlerts(key uint64, alerts []*Alert) error
// GetAlerts returns a list of alerts for each alerting rule,
// alerting rule is identified by a fingerprint of its config.
GetAlerts(key uint64) (map[uint64]*Alert, error)
}
// FileStore implements the AlertStore interface.
type FileStore struct {
logger *slog.Logger
alertsByRule map[uint64][]*Alert
//protects the `alertsByRule` map
stateMtx sync.RWMutex
path string
}
func NewFileStore(l *slog.Logger, storagePath string) *FileStore {
s := &FileStore{
logger: l,
alertsByRule: make(map[uint64][]*Alert),
path: storagePath,
}
s.initState()
return s
}
// initState reads the state from file storage into the alertsByRule map
func (s *FileStore) initState() {
file, err := os.OpenFile(s.path, os.O_RDWR|os.O_CREATE, 0o666)
if err != nil {
s.logger.Error("Failed reading alerts state from file", "err", err)
return
}
defer file.Close()
var alertsByRule map[uint64][]*Alert
err = json.NewDecoder(file).Decode(&alertsByRule)
if err != nil {
s.logger.Error("Failed reading alerts state from file", "err", err)
}
if alertsByRule == nil {
alertsByRule = make(map[uint64][]*Alert)
}
s.alertsByRule = alertsByRule
}
// GetAlerts returns the stored alerts for an alerting rule
// Alert state is read from the in memory map which is populated during initialization
func (s *FileStore) GetAlerts(key uint64) (map[uint64]*Alert, error) {
s.stateMtx.RLock()
defer s.stateMtx.RUnlock()
restoredAlerts, ok := s.alertsByRule[key]
if !ok {
return nil, nil
}
alerts := make(map[uint64]*Alert)
for _, alert := range restoredAlerts {
if alert == nil {
continue
}
h := alert.Labels.Hash()
alerts[h] = alert
}
return alerts, nil
}
// SetAlerts updates the stateByRule map and writes state to file storage
func (s *FileStore) SetAlerts(key uint64, alerts []*Alert) error {
s.stateMtx.Lock()
defer s.stateMtx.Unlock()
// Update in memory
if alerts != nil {
s.alertsByRule[key] = alerts
}
// flush in memory state to file storage
file, err := os.Create(s.path)
if err != nil {
return err
}
defer file.Close()
encoder := json.NewEncoder(file)
err = encoder.Encode(s.alertsByRule)
if err != nil {
return err
}
return nil
}