72 lines
2.7 KiB
Go
72 lines
2.7 KiB
Go
package gameking
|
||
|
||
import (
|
||
"errors"
|
||
"net/http"
|
||
"strings"
|
||
|
||
"chatapp3-golang/internal/model"
|
||
|
||
"gorm.io/gorm"
|
||
"gorm.io/gorm/clause"
|
||
)
|
||
|
||
// ensureActivityEnableSafetyTx 把“索引已在线”和“GC 没在删除”纳入首次启用事务。
|
||
// 它与 GC 删除事务锁同一控制行,因此两个并发请求不会同时各自通过旧状态检查。
|
||
func ensureActivityEnableSafetyTx(tx *gorm.DB) error {
|
||
if err := requireBackfillIndexOnDB(tx); err != nil {
|
||
return err
|
||
}
|
||
var run model.TaskCenterArchiveGCRun
|
||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", uint8(1)).First(&run).Error; err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return NewAppError(http.StatusServiceUnavailable, "archive_gc_migration_required", "migration 060 archive GC safety state is required before enabling game king")
|
||
}
|
||
return NewAppError(http.StatusServiceUnavailable, "archive_gc_state_check_failed", "cannot verify archive GC state before enabling game king")
|
||
}
|
||
if strings.EqualFold(run.Status, "RUNNING") {
|
||
return NewAppError(http.StatusConflict, "archive_gc_running", "pause task center archive GC before enabling game king")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func requireBackfillIndexOnDB(db *gorm.DB) error {
|
||
var columns []backfillIndexColumn
|
||
if err := db.Raw(`
|
||
-- 与任务中心 GC 门禁保持相同映射规则;不加别名时 MySQL 的大写列标签不会填充
|
||
-- 显式小写 GORM tag,导致真实存在的 Game King 索引也被误判为缺失。
|
||
SELECT COLUMN_NAME AS column_name,
|
||
SEQ_IN_INDEX AS seq_in_index,
|
||
SUB_PART AS sub_part,
|
||
INDEX_TYPE AS index_type,
|
||
IS_VISIBLE AS is_visible
|
||
FROM information_schema.STATISTICS
|
||
WHERE TABLE_SCHEMA = DATABASE()
|
||
AND TABLE_NAME = 'task_center_event_archive_outbox'
|
||
AND INDEX_NAME = ?
|
||
ORDER BY SEQ_IN_INDEX ASC`, taskCenterGameKingBackfillIndex).Scan(&columns).Error; err != nil {
|
||
return NewAppError(http.StatusServiceUnavailable, "backfill_index_check_failed", "cannot verify the required task center backfill index")
|
||
}
|
||
|
||
required := [...]string{"sys_origin", "event_type", "occurred_at", "id"}
|
||
if len(columns) == len(required) {
|
||
valid := true
|
||
for index, requiredName := range required {
|
||
column := columns[index]
|
||
if column.SeqInIndex != index+1 || !strings.EqualFold(column.ColumnName, requiredName) || column.SubPart != nil ||
|
||
!strings.EqualFold(column.IndexType, "BTREE") || !strings.EqualFold(column.IsVisible, "YES") {
|
||
valid = false
|
||
break
|
||
}
|
||
}
|
||
if valid {
|
||
return nil
|
||
}
|
||
}
|
||
return NewAppError(
|
||
http.StatusServiceUnavailable,
|
||
"backfill_index_required",
|
||
"migration 058 backfill index (sys_origin,event_type,occurred_at,id) must be online before enabling or backfill",
|
||
)
|
||
}
|