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