From c5278af6c498020d206eebe601f7dcd23160668b Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 27 Sep 2018 11:33:13 +0200 Subject: [PATCH] add support for mysql and postgres unique index error codes --- pkg/models/alert_notifications.go | 10 +++-- pkg/services/alerting/notifier.go | 2 + pkg/services/sqlstore/alert_notification.go | 43 ++++++++++++++++--- .../sqlstore/alert_notification_test.go | 17 +++++--- 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/pkg/models/alert_notifications.go b/pkg/models/alert_notifications.go index 54220e7d120..c3c41dc5dd9 100644 --- a/pkg/models/alert_notifications.go +++ b/pkg/models/alert_notifications.go @@ -11,7 +11,7 @@ var ( ErrNotificationFrequencyNotFound = errors.New("Notification frequency not specified") ErrAlertNotificationStateNotFound = errors.New("alert notification state not found") ErrAlertNotificationStateVersionConflict = errors.New("alert notification state update version conflict") - ErrAlertNotificationStateAllreadyExist = errors.New("alert notification state allready exists.") + ErrAlertNotificationStateAlreadyExist = errors.New("alert notification state already exists.") ) type AlertNotificationStateType string @@ -95,13 +95,17 @@ type AlertNotificationState struct { Version int64 } -type UpdateAlertNotificationStateCommand struct { +type SetAlertNotificationStateToPendingCommand struct { Id int64 SentAt int64 - State AlertNotificationStateType Version int64 } +type SetAlertNotificationStateToCompleteCommand struct { + Id int64 + SentAt int64 +} + type GetNotificationStateQuery struct { OrgId int64 AlertId int64 diff --git a/pkg/services/alerting/notifier.go b/pkg/services/alerting/notifier.go index ec448a8c1b0..941d3df9dc3 100644 --- a/pkg/services/alerting/notifier.go +++ b/pkg/services/alerting/notifier.go @@ -64,6 +64,8 @@ func (n *notificationService) sendNotifications(evalContext *EvalContext, notifi err := bus.InTransaction(evalContext.Ctx, func(ctx context.Context) error { n.log.Debug("trying to send notification", "id", not.GetNotifierId()) + // insert if needed + // Verify that we can send the notification again // but this time within the same transaction. // if !evalContext.IsTestRun && !not.ShouldNotify(ctx, evalContext) { diff --git a/pkg/services/sqlstore/alert_notification.go b/pkg/services/sqlstore/alert_notification.go index 4a115bcd788..92860c361aa 100644 --- a/pkg/services/sqlstore/alert_notification.go +++ b/pkg/services/sqlstore/alert_notification.go @@ -20,6 +20,8 @@ func init() { bus.AddHandler("sql", GetAllAlertNotifications) bus.AddHandlerCtx("sql", InsertAlertNotificationState) bus.AddHandlerCtx("sql", GetAlertNotificationState) + bus.AddHandlerCtx("sql", SetAlertNotificationStateToCompleteCommand) + bus.AddHandlerCtx("sql", SetAlertNotificationStateToPendingCommand) } func DeleteAlertNotification(cmd *m.DeleteAlertNotificationCommand) error { @@ -244,25 +246,54 @@ func InsertAlertNotificationState(ctx context.Context, cmd *m.InsertAlertNotific return nil } - if strings.HasPrefix(err.Error(), "UNIQUE constraint failed") { - return m.ErrAlertNotificationStateAllreadyExist + uniqenessIndexFailureCodes := []string{ + "UNIQUE constraint failed", + "pq: duplicate key value violates unique constraint", + "Error 1062: Duplicate entry ", + } + + for _, code := range uniqenessIndexFailureCodes { + if strings.HasPrefix(err.Error(), code) { + return m.ErrAlertNotificationStateAlreadyExist + } } return err }) } -func UpdateAlertNotificationState(ctx context.Context, cmd *m.UpdateAlertNotificationStateCommand) error { +func SetAlertNotificationStateToCompleteCommand(ctx context.Context, cmd *m.SetAlertNotificationStateToCompleteCommand) error { + return withDbSession(ctx, func(sess *DBSession) error { + sql := `UPDATE alert_notification_state SET + state= ? + WHERE + id = ?` + + res, err := sess.Exec(sql, m.AlertNotificationStateCompleted, cmd.Id) + if err != nil { + return err + } + + affected, _ := res.RowsAffected() + + if affected == 0 { + return m.ErrAlertNotificationStateVersionConflict + } + + return nil + }) +} + +func SetAlertNotificationStateToPendingCommand(ctx context.Context, cmd *m.SetAlertNotificationStateToPendingCommand) error { return withDbSession(ctx, func(sess *DBSession) error { sql := `UPDATE alert_notification_state SET state= ?, version = ? WHERE id = ? AND - version = ? - ` + version = ?` - res, err := sess.Exec(sql, cmd.State, cmd.Version+1, cmd.Id, cmd.Version) + res, err := sess.Exec(sql, m.AlertNotificationStatePending, cmd.Version+1, cmd.Id, cmd.Version) if err != nil { return err } diff --git a/pkg/services/sqlstore/alert_notification_test.go b/pkg/services/sqlstore/alert_notification_test.go index 4b71b0d09dd..206c96b5c6a 100644 --- a/pkg/services/sqlstore/alert_notification_test.go +++ b/pkg/services/sqlstore/alert_notification_test.go @@ -38,23 +38,28 @@ func TestAlertNotificationSQLAccess(t *testing.T) { So(err, ShouldBeNil) err = InsertAlertNotificationState(context.Background(), createCmd) - So(err, ShouldEqual, models.ErrAlertNotificationStateAllreadyExist) + So(err, ShouldEqual, models.ErrAlertNotificationStateAlreadyExist) Convey("should be able to update alert notifier state", func() { - updateCmd := &models.UpdateAlertNotificationStateCommand{ + updateCmd := &models.SetAlertNotificationStateToPendingCommand{ Id: 1, SentAt: 1, - State: models.AlertNotificationStatePending, Version: 0, } - err := UpdateAlertNotificationState(context.Background(), updateCmd) + err := SetAlertNotificationStateToPendingCommand(context.Background(), updateCmd) So(err, ShouldBeNil) - Convey("should not be able to update older versions", func() { - err = UpdateAlertNotificationState(context.Background(), updateCmd) + Convey("should not be able to set pending on old version", func() { + err = SetAlertNotificationStateToPendingCommand(context.Background(), updateCmd) So(err, ShouldEqual, models.ErrAlertNotificationStateVersionConflict) }) + + Convey("should be able to set state to completed", func() { + cmd := &models.SetAlertNotificationStateToCompleteCommand{Id: 1} + err = SetAlertNotificationStateToCompleteCommand(context.Background(), cmd) + So(err, ShouldBeNil) + }) }) }) })