Alerting: Store alertmanager configuration history in a separate table in the database (#60492)

* Update config store to split between active and history tables

* Migrations to fix up indexes

* Implement migration from old format to new

* Move add migrations call

* Delete duplicated rows

* Explicitly map fields

* Quote the column name because it's a reserved word

* Lift migrations to top

* Use XORM for nearly everything, avoid any non trivial raw SQL

* Touch up indexes and zero out IDs on move

* Drop TODO that's already completed

* Fix assignment of IDs
This commit is contained in:
Alexander Weaver
2023-01-04 10:43:26 -06:00
committed by GitHub
parent c74d86ca27
commit 0e7640475f
3 changed files with 104 additions and 100 deletions

View File

@@ -35,6 +35,7 @@ func AddTablesMigrations(mg *migrator.Migrator) {
AddAlertImageMigrations(mg)
AddAlertmanagerConfigHistoryMigrations(mg)
ExtractAlertmanagerConfigurationHistoryMigration(mg)
}
// AddAlertDefinitionMigrations should not be modified.

View File

@@ -927,3 +927,80 @@ func (s *uidSet) generateUid() (string, error) {
return "", errors.New("failed to generate UID")
}
func ExtractAlertmanagerConfigurationHistoryMigration(mg *migrator.Migrator) {
if !mg.Cfg.UnifiedAlerting.IsEnabled() {
return
}
// Since it's not always consistent as to what state the org ID indexes are in, just drop them all and rebuild from scratch.
// This is not expensive since this table is guaranteed to have a small number of rows.
mg.AddMigration("drop non-unique orgID index on alert_configuration", migrator.NewDropIndexMigration(migrator.Table{Name: "alert_configuration"}, &migrator.Index{Cols: []string{"org_id"}}))
mg.AddMigration("drop unique orgID index on alert_configuration if exists", migrator.NewDropIndexMigration(migrator.Table{Name: "alert_configuration"}, &migrator.Index{Type: migrator.UniqueIndex, Cols: []string{"org_id"}}))
mg.AddMigration("extract alertmanager configuration history to separate table", &extractAlertmanagerConfigurationHistory{})
mg.AddMigration("add unique index on orgID to alert_configuration", migrator.NewAddIndexMigration(migrator.Table{Name: "alert_configuration"}, &migrator.Index{Type: migrator.UniqueIndex, Cols: []string{"org_id"}}))
}
type extractAlertmanagerConfigurationHistory struct {
migrator.MigrationBase
}
// extractAMConfigHistoryConfigModel is the model of an alertmanager configuration row, at the time that the extractAlertmanagerConfigurationHistory migration was run.
// This is not to be used outside of the extractAlertmanagerConfigurationHistory migration.
type extractAMConfigHistoryConfigModel struct {
ID int64 `xorm:"pk autoincr 'id'"`
AlertmanagerConfiguration string
ConfigurationHash string
ConfigurationVersion string
CreatedAt int64 `xorm:"created"`
Default bool
OrgID int64 `xorm:"org_id"`
}
func (c extractAlertmanagerConfigurationHistory) SQL(migrator.Dialect) string {
return codeMigration
}
func (c extractAlertmanagerConfigurationHistory) Exec(sess *xorm.Session, migrator *migrator.Migrator) error {
var orgs []int64
if err := sess.Table("alert_configuration").Distinct("org_id").Find(&orgs); err != nil {
return fmt.Errorf("failed to retrieve the organizations with alerting configurations: %w", err)
}
// Clear out the history table, just in case. It should already be empty.
if _, err := sess.Exec("DELETE FROM alert_configuration_history"); err != nil {
return fmt.Errorf("failed to clear the config history table: %w", err)
}
for _, orgID := range orgs {
var activeConfigID int64
has, err := sess.SQL(`SELECT MAX(id) FROM alert_configuration WHERE org_id = ?`, orgID).Get(&activeConfigID)
if err != nil {
return fmt.Errorf("failed to query active config ID for org %d: %w", orgID, err)
}
if !has {
return fmt.Errorf("we previously found a config for org, but later it was unexpectedly missing: %d", orgID)
}
history := make([]extractAMConfigHistoryConfigModel, 0)
err = sess.Table("alert_configuration").Where("org_id = ? AND id < ?", orgID, activeConfigID).Find(&history)
if err != nil {
return fmt.Errorf("failed to query for non-active configs for org %d: %w", orgID, err)
}
// Set the IDs back to the default, so XORM will ignore the field and auto-assign them.
for i := range history {
history[i].ID = 0
}
_, err = sess.Table("alert_configuration_history").InsertMulti(history)
if err != nil {
return fmt.Errorf("failed to insert historical configs for org: %d: %w", orgID, err)
}
_, err = sess.Exec("DELETE FROM alert_configuration WHERE org_id = ? AND id < ?", orgID, activeConfigID)
if err != nil {
return fmt.Errorf("failed to evict old configurations for org after moving to history table: %d: %w", orgID, err)
}
}
return nil
}