feat: alert dedupe, proposal state API, HITL mirrors API approvals, Jev recovering reads the alert condition
第 4 回キャンペーン(2026-10-08)の 3 つの穴を塞ぐ。
- ハーネス: 同じアラート名が ALERT_DEDUPE_S(既定 300 秒)以内に再着したら
/diagnose を送らず note(dedupe) に記録する。Grafana は Pod ごとに 1 件ずつ
出し 30 秒後にグループを再送するため、1 障害が診断 4 件・提案 4 件になっていた。
全件 resolved になれば窓をリセットする。
- ハーネス: GET /proposals/{token} で承認・実行状態を返す。
- hitl: 60 秒ごとに未決の提案をハーネスへ照会し、API で承認された件は
メッセージを「ハーネス API で承認」に書き換えてタイマーを外す。期限切れ処理の
直前にも同じ照会を行う。
- AI: Jev 選別の now に alert_condition_now / alert_condition_clear /
recovered_now を追加し、recovering は recovered_now を読む。SLO に触れない
障害(replay 停止、メモリ圧迫)で 0.98 を返していた。now の p95 と 5xx 比率に
route フィルタを付け、判定と同じ式にそろえる。
This commit is contained in:
1 parent
8836b37927
commit
f8e0aa9122
9 files changed
+229
-9
No files matched your search
@@ -9,6 +9,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -33,6 +34,7 @@ type Config struct {
|
||||
DBPassFile string // harness role password on db-primary / db-replica
|
||||
HumanTokFile string
|
||||
AIDiagnoseURL string
|
||||
AlertDedupeS int // layer-2 alerts with the same name inside this window share one diagnosis (0 = off)
|
||||
ScenarioDir string
|
||||
JudgeEvery time.Duration
|
||||
ObserveMax time.Duration
|
||||
@@ -62,6 +64,7 @@ func loadConfig() Config {
|
||||
DBPassFile: env("DB_PASSWORD_FILE", "/run/secrets/db-password"),
|
||||
HumanTokFile: env("HUMAN_TOKEN_FILE", "/run/secrets/harness-token"),
|
||||
AIDiagnoseURL: env("AI_DIAGNOSE_URL", "http://10.20.0.12:8080/diagnose"),
|
||||
AlertDedupeS: envInt("ALERT_DEDUPE_S", 300),
|
||||
ScenarioDir: env("SCENARIO_DIR", "/etc/harness/scenarios"),
|
||||
JudgeEvery: 30 * time.Second,
|
||||
ObserveMax: 30 * time.Minute,
|
||||
@@ -115,6 +118,7 @@ func main() {
|
||||
mux.HandleFunc("POST /runs/{id}/abort", app.handleRunAbort)
|
||||
mux.HandleFunc("POST /hooks/alert", app.hooks.handleWebhook)
|
||||
mux.HandleFunc("POST /proposals", app.handleProposal)
|
||||
mux.HandleFunc("GET /proposals/{token}", app.handleProposalGet)
|
||||
mux.HandleFunc("POST /proposals/{token}/approve", app.handleProposalApprove)
|
||||
mux.HandleFunc("POST /proposals/{token}/reject", app.handleProposalReject)
|
||||
mux.HandleFunc("POST /actions/execute", app.handleActionExecute)
|
||||
@@ -208,3 +212,12 @@ func (a *App) handleScenarios(w http.ResponseWriter, r *http.Request) {
|
||||
sortScenarios(list)
|
||||
writeJSON(w, 200, list)
|
||||
}
|
||||
|
||||
func envInt(k string, d int) int {
|
||||
if v := os.Getenv(k); v != "" {
|
||||
if n, err := strconv.Atoi(v); err == nil {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
Reference in new issue
Block a user