[v11.1.x] Alerting: Fix persisting result fingerprint that is used by recovery threshold (#91290)

Alerting: Fix persisting result fingerprint that is used by recovery threshold (#91224)

(cherry picked from commit 537f1fb857)

# Conflicts:
#	pkg/services/ngalert/state/persister_sync.go
This commit is contained in:
Yuri Tseretyan
2024-07-31 11:16:37 -04:00
committed by GitHub
parent bb5d2c83ef
commit 738cc49ec5
3 changed files with 82 additions and 2 deletions
+6 -2
View File
@@ -92,10 +92,10 @@ func (c *cache) getOrCreate(ctx context.Context, log log.Logger, alertRule *ngMo
states = &ruleStates{states: make(map[string]*State)}
c.states[stateCandidate.OrgID][stateCandidate.AlertRuleUID] = states
}
return states.getOrAdd(stateCandidate)
return states.getOrAdd(stateCandidate, log)
}
func (rs *ruleStates) getOrAdd(stateCandidate State) *State {
func (rs *ruleStates) getOrAdd(stateCandidate State, log log.Logger) *State {
state, ok := rs.states[stateCandidate.CacheID]
// Check if the state with this ID already exists.
if !ok {
@@ -116,6 +116,10 @@ func (rs *ruleStates) getOrAdd(stateCandidate State) *State {
}
state.Annotations = stateCandidate.Annotations
state.Values = stateCandidate.Values
if state.ResultFingerprint != stateCandidate.ResultFingerprint {
log.Info("Result fingerprint has changed", "oldFingerprint", state.ResultFingerprint, "newFingerprint", stateCandidate.ResultFingerprint, "cacheID", state.CacheID, "stateLabels", state.Labels.String())
state.ResultFingerprint = stateCandidate.ResultFingerprint
}
rs.states[stateCandidate.CacheID] = state
return state
}
@@ -93,6 +93,7 @@ func (a *SyncStatePersister) saveAlertStates(ctx context.Context, states ...Stat
LastEvalTime: s.LastEvaluationTime,
CurrentStateSince: s.StartsAt,
CurrentStateEnd: s.EndsAt,
ResultFingerprint: s.ResultFingerprint.String(),
}
err = a.store.SaveAlertInstance(ctx, instance)
@@ -2,9 +2,13 @@ package state
import (
"context"
"errors"
"fmt"
"math/rand"
"testing"
"time"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/component-base/tracing"
@@ -100,4 +104,75 @@ func TestSyncPersister_saveAlertStates(t *testing.T) {
assert.Containsf(t, savedKeys, key, "state %s (%s) was not saved but should be", tr.State.State, tr.StateReason)
}
})
t.Run("should save expected fields", func(t *testing.T) {
trace := tracing.NewNoopTracerProvider().Tracer("test")
_, span := trace.Start(context.Background(), "")
st := &FakeInstanceStore{}
syncStatePersister := NewSyncStatePersisiter(&logtest.Fake{}, ManagerCfg{
InstanceStore: st,
MaxStateSaveConcurrency: 1,
})
state := &State{
OrgID: rand.Int63(),
AlertRuleUID: util.GenerateShortUID(),
CacheID: data.Fingerprint(rand.Int63()).String(),
State: eval.Alerting,
StateReason: "TEST",
ResultFingerprint: data.Fingerprint(rand.Int63()),
LatestResult: &Evaluation{
EvaluationTime: time.Now().Add(1 * time.Minute),
EvaluationState: eval.Alerting,
Values: map[string]*float64{
"A": util.Pointer(1.0),
"B": util.Pointer(2.0),
},
Condition: "A",
},
Error: errors.New("test"),
Image: &ngmodels.Image{
ID: rand.Int63(),
Token: util.GenerateShortUID(),
Path: util.GenerateShortUID(),
URL: util.GenerateShortUID(),
CreatedAt: time.Now().Add(2 * time.Minute),
ExpiresAt: time.Now().Add(3 * time.Minute),
},
Annotations: ngmodels.GenerateAlertLabels(4, "annotations_"),
Labels: ngmodels.GenerateAlertLabels(4, "labels_"),
Values: map[string]float64{
"A1": 11.0,
"B1": 12.0,
},
StartsAt: time.Now().Add(4 * time.Minute),
EndsAt: time.Now().Add(5 * time.Minute),
LastSentAt: time.Now().Add(7 * time.Minute),
LastEvaluationString: util.GenerateShortUID(),
LastEvaluationTime: time.Now().Add(8 * time.Minute),
EvaluationDuration: time.Duration(rand.Intn(100)+1) * time.Second,
}
transition := StateTransition{
State: state,
PreviousState: eval.Normal,
PreviousStateReason: util.GenerateShortUID(),
}
syncStatePersister.Sync(context.Background(), span, []StateTransition{transition}, nil)
require.Len(t, st.RecordedOps(), 1)
saved := st.RecordedOps()[0].(ngmodels.AlertInstance)
expectedAlertInstanceKey, err := state.GetAlertInstanceKey()
require.NoError(t, err)
assert.Equal(t, expectedAlertInstanceKey, saved.AlertInstanceKey)
assert.Equal(t, ngmodels.InstanceLabels(state.Labels), saved.Labels)
assert.EqualValues(t, ngmodels.InstanceStateType(state.State.String()), saved.CurrentState)
assert.Equal(t, state.StateReason, saved.CurrentReason)
assert.Equal(t, state.StartsAt, saved.CurrentStateSince)
assert.Equal(t, state.EndsAt, saved.CurrentStateEnd)
assert.Equal(t, state.LastEvaluationTime, saved.LastEvalTime)
assert.Equal(t, state.ResultFingerprint.String(), saved.ResultFingerprint)
})
}