上一篇,AI 在 EXP-002 證實:280 個 VUs 持續送入轉帳時,對 account-service 注入 10 秒延遲,會讓 teller-service 的 thread 被占滿,最後觸發兩次重啟。報告也把 Circuit Breaker 和 HTTP client timeout 列為 P0 修復。
根據修復建議改完之後,teller-service 真的撐得住嗎?
chaos-agent 先把報告建議整理成修復的交接文件。接著,我到 lite-bank-demo repo,把這份文件交給 AI,請它依照內容修改程式碼。新版部署完成後,我再讓 chaos-agent 用相同條件執行 EXP-003,比對修復前後的結果。
AI 在交接文件中列出四個修改重點:
RestTemplate 設定 connect timeout 1 秒、read timeout 2 秒。AccountServiceClient 與 TransactionServiceClient 加上 Circuit Breaker 和 fallback。register-health-indicator 設為 false,避免 Circuit Breaker OPEN 時讓 health probe 跟著變成 DOWN。最後一點的意思是 AI 也注意到 Circuit Breaker 開啟可能影響 health check,因此要求將 register-health-indicator 設為 false,避免 EXP-003 的驗收結果受到干擾。
交接文件也有先列出修復後的驗收預期,準備交給 EXP-003 驗證。
我在 lite-bank-demo repo 請 AI 依照這份規格修改程式碼,完成後再重新部署。接著,我請 AI 更新 llm-wiki 的程式碼來源並重新 ingest。確認 AI 已經讀到修復後的版本,我才把 EXP-003 驗收任務交給 chaos-agent。
程式更新並重新部署後,AI 再次規劃跟 EXP-002 一樣的故障與負載條件:
teller-service → account-service 延遲 10 秒。這次要驗證加入 Circuit Breaker 與 timeout 後,teller-service 是否在相同條件下維持運作。
EXP-002 預期 teller-service 會被拖垮。EXP-003 的假設變成 teller-service 應該維持 Ready、不重啟,Circuit Breaker 應該會打開。呼叫下游服務超過 2 秒還沒回應時,就停止等待,讓 thread 可以繼續處理其他請求。
這次一樣挑幾個有趣的地方說明。先看 AI 在實驗前寫下的假設:

圖:AI 根據修復內容,將 EXP-002 的結果反轉成 EXP-003 驗收條件。
這份假設有三個重點。第一,Circuit Breaker 打開代表保護機制開始作用。進入 OPEN 後,新的下游呼叫會直接失敗;經過一段時間再切到 HALF_OPEN,放少量請求出去確認下游是否恢復。
第二,read timeout 將每次等待下游的時間限制在 2 秒。即使轉帳仍然失敗,thread 也能繼續處理下一個請求,不會再被 10 秒延遲長時間占住。
第三,這場驗收觀察 up、readiness 與 restart count。故障持續期間,轉帳本來就可能失敗;只要 teller-service 還能回應、維持 Ready 並且沒有重啟,就代表服務仍在降級運作。
我回頭對照修復交接文件時,發現 AI 漏了一項預期,交接文件原本寫著 tomcat_threads_busy 應該會遠低於 200,到了 EXP-003 的正式假設,只剩下「thread 不會耗盡」,沒有保留這項 busy 預期。

圖:交接文件列出的 EXP-003 驗收預期,其中 tomcat_threads_busy 預期會遠低於 200。
這裡可以看到交接文件與實驗假設之間出現了落差。交接文件中可以量測的預期,應該逐項帶進實驗假設與驗收條件。等結果出來後,才能直接判斷每一項預測是否成立。
正式測試前,AI 先送 10 筆轉帳暖機,確認新版 teller-service 能正常處理請求、Prometheus 抓得到 metrics,Circuit Breaker 也處於正常放行的 CLOSED 狀態。環境確認沒問題後,才把負載提高到 280 個 VUs。
EXP-003 完成後,AI 將修復前後實驗的 up、readiness 與 tomcat_threads_busy 整理成圖表,並放到報告內:

圖:AI 產生的對照圖表。紅線是修復前,綠線是修復後。
圖表最上方的 up 代表 Prometheus 能不能順利抓到 teller-service 的 metrics;中間是 readiness;最下方是忙碌中的 Tomcat thread 數量。
先看最上方的 up 圖。EXP-002 的紅線從 1 掉到 0,表示 Prometheus 已經抓不到 teller-service 的 metrics;EXP-003 的綠線則全程維持 1。
接著看中間的 readiness 圖。EXP-002 的紅線在實驗期間掉到 0,代表 teller-service 變成 NotReady;EXP-003 的綠線全程維持 Ready。
最下方是忙碌中的 Tomcat thread 數量。兩場實驗都到達 200,交接文件原本預期修復後會遠低於 200,這項預測沒有成立。280 個 VUs 本來就超過 200 條 Tomcat thread,只要負載持續送進來,busy 仍然可能滿載。
AI 接著把 busy 跟 up、readiness 及 Circuit Breaker 狀態放在一起比較:

圖:AI 將 busy、服務存活訊號與 Circuit Breaker 狀態放在一起比較。
先看最上方的 busy。EXP-003 期間一樣到達 200,代表 200 條 Tomcat thread 都在忙。修復前,每條 thread 需要等待 10 秒;加入 read timeout 後,等待超過 2 秒就會停止,thread 可以繼續處理下一個請求。因此 busy 雖然同樣是 200,修復後的 thread 還能持續週轉。
接著看中間的存活訊號。up 與 readiness 全程維持 1,再搭配 Kubernetes 執行紀錄中的 restart=0,可以確認 teller-service 持續存活,也沒有被重啟。
最後看下方的 Circuit Breaker。OPEN 代表暫時停止呼叫異常的 account-service,直接回傳失敗;HALF_OPEN 會放少量請求通過,測試 account-service 是否恢復。
AI 同時發現一個實驗限制:280 VUs 在正式注入延遲前,就已經讓 Circuit Breaker 打開。
這次實驗可以確認修復後的 teller-service 能在相同故障與負載下存活,卻無法單獨證明 10 秒延遲就是 Circuit Breaker 打開的原因。
在相同的 280 VUs 與 10 秒下游延遲下,修復後的 teller-service 持續存活,也沒有被重啟。這組對照結果可以確認 Circuit Breaker 與 timeout 的組合修復有效。
account-service 變慢時,轉帳仍可能失敗。read timeout 會在等待 2 秒後停止下游呼叫;Circuit Breaker 進入 OPEN 後,則會暫停呼叫 account-service,直接回傳失敗,讓 teller-service 維持降級運作。
AI 最後將 EXP-003 判定為 PASS,並把 TEL-W001 與 TEL-W002 更新成 mitigated。報告也提出兩個後續建議:
EXP-003 確認 AI 提出的 Circuit Breaker 與 timeout 的組合修復有效。在相同故障與負載下,修復後的 teller-service 持續執行、維持 Ready,也沒有再次重啟。
過程中也看到交接文件原本預測 busy 會遠低於 200,AI 卻沒有把這項預期寫進 EXP-003 的正式假設,最後實驗結果也跟預測不同。這項落差最後是我在檢視整體流程時才發現的。
下一篇會整理這兩場實驗,看看 AI 到底完成了哪些混沌工程工作,以及整個流程還有哪些地方可以改進。
今天就先寫到這,我們明天見!