yumi-golang/internal/service/gameking/archive_safety.go
2026-07-18 18:56:15 +08:00

66 lines
2.4 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(`
SELECT COLUMN_NAME, SEQ_IN_INDEX, SUB_PART, INDEX_TYPE, 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",
)
}