From 217f82c57cad3dfee5c661da21c758f9ecda1d05 Mon Sep 17 00:00:00 2001 From: yanweidong Date: Wed, 22 Jul 2026 14:45:41 +0800 Subject: [PATCH] fix(models): isolate versioned migration sessions --- backend/internal/models/migrations.go | 12 ++++++++---- backend/internal/models/migrations_test.go | 5 ++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/backend/internal/models/migrations.go b/backend/internal/models/migrations.go index 6146fac..d6b738d 100644 --- a/backend/internal/models/migrations.go +++ b/backend/internal/models/migrations.go @@ -33,14 +33,18 @@ func runVersionedMigrations(database *gorm.DB) error { {version: 2, name: "deduplicate_project_tags", run: deduplicateLegacyProjectTags}, } for _, migration := range migrations { + // Connection callbacks can provide an initialized Gorm session. Start each + // migration from a clean statement so Model/Where clauses used to check the + // version table are not inherited by the data-repair transaction. + migrationDatabase := database.Session(&gorm.Session{NewDB: true}) var applied int64 - if err := database.Model(&SenlinAgentSchemaMigration{}).Where("version = ?", migration.version).Count(&applied).Error; err != nil { + if err := migrationDatabase.Model(&SenlinAgentSchemaMigration{}).Where("version = ?", migration.version).Count(&applied).Error; err != nil { return err } if applied > 0 { continue } - if err := database.Transaction(func(tx *gorm.DB) error { + if err := migrationDatabase.Session(&gorm.Session{NewDB: true}).Transaction(func(tx *gorm.DB) error { details, err := migration.run(tx) if err != nil { return err @@ -61,7 +65,7 @@ func runVersionedMigrations(database *gorm.DB) error { func normalizeLegacyProjectIdentifiers(tx *gorm.DB) (map[string]int, error) { var projects []SenlinAgentProject - if err := tx.Select("id", "owner_id", "identifier").Order("owner_id asc, id asc").Find(&projects).Error; err != nil { + if err := tx.Model(&SenlinAgentProject{}).Select("id", "owner_id", "identifier").Order("owner_id asc, id asc").Find(&projects).Error; err != nil { return nil, err } @@ -121,7 +125,7 @@ func uniqueIdentifier(base string, reserved map[string]struct{}) string { func deduplicateLegacyProjectTags(tx *gorm.DB) (map[string]int, error) { var tags []SenlinAgentTag - if err := tx.Select("id", "identity", "project_id", "name").Order("project_id asc, name asc, id asc").Find(&tags).Error; err != nil { + if err := tx.Model(&SenlinAgentTag{}).Select("id", "identity", "project_id", "name").Order("project_id asc, name asc, id asc").Find(&tags).Error; err != nil { return nil, err } keepers := make(map[string]SenlinAgentTag) diff --git a/backend/internal/models/migrations_test.go b/backend/internal/models/migrations_test.go index 3d319e0..9e59a46 100644 --- a/backend/internal/models/migrations_test.go +++ b/backend/internal/models/migrations_test.go @@ -35,7 +35,10 @@ func TestAutoMigrateUpgradesLegacyProjectsAndTagsWithoutLosingAssociations(t *te require.NoError(t, database.Exec(statement).Error) } - require.NoError(t, AutoMigrate(database)) + // PostgreSQL migrations run through database.Connection, whose callback DB can + // carry an initialized Gorm statement. Exercise that mode so model state from + // the migration-version lookup cannot leak into the data-repair transaction. + require.NoError(t, AutoMigrate(database.Session(&gorm.Session{Initialized: true}))) var projects []SenlinAgentProject require.NoError(t, database.Order("id asc").Find(&projects).Error)