mirror of https://github.com/grafana/grafana
Alerting: Support for optimistic locking for alert rules (#50274)
* add support for optimistic locking for alert_rule table * return 409 in the case of opitimistic lockpull/50720/head
parent
402b5ce4c6
commit
c314ce48c7
@ -0,0 +1,91 @@ |
|||||||
|
package store |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
"golang.org/x/exp/rand" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/ngalert/models" |
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore" |
||||||
|
"github.com/grafana/grafana/pkg/util" |
||||||
|
) |
||||||
|
|
||||||
|
func TestUpdateAlertRules(t *testing.T) { |
||||||
|
sqlStore := sqlstore.InitTestDB(t) |
||||||
|
store := DBstore{ |
||||||
|
SQLStore: sqlStore, |
||||||
|
BaseInterval: time.Duration(rand.Int63n(100)) * time.Second, |
||||||
|
} |
||||||
|
createRule := func(t *testing.T) *models.AlertRule { |
||||||
|
t.Helper() |
||||||
|
rule := models.AlertRuleGen(withIntervalMatching(store.BaseInterval))() |
||||||
|
err := sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { |
||||||
|
_, err := sess.Table(models.AlertRule{}).InsertOne(rule) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
dbRule := &models.AlertRule{} |
||||||
|
exist, err := sess.Table(models.AlertRule{}).ID(rule.ID).Get(dbRule) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if !exist { |
||||||
|
return errors.New("cannot read inserted record") |
||||||
|
} |
||||||
|
rule = dbRule |
||||||
|
return nil |
||||||
|
}) |
||||||
|
require.NoError(t, err) |
||||||
|
return rule |
||||||
|
} |
||||||
|
|
||||||
|
t.Run("should increase version", func(t *testing.T) { |
||||||
|
rule := createRule(t) |
||||||
|
newRule := models.CopyRule(rule) |
||||||
|
newRule.Title = util.GenerateShortUID() |
||||||
|
err := store.UpdateAlertRules(context.Background(), []UpdateRule{{ |
||||||
|
Existing: rule, |
||||||
|
New: *newRule, |
||||||
|
}, |
||||||
|
}) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
dbrule := &models.AlertRule{} |
||||||
|
err = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { |
||||||
|
exist, err := sess.Table(models.AlertRule{}).ID(rule.ID).Get(dbrule) |
||||||
|
require.Truef(t, exist, fmt.Sprintf("rule with ID %d does not exist", rule.ID)) |
||||||
|
return err |
||||||
|
}) |
||||||
|
|
||||||
|
require.NoError(t, err) |
||||||
|
require.Equal(t, rule.Version+1, dbrule.Version) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("should fail due to optimistic locking if version does not match", func(t *testing.T) { |
||||||
|
rule := createRule(t) |
||||||
|
rule.Version-- // simulate version discrepancy
|
||||||
|
|
||||||
|
newRule := models.CopyRule(rule) |
||||||
|
newRule.Title = util.GenerateShortUID() |
||||||
|
|
||||||
|
err := store.UpdateAlertRules(context.Background(), []UpdateRule{{ |
||||||
|
Existing: rule, |
||||||
|
New: *newRule, |
||||||
|
}, |
||||||
|
}) |
||||||
|
|
||||||
|
require.ErrorIs(t, err, ErrOptimisticLock) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
func withIntervalMatching(baseInterval time.Duration) func(*models.AlertRule) { |
||||||
|
return func(rule *models.AlertRule) { |
||||||
|
rule.IntervalSeconds = int64(baseInterval.Seconds()) * rand.Int63n(10) |
||||||
|
rule.For = time.Duration(rule.IntervalSeconds*rand.Int63n(9)+1) * time.Second |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue