fix(api): Review #2 技術債清理 — I1/S1/S2/S3 全數修正
All checks were successful
CD Pipeline / build-and-deploy (push) Successful in 12m13s

I1: error_type 欄位補全
- AnomalyCounter.derive_key_from_incident() 新增
  從 signal.labels 提取 reason/error_type,確保四欄位完整

S1: 三處 signature 建構邏輯統一
- auto_repair_service._derive_anomaly_key() → 委託 derive_key_from_incident()
- approval_execution._get_anomaly_key_from_approval() → 同上
- incident_service.resolve_incident() B4 → 同上
- 消除 3 處重複的 signature 建構程式碼

S2: Redis Pipeline 批次查詢
- get_all_disposition_stats() 從 N+1 hgetall 改為 2 次 Pipeline
- Pipeline 1: 批次 hgetall 所有 disposition key
- Pipeline 2: 批次 hget metadata (alert_name)
- 效能從 O(2N) Redis round-trip 降至 O(2)

S3: auto_repair.py get_incident AttributeError 修復
- get_incident() → get_from_working_memory() (pre-existing bug)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-07 13:13:42 +08:00
parent b20a619a3d
commit b7ea362efc
5 changed files with 58 additions and 47 deletions

View File

@@ -219,11 +219,8 @@ class ApprovalExecutionService:
async def _get_anomaly_key_from_approval(self, approval: ApprovalRequest) -> str | None:
"""
從 approval → incident → anomaly_signature → hash
2026-04-07 Claude Code: Sprint 4 B3
Returns:
anomaly_key or None if not derivable
從 approval → incident → anomaly_key
2026-04-07 Claude Code: I1+S1 Fix — 委託 AnomalyCounter.derive_key_from_incident()
"""
try:
if not approval.incident_id:
@@ -231,18 +228,10 @@ class ApprovalExecutionService:
from src.services.incident_service import get_incident_service
incident_service = get_incident_service()
incident = await incident_service.get_from_working_memory(approval.incident_id)
if not incident or not incident.signals:
if not incident:
return None
# 從第一個 signal 建立 anomaly signature
# P0-1 Fix: namespace 從 signal.labels 取,非 getattr
signal = incident.signals[0]
signature = {
"alert_name": signal.alert_name,
"service": incident.affected_services[0] if incident.affected_services else "",
"namespace": signal.labels.get("namespace", "") if signal.labels else "",
}
from src.services.anomaly_counter import AnomalyCounter
return AnomalyCounter.hash_signature(signature)
return AnomalyCounter.derive_key_from_incident(incident)
except Exception as e:
logger.warning("get_anomaly_key_from_approval_failed", error=str(e))
return None