ServiceFlow

這個 FastAPI 訂單及支援工單 API,是我練習應用程式故障排查的項目。我從 API 回應、請求日誌及資料庫紀錄着手,找出報表查詢緩慢、付款失敗處理及訂單金額不一致的原因,再以回歸測試核對修正結果。

公開儲存庫 · 本機產生的測試資料 · 無網上示範

PythonFastAPISQLitepytest應用支援

本機驗證結果

服務層彙總時間

90.26 ms → 2.11 ms

本次本機測試為 42.8 倍 · 預熱 1 次,計時 5 次取中位數

SQLite 測試資料

500 名客戶 / 2,000 筆訂單

5,966 項訂單明細

回歸測試

28 項 pytest 測試

同一套測試在正常和故障模式下均通過

問題排查

以下問題均以程式產生的資料模擬。每份排查紀錄都附有重現步驟、相關程式碼及測試,可逐一核對處理過程。

INC-001 訂單報表查詢緩慢

發現的問題
報表回傳 HTTP 200 及預期的 500 行資料,但逐個客戶查詢的 N+1 迴圈,加上缺少客戶索引,拖慢了查詢。
修正方式
為客戶欄位加入索引,再以一條 JOIN … GROUP BY 查詢取代逐個客戶執行的迴圈。

INC-002 付款失敗處理

發現的問題
付款客戶端沒有設定逾時限制,扣款失敗後訂單仍標記為 paid。
修正方式
加入 3 秒客戶端逾時設定,成功扣款後才標記訂單為 paid;失敗時回傳統一的 502 錯誤,不透露內部資訊,亦不自動重試。

INC-003 訂單資料驗證

發現的問題
API 接受了數量為負數或未填價格的明細,並儲存了錯誤的訂單總額。
修正方式
儲存訂單前先檢查數量及價格:qty 至少為 1,價格必填且不可為負數。無效請求不會新增訂單。

訂單報表為何查詢緩慢

  1. 先確認報表仍回傳 HTTP 200 及預期的 500 行資料,將問題範圍收窄至查詢時間,而非遺漏結果。
  2. 檢查服務程式碼後發現,它先查詢一次客戶清單,再為每名客戶分別執行 SUM 和 COUNT,合共需要 1 + 2N 次資料庫查詢。故障設定下,orders.customer_id 亦沒有索引。
  3. 修正同時處理這兩個問題:為 orders.customer_id 加入索引,並改用一次分組彙總。回歸測試核對計算結果,以及兩種模式下索引是否存在。
索引檢查:單一客戶的 SUM 查詢

SCAN orders
SEARCH orders USING INDEX ix_orders_customer_id

這裏以 EXPLAIN QUERY PLAN 檢查一條獨立的客戶 SUM 查詢,確認索引如何改變查詢方式。完整 JOIN … GROUP BY 報表查詢的執行計劃另存於儲存庫。

預熱 1 次、計時 5 次後,服務函式執行時間的中位數由 90.26 ms 降至 2.11 ms。量度在同一個程序內完成,不包括 HTTP 請求開銷。基準檔案保留了每次量度值,換一部電腦執行時,所需時間會有所不同。

範圍與限制

  • 項目以程式產生的資料在本機模擬故障。這些結果並非正式環境事故處理、SLA 達標情況或當值支援經驗。
  • 量度及測試均使用 SQLite。PostgreSQL 可以設定,但尚未驗證。API 沒有身份驗證或角色權限控制。
  • 付款流程需要本機模擬付款 API。測試檢查了 httpx 逾時參數,以及拋出 TimeoutException 時的處理方式,未量度完整請求流程的逾時時間。若加入付款重試,需要先設計冪等鍵。