|
|
@@ -0,0 +1,175 @@
|
|
|
+#!/usr/bin/env bash
|
|
|
+# S8-DETECTION-LOG-RETENTION-1:TEMP_SCHED_* detection_log / rule_detection_state 治理脚本(dev/test only, aidopdev)。
|
|
|
+#
|
|
|
+# 严格范围:
|
|
|
+# - 仅 rule_code LIKE 'TEMP_SCHED_%' 的数据
|
|
|
+# - 默认 dry-run(只统计、不删除);--apply 才真删
|
|
|
+# - 默认 --days=1,对早于 NOW - days 的 TEMP 行做 DELETE 候选
|
|
|
+# - 不动 ado_s8_exception
|
|
|
+# - 不动 demo rule 10/11/12 数据
|
|
|
+# - 不动非 TEMP_SCHED_% 的真实业务数据
|
|
|
+#
|
|
|
+# 用法:
|
|
|
+# bash scripts/s8/detection-retention-cleanup.sh # dry-run,默认 1 天
|
|
|
+# bash scripts/s8/detection-retention-cleanup.sh --days 7 # dry-run,7 天
|
|
|
+# bash scripts/s8/detection-retention-cleanup.sh --apply # 真删,默认 1 天
|
|
|
+# bash scripts/s8/detection-retention-cleanup.sh --apply --days 0 # 真删全部 TEMP 历史(dev/test 验证用)
|
|
|
+#
|
|
|
+# 退出码:0 总是(只要查询/删除未抛异常);致命错误 → 2。
|
|
|
+
|
|
|
+set -uo pipefail
|
|
|
+
|
|
|
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
+# shellcheck source=./s8-regression-common.sh
|
|
|
+source "${SCRIPT_DIR}/s8-regression-common.sh"
|
|
|
+
|
|
|
+APPLY=0
|
|
|
+DAYS=1
|
|
|
+
|
|
|
+while [[ $# -gt 0 ]]; do
|
|
|
+ case "$1" in
|
|
|
+ --apply) APPLY=1; shift ;;
|
|
|
+ --days) DAYS="${2:-1}"; shift 2 ;;
|
|
|
+ -h|--help)
|
|
|
+ sed -n '2,20p' "$0"
|
|
|
+ exit 0 ;;
|
|
|
+ *) echo "unknown arg: $1" >&2; exit 2 ;;
|
|
|
+ esac
|
|
|
+done
|
|
|
+
|
|
|
+[[ "${DAYS}" =~ ^[0-9]+$ ]] || s8reg_die "--days must be a non-negative integer, got '${DAYS}'"
|
|
|
+
|
|
|
+CUTOFF=$(mysql_run "SELECT DATE_SUB(NOW(), INTERVAL ${DAYS} DAY);")
|
|
|
+NOW_TS=$(mysql_run "SELECT NOW();")
|
|
|
+MODE=$([[ "${APPLY}" == "1" ]] && echo "APPLY (DELETE)" || echo "DRY-RUN (no delete)")
|
|
|
+
|
|
|
+echo "============================================================"
|
|
|
+echo "S8 detection retention cleanup"
|
|
|
+echo " DB : ${DB_HOST}:${DB_PORT}/${DB_NAME}"
|
|
|
+echo " now : ${NOW_TS}"
|
|
|
+echo " cutoff : ${CUTOFF} (--days=${DAYS})"
|
|
|
+echo " mode : ${MODE}"
|
|
|
+echo " scope : rule_code LIKE 'TEMP_SCHED_%' (real business data NOT touched)"
|
|
|
+echo "============================================================"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 守恒前 snapshot
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+baseline_before=$(read_baseline)
|
|
|
+demo_state_before=$(mysql_run "SELECT GROUP_CONCAT(CONCAT(id,':',enabled,':',IFNULL(paused_until,'NULL'),':',trigger_count_required,':',recover_count_required) ORDER BY id) FROM ado_s8_watch_rule WHERE id IN (10,11,12);")
|
|
|
+temp_active_exc_before=$(mysql_run "SELECT COUNT(*) FROM ado_s8_exception WHERE source_rule_code LIKE 'TEMP_SCHED_%' AND is_deleted=0;")
|
|
|
+
|
|
|
+echo
|
|
|
+echo "---- BEFORE ----"
|
|
|
+echo " baseline = ${baseline_before}"
|
|
|
+echo " demo rule 10/11/12 = ${demo_state_before}"
|
|
|
+echo " TEMP active exception (is_deleted=0) = ${temp_active_exc_before}"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# detection_log 统计
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+log_total=$(mysql_run "SELECT COUNT(*) FROM ado_s8_detection_log;")
|
|
|
+log_temp_total=$(mysql_run "SELECT COUNT(*) FROM ado_s8_detection_log WHERE rule_code LIKE 'TEMP_SCHED_%';")
|
|
|
+log_temp_candidates=$(mysql_run "SELECT COUNT(*) FROM ado_s8_detection_log WHERE rule_code LIKE 'TEMP_SCHED_%' AND detected_at < '${CUTOFF}';")
|
|
|
+
|
|
|
+echo
|
|
|
+echo "---- detection_log ----"
|
|
|
+echo " total = ${log_total}"
|
|
|
+echo " TEMP_SCHED_% total = ${log_temp_total}"
|
|
|
+echo " TEMP candidates < cutoff = ${log_temp_candidates}"
|
|
|
+
|
|
|
+if [[ "${log_temp_candidates}" != "0" ]]; then
|
|
|
+ echo " per-rule breakdown of candidates:"
|
|
|
+ mysql_run "SELECT rule_code, COUNT(*) AS cnt, MIN(detected_at) AS earliest, MAX(detected_at) AS latest FROM ado_s8_detection_log WHERE rule_code LIKE 'TEMP_SCHED_%' AND detected_at < '${CUTOFF}' GROUP BY rule_code ORDER BY cnt DESC;" | sed 's/^/ /'
|
|
|
+fi
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# rule_detection_state 统计
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+state_total=$(mysql_run "SELECT COUNT(*) FROM ado_s8_rule_detection_state;")
|
|
|
+state_temp_total=$(mysql_run "SELECT COUNT(*) FROM ado_s8_rule_detection_state WHERE rule_code LIKE 'TEMP_SCHED_%';")
|
|
|
+state_temp_candidates=$(mysql_run "SELECT COUNT(*) FROM ado_s8_rule_detection_state WHERE rule_code LIKE 'TEMP_SCHED_%' AND last_seen_at < '${CUTOFF}';")
|
|
|
+
|
|
|
+echo
|
|
|
+echo "---- rule_detection_state ----"
|
|
|
+echo " total = ${state_total}"
|
|
|
+echo " TEMP_SCHED_% total = ${state_temp_total}"
|
|
|
+echo " TEMP candidates < cutoff = ${state_temp_candidates}"
|
|
|
+
|
|
|
+if [[ "${state_temp_candidates}" != "0" ]]; then
|
|
|
+ echo " per-rule breakdown of candidates:"
|
|
|
+ mysql_run "SELECT rule_code, dedup_key, last_seen_at FROM ado_s8_rule_detection_state WHERE rule_code LIKE 'TEMP_SCHED_%' AND last_seen_at < '${CUTOFF}' ORDER BY last_seen_at;" | sed 's/^/ /'
|
|
|
+fi
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# Apply
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+log_deleted=0
|
|
|
+state_deleted=0
|
|
|
+if [[ "${APPLY}" == "1" ]]; then
|
|
|
+ echo
|
|
|
+ echo "---- APPLY ----"
|
|
|
+ log_deleted=$(mysql_run "DELETE FROM ado_s8_detection_log WHERE rule_code LIKE 'TEMP_SCHED_%' AND detected_at < '${CUTOFF}'; SELECT ROW_COUNT();")
|
|
|
+ echo " detection_log: deleted ${log_deleted} row(s)"
|
|
|
+ state_deleted=$(mysql_run "DELETE FROM ado_s8_rule_detection_state WHERE rule_code LIKE 'TEMP_SCHED_%' AND last_seen_at < '${CUTOFF}'; SELECT ROW_COUNT();")
|
|
|
+ echo " rule_detection_state: deleted ${state_deleted} row(s)"
|
|
|
+
|
|
|
+ if [[ "${log_deleted}" != "${log_temp_candidates}" ]]; then
|
|
|
+ echo " WARNING: detection_log delete count (${log_deleted}) != pre-count (${log_temp_candidates}) — possible concurrent insert"
|
|
|
+ fi
|
|
|
+ if [[ "${state_deleted}" != "${state_temp_candidates}" ]]; then
|
|
|
+ echo " WARNING: rule_detection_state delete count (${state_deleted}) != pre-count (${state_temp_candidates})"
|
|
|
+ fi
|
|
|
+else
|
|
|
+ echo
|
|
|
+ echo "---- DRY-RUN (no DELETE executed) ----"
|
|
|
+ echo " would-delete detection_log: ${log_temp_candidates}"
|
|
|
+ echo " would-delete rule_detection_state: ${state_temp_candidates}"
|
|
|
+ echo " re-run with --apply to actually delete"
|
|
|
+fi
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 守恒后 snapshot
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+baseline_after=$(read_baseline)
|
|
|
+demo_state_after=$(mysql_run "SELECT GROUP_CONCAT(CONCAT(id,':',enabled,':',IFNULL(paused_until,'NULL'),':',trigger_count_required,':',recover_count_required) ORDER BY id) FROM ado_s8_watch_rule WHERE id IN (10,11,12);")
|
|
|
+temp_active_exc_after=$(mysql_run "SELECT COUNT(*) FROM ado_s8_exception WHERE source_rule_code LIKE 'TEMP_SCHED_%' AND is_deleted=0;")
|
|
|
+
|
|
|
+echo
|
|
|
+echo "---- AFTER ----"
|
|
|
+echo " baseline = ${baseline_after}"
|
|
|
+echo " demo rule 10/11/12 = ${demo_state_after}"
|
|
|
+echo " TEMP active exception (is_deleted=0) = ${temp_active_exc_after}"
|
|
|
+
|
|
|
+echo
|
|
|
+echo "---- ASSERTIONS ----"
|
|
|
+fail=0
|
|
|
+if [[ "${baseline_before}" == "${baseline_after}" ]]; then
|
|
|
+ echo " PASS: baseline unchanged (${baseline_before})"
|
|
|
+else
|
|
|
+ echo " FAIL: baseline drifted ${baseline_before} → ${baseline_after}"
|
|
|
+ fail=$((fail+1))
|
|
|
+fi
|
|
|
+if [[ "${demo_state_before}" == "${demo_state_after}" ]]; then
|
|
|
+ echo " PASS: demo rule 10/11/12 conserved"
|
|
|
+else
|
|
|
+ echo " FAIL: demo rule drift before=${demo_state_before} after=${demo_state_after}"
|
|
|
+ fail=$((fail+1))
|
|
|
+fi
|
|
|
+if [[ "${temp_active_exc_before}" == "${temp_active_exc_after}" ]]; then
|
|
|
+ echo " PASS: TEMP active exception count unchanged (${temp_active_exc_before})"
|
|
|
+else
|
|
|
+ echo " FAIL: TEMP active exception drift before=${temp_active_exc_before} after=${temp_active_exc_after}"
|
|
|
+ fail=$((fail+1))
|
|
|
+fi
|
|
|
+
|
|
|
+echo
|
|
|
+echo "============================================================"
|
|
|
+if [[ "${APPLY}" == "1" ]]; then
|
|
|
+ echo "Summary: APPLY mode — deleted log=${log_deleted} state=${state_deleted}; days=${DAYS} cutoff=${CUTOFF}"
|
|
|
+else
|
|
|
+ echo "Summary: DRY-RUN — would-delete log=${log_temp_candidates} state=${state_temp_candidates}; days=${DAYS} cutoff=${CUTOFF}"
|
|
|
+fi
|
|
|
+echo "============================================================"
|
|
|
+
|
|
|
+exit $(( fail > 0 ? 1 : 0 ))
|