From 2c18526a78d44e8dc7c9fe75aba7b0ba134af25c Mon Sep 17 00:00:00 2001 From: William Wernert Date: Mon, 24 Nov 2025 14:58:43 -0500 Subject: [PATCH] Alerting: Collate rule_group column as binary (#114365) * fix: collate rule_group column as utf8mb4_bin This ensures this column is sorted identically by the database and golang, which should eliminate issues related to a mismatch in sort order. * chore: make test stricter * fix: also add binary collation for postgres --- pkg/services/ngalert/store/alert_rule_test.go | 8 +++----- pkg/services/sqlstore/migrations/migrations.go | 2 ++ .../ualert/alert_rule_group_collation_bin.go | 10 ++++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 pkg/services/sqlstore/migrations/ualert/alert_rule_group_collation_bin.go diff --git a/pkg/services/ngalert/store/alert_rule_test.go b/pkg/services/ngalert/store/alert_rule_test.go index 9a491120c8c..b0b7592c2d1 100644 --- a/pkg/services/ngalert/store/alert_rule_test.go +++ b/pkg/services/ngalert/store/alert_rule_test.go @@ -1516,11 +1516,9 @@ func TestIntegrationListAlertRulesByGroupCaseSensitiveOrdering(t *testing.T) { } // Verify case-sensitive alphabetical ordering - // different databases may sort uppercase before lowercase or vice versa depending on character set, the important part is that the order is consistent and case-sensitive - expectedOrder := []string{"test", "Test", "TEST"} - alternateExpectedOrder := []string{"TEST", "Test", "test"} - if !slices.Equal(groupOrder, expectedOrder) && !slices.Equal(groupOrder, alternateExpectedOrder) { - t.Fatalf("groups are not ordered case-sensitively as expected. got: %v, want: %v or %v", groupOrder, expectedOrder, alternateExpectedOrder) + expectedOrder := []string{"TEST", "Test", "test"} + if !slices.Equal(groupOrder, expectedOrder) { + t.Fatalf("groups are not ordered case-sensitively as expected. got: %v, want: %v", groupOrder, expectedOrder) } // Verify each group contains the correct rules diff --git a/pkg/services/sqlstore/migrations/migrations.go b/pkg/services/sqlstore/migrations/migrations.go index 279839a4ae4..4fc4e1df131 100644 --- a/pkg/services/sqlstore/migrations/migrations.go +++ b/pkg/services/sqlstore/migrations/migrations.go @@ -162,4 +162,6 @@ func (oss *OSSMigrations) AddMigration(mg *Migrator) { ualert.ExpandAlertRuleUpdatedByMigration(mg) ualert.AddAlertRuleGroupIndexMigration(mg) + + ualert.CollateBinAlertRuleGroup(mg) } diff --git a/pkg/services/sqlstore/migrations/ualert/alert_rule_group_collation_bin.go b/pkg/services/sqlstore/migrations/ualert/alert_rule_group_collation_bin.go new file mode 100644 index 00000000000..4464d6669d3 --- /dev/null +++ b/pkg/services/sqlstore/migrations/ualert/alert_rule_group_collation_bin.go @@ -0,0 +1,10 @@ +package ualert + +import "github.com/grafana/grafana/pkg/services/sqlstore/migrator" + +// CollateBinAlertRuleGroup ensures that rule_group column collates in the same way go sorts strings. +func CollateBinAlertRuleGroup(mg *migrator.Migrator) { + mg.AddMigration("ensure rule_group column sorts the same way as golang", migrator.NewRawSQLMigration(""). + Mysql("ALTER TABLE alert_rule MODIFY rule_group VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL;"). + Postgres(`ALTER TABLE alert_rule ALTER COLUMN rule_group SET DATA TYPE varchar(190) COLLATE "C";`)) +}