发布 0.2.0:模型池自愈、探测修正、任务异步触发与删除加固
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -104,7 +105,8 @@ func planTenantTask(task model.Task, id uint) (tenantTaskAction, bool, error) {
|
||||
func planSnatchTask(task model.Task, id uint) (tenantTaskAction, bool, error) {
|
||||
var payload snatchPayload
|
||||
if err := json.Unmarshal([]byte(task.Payload), &payload); err != nil {
|
||||
return tenantTaskAction{}, false, fmt.Errorf("parse task %d payload: %w", task.ID, err)
|
||||
logSkippedTask(task, err)
|
||||
return tenantTaskAction{}, false, nil
|
||||
}
|
||||
if payload.OciConfigID != id {
|
||||
return tenantTaskAction{}, false, nil
|
||||
@@ -112,10 +114,17 @@ func planSnatchTask(task model.Task, id uint) (tenantTaskAction, bool, error) {
|
||||
return tenantTaskAction{task: task, deleteTask: true}, true, nil
|
||||
}
|
||||
|
||||
// logSkippedTask 记录 payload 无法解析而被跳过的任务:坏数据无法归属租户,
|
||||
// fail-closed 会永久阻断删除且只能手工修库;改为保留原任务(不删不改)并放行删除。
|
||||
func logSkippedTask(task model.Task, err error) {
|
||||
log.Printf("tenant delete: 任务 %d(%s)payload 无法解析,跳过处理: %v", task.ID, task.Type, err)
|
||||
}
|
||||
|
||||
func planMultiTenantTask(task model.Task, id uint) (tenantTaskAction, bool, error) {
|
||||
payload, err := decodeConfigIDs(task)
|
||||
if err != nil {
|
||||
return tenantTaskAction{}, false, err
|
||||
logSkippedTask(task, err)
|
||||
return tenantTaskAction{}, false, nil
|
||||
}
|
||||
if len(payload.OciConfigIDs) == 0 {
|
||||
return tenantTaskAction{task: task, payload: task.Payload}, true, nil
|
||||
@@ -197,11 +206,11 @@ func resetTenantTask(tx *gorm.DB, action tenantTaskAction) error {
|
||||
}
|
||||
|
||||
func deleteTenantEvents(tx *gorm.DB, id uint, result *tenantDeleteResult) error {
|
||||
ruleIDs, eventIDs, affectedRules, err := loadTenantEventRefs(tx, id)
|
||||
ruleIDs, affectedRules, err := loadTenantEventRefs(tx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := deleteAlertHits(tx, ruleIDs, eventIDs); err != nil {
|
||||
if err := deleteAlertHits(tx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
result.alertRuleIDs = mergeIDs(ruleIDs, affectedRules)
|
||||
@@ -214,20 +223,19 @@ func deleteTenantEvents(tx *gorm.DB, id uint, result *tenantDeleteResult) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadTenantEventRefs(tx *gorm.DB, id uint) ([]uint, []uint, []uint, error) {
|
||||
func loadTenantEventRefs(tx *gorm.DB, id uint) ([]uint, []uint, error) {
|
||||
ruleIDs, err := lockedTenantRuleIDs(tx, id)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("load tenant alert rules: %w", err)
|
||||
return nil, nil, fmt.Errorf("load tenant alert rules: %w", err)
|
||||
}
|
||||
eventIDs, err := lockedTenantEventIDs(tx, id)
|
||||
if err := lockTenantEventRows(tx, id); err != nil {
|
||||
return nil, nil, fmt.Errorf("load tenant log events: %w", err)
|
||||
}
|
||||
affected, err := alertHitRuleIDs(tx, id)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("load tenant log events: %w", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
affected, err := alertHitRuleIDs(tx, eventIDs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return ruleIDs, eventIDs, affected, nil
|
||||
return ruleIDs, affected, nil
|
||||
}
|
||||
|
||||
func lockedTenantRuleIDs(tx *gorm.DB, id uint) ([]uint, error) {
|
||||
@@ -241,23 +249,24 @@ func lockedTenantRuleIDs(tx *gorm.DB, id uint) ([]uint, error) {
|
||||
return ids, err
|
||||
}
|
||||
|
||||
func lockedTenantEventIDs(tx *gorm.DB, id uint) ([]uint, error) {
|
||||
// lockTenantEventRows 对租户全部日志事件行加 FOR UPDATE 锁(SQLite 忽略,
|
||||
// MySQL/PG 阻塞并发命中写入);事件可达数万,ID 不再回传拼接 SQL,
|
||||
// 后续删除与关联查询一律用子查询,避免绑定变量上限。
|
||||
func lockTenantEventRows(tx *gorm.DB, id uint) error {
|
||||
var rows []model.LogEvent
|
||||
err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Select("id").
|
||||
return tx.Clauses(clause.Locking{Strength: "UPDATE"}).Select("id").
|
||||
Where("oci_config_id = ?", id).Order("id").Find(&rows).Error
|
||||
ids := make([]uint, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
ids = append(ids, row.ID)
|
||||
}
|
||||
return ids, err
|
||||
}
|
||||
|
||||
func alertHitRuleIDs(tx *gorm.DB, eventIDs []uint) ([]uint, error) {
|
||||
if len(eventIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
// tenantEventIDs 构造「本租户日志事件 ID」子查询,供 IN (?) 内联。
|
||||
func tenantEventIDs(tx *gorm.DB, id uint) *gorm.DB {
|
||||
return tx.Model(&model.LogEvent{}).Select("id").Where("oci_config_id = ?", id)
|
||||
}
|
||||
|
||||
// alertHitRuleIDs 找出命中引用了本租户事件的规则 ID(去重,可能含他租户/全局规则)。
|
||||
func alertHitRuleIDs(tx *gorm.DB, id uint) ([]uint, error) {
|
||||
ids := make([]uint, 0)
|
||||
err := tx.Model(&model.AlertRuleHit{}).Where("log_event_id IN ?", eventIDs).
|
||||
err := tx.Model(&model.AlertRuleHit{}).Where("log_event_id IN (?)", tenantEventIDs(tx, id)).
|
||||
Distinct().Pluck("rule_id", &ids).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load affected alert rules: %w", err)
|
||||
@@ -280,19 +289,13 @@ func mergeIDs(groups ...[]uint) []uint {
|
||||
return out
|
||||
}
|
||||
|
||||
func deleteAlertHits(tx *gorm.DB, ruleIDs, eventIDs []uint) error {
|
||||
query := tx.Model(&model.AlertRuleHit{})
|
||||
switch {
|
||||
case len(ruleIDs) > 0 && len(eventIDs) > 0:
|
||||
query = query.Where("rule_id IN ? OR log_event_id IN ?", ruleIDs, eventIDs)
|
||||
case len(ruleIDs) > 0:
|
||||
query = query.Where("rule_id IN ?", ruleIDs)
|
||||
case len(eventIDs) > 0:
|
||||
query = query.Where("log_event_id IN ?", eventIDs)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
if err := query.Delete(&model.AlertRuleHit{}).Error; err != nil {
|
||||
// deleteAlertHits 删除本租户相关的全部命中:规则属于本租户,或命中引用了
|
||||
// 本租户的日志事件(他租户/全局规则命中本租户事件的行一并清)。
|
||||
func deleteAlertHits(tx *gorm.DB, id uint) error {
|
||||
rules := tx.Model(&model.AlertRule{}).Select("id").Where("oci_config_id = ?", id)
|
||||
err := tx.Where("rule_id IN (?) OR log_event_id IN (?)", rules, tenantEventIDs(tx, id)).
|
||||
Delete(&model.AlertRuleHit{}).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete tenant alert hits: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user