fix(db): AutoMigrate 前清理旧模型遗留的同名非唯一索引,修复 MySQL 启动失败 #14

Merged
kevin merged 2 commits from dsh/mailgo:fix/legacy-ban-index into main 2026-08-20 11:41:04 +08:00
Contributor

问题

mail_go 部署在 MySQL 库上升级到含 P4 安全修复(BanEntry.IPAddress 升级为 uniqueIndex)的版本后启动即崩溃循环:

Error 1061 (42000): Duplicate key name 'idx_ban_entries_ip_address'
CREATE UNIQUE INDEX `idx_ban_entries_ip_address` ON `ban_entries`(`ip_address`)
数据库初始化失败: 数据库迁移失败

根因

  • 旧模型 BanEntry.IPAddress 为普通 index 标签,AutoMigrate 在 ban_entries 上建成同名非唯一索引
  • 升级为 uniqueIndex 后,gorm mysql 驱动(v1.5.7)MigrateColumnUnique非唯一列无条件执行 CREATE UNIQUE INDEX(该路径不检查 HasIndex),与同名旧索引冲突报 Error 1061;
  • AutoMigrate 末尾按索引名查 HasIndex 的保护永远执行不到(逐列阶段先失败返回);
  • 现有 dedupeBanEntries 只清理重复行,不处理同名旧索引,因此从旧库升级的部署必然失败。

修复

新增 dropLegacyBanIndex:在 dedupeBanEntries 之后、AutoMigrate 之前,检测 information_schema.statisticsidx_ban_entries_ip_addressnon_unique=1 并删除,由 AutoMigrate 重建为唯一索引。

  • 仅 MySQL 生效(SQLite 迁移路径不受影响);
  • 表不存在(首次安装)时静默跳过;失败仅告警,不阻断启动;
  • dedupeBanEntries 风格一致,幂等。

验证

生产库实测(服务停机 → 复现旧现场:删除唯一索引、重建同名非唯一索引 → 运行含补丁的迁移 → 索引重建为 UNIQUE、全部迁移完成),并已在生产部署观察运行正常。

## 问题 `mail_go` 部署在 MySQL 库上升级到含 P4 安全修复(`BanEntry.IPAddress` 升级为 `uniqueIndex`)的版本后启动即崩溃循环: ``` Error 1061 (42000): Duplicate key name 'idx_ban_entries_ip_address' CREATE UNIQUE INDEX `idx_ban_entries_ip_address` ON `ban_entries`(`ip_address`) 数据库初始化失败: 数据库迁移失败 ``` ## 根因 - 旧模型 `BanEntry.IPAddress` 为普通 `index` 标签,AutoMigrate 在 `ban_entries` 上建成**同名非唯一索引**; - 升级为 `uniqueIndex` 后,gorm mysql 驱动(v1.5.7)`MigrateColumnUnique` 对**非唯一列无条件执行 `CREATE UNIQUE INDEX`**(该路径不检查 `HasIndex`),与同名旧索引冲突报 Error 1061; - `AutoMigrate` 末尾按索引名查 `HasIndex` 的保护永远执行不到(逐列阶段先失败返回); - 现有 `dedupeBanEntries` 只清理重复行,**不处理同名旧索引**,因此从旧库升级的部署必然失败。 ## 修复 新增 `dropLegacyBanIndex`:在 `dedupeBanEntries` 之后、`AutoMigrate` 之前,检测 `information_schema.statistics` 中 `idx_ban_entries_ip_address` 的 `non_unique=1` 并删除,由 AutoMigrate 重建为唯一索引。 - 仅 MySQL 生效(SQLite 迁移路径不受影响); - 表不存在(首次安装)时静默跳过;失败仅告警,不阻断启动; - 与 `dedupeBanEntries` 风格一致,幂等。 ## 验证 生产库实测(服务停机 → 复现旧现场:删除唯一索引、重建同名非唯一索引 → 运行含补丁的迁移 → 索引重建为 UNIQUE、全部迁移完成),并已在生产部署观察运行正常。
dsh added 2 commits 2026-08-20 11:40:47 +08:00
旧模型 BanEntry.IPAddress 为普通 index,由 AutoMigrate 建成非唯一索引
idx_ban_entries_ip_address;升级到 uniqueIndex 后,gorm mysql 驱动
MigrateColumnUnique 对非唯一列无条件 CREATE UNIQUE INDEX(不检查
HasIndex),与同名旧索引冲突报 Error 1061 Duplicate key name,服务
启动即失败(AutoMigrate 末尾的 HasIndex 保护执行不到)。

dropLegacyBanIndex 在 dedupeBanEntries 之后、AutoMigrate 之前,检测
information_schema.statistics 中同名 non_unique=1 的索引并删除,由
AutoMigrate 重建为唯一索引。仅 MySQL 生效,表不存在/失败仅告警。
kevin merged commit 5abe5c4528 into main 2026-08-20 11:41:04 +08:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kevin/mailgo#14