2024-01-22 06:07:11 -06:00
|
|
|
package state
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/benbjohnson/clock"
|
|
|
|
"go.opentelemetry.io/otel/trace"
|
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
2024-01-23 10:03:30 -06:00
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
2024-01-22 06:07:11 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
type AsyncStatePersister struct {
|
|
|
|
log log.Logger
|
|
|
|
// doNotSaveNormalState controls whether eval.Normal state is persisted to the database and returned by get methods.
|
|
|
|
doNotSaveNormalState bool
|
|
|
|
store InstanceStore
|
2024-01-23 10:03:30 -06:00
|
|
|
ticker *clock.Ticker
|
|
|
|
metrics *metrics.State
|
2024-01-22 06:07:11 -06:00
|
|
|
}
|
|
|
|
|
2024-01-23 10:03:30 -06:00
|
|
|
func NewAsyncStatePersister(log log.Logger, ticker *clock.Ticker, cfg ManagerCfg) StatePersister {
|
2024-01-22 06:07:11 -06:00
|
|
|
return &AsyncStatePersister{
|
|
|
|
log: log,
|
|
|
|
store: cfg.InstanceStore,
|
2024-01-23 10:03:30 -06:00
|
|
|
ticker: ticker,
|
2024-01-22 06:07:11 -06:00
|
|
|
doNotSaveNormalState: cfg.DoNotSaveNormalState,
|
2024-01-23 10:03:30 -06:00
|
|
|
metrics: cfg.Metrics,
|
2024-01-22 06:07:11 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-23 10:03:30 -06:00
|
|
|
func (a *AsyncStatePersister) Async(ctx context.Context, cache *cache) {
|
2024-01-22 06:07:11 -06:00
|
|
|
for {
|
|
|
|
select {
|
2024-01-23 10:03:30 -06:00
|
|
|
case <-a.ticker.C:
|
2024-01-22 06:07:11 -06:00
|
|
|
if err := a.fullSync(ctx, cache); err != nil {
|
|
|
|
a.log.Error("Failed to do a full state sync to database", "err", err)
|
|
|
|
}
|
|
|
|
case <-ctx.Done():
|
|
|
|
a.log.Info("Scheduler is shutting down, doing a final state sync.")
|
|
|
|
if err := a.fullSync(context.Background(), cache); err != nil {
|
|
|
|
a.log.Error("Failed to do a full state sync to database", "err", err)
|
|
|
|
}
|
2024-01-23 10:03:30 -06:00
|
|
|
a.ticker.Stop()
|
2024-01-22 06:07:11 -06:00
|
|
|
a.log.Info("State async worker is shut down.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AsyncStatePersister) fullSync(ctx context.Context, cache *cache) error {
|
|
|
|
startTime := time.Now()
|
2024-01-23 10:03:30 -06:00
|
|
|
a.log.Debug("Full state sync start")
|
2024-01-22 06:07:11 -06:00
|
|
|
instances := cache.asInstances(a.doNotSaveNormalState)
|
|
|
|
if err := a.store.FullSync(ctx, instances); err != nil {
|
|
|
|
a.log.Error("Full state sync failed", "duration", time.Since(startTime), "instances", len(instances))
|
|
|
|
return err
|
|
|
|
}
|
2024-01-23 10:03:30 -06:00
|
|
|
a.log.Debug("Full state sync done", "duration", time.Since(startTime), "instances", len(instances))
|
|
|
|
if a.metrics != nil {
|
|
|
|
a.metrics.StateFullSyncDuration.Observe(time.Since(startTime).Seconds())
|
|
|
|
}
|
2024-01-22 06:07:11 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AsyncStatePersister) Sync(_ context.Context, _ trace.Span, _, _ []StateTransition) {
|
|
|
|
a.log.Debug("Sync: No-Op")
|
|
|
|
}
|