| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- #!/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 ))
|