Files
DashBoard/openspec/changes/archive/2026-02-09-harden-mid-section-defect/design.md
egg af59031f95 feat(mid-section-defect): harden with distributed lock, rate limit, filter separation, abort, SQL classification and tests
Address 6 code review findings (P0-P3): add Redis distributed lock to prevent
duplicate Oracle pipeline on cold cache, apply rate limiting to 3 high-cost
routes, separate UI filter state from committed query state, add AbortController
for request cancellation, push workcenter group classification into Oracle SQL
CASE WHEN, and add 18 route+service tests. Also add workcenter group selection
to job-query equipment selector and rename button to "查詢".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 09:32:14 +08:00

4.4 KiB
Raw Blame History

Context

/mid-section-defect 頁面已上線功能完整但缺乏生產環境保護機制。Code review 揭示 6 個問題,依嚴重度分為 P0-P3。現有基礎建設try_acquire_lockconfigured_rate_limitcreateAbortSignal)均可直接複用,無需新增框架。

現有架構

  • Backend: query_analysis() (line 82-184) 含 5min Redis 快取 → query_analysis_detail() (line 187-224) 呼叫前者取快取結果再分頁
  • Frontend: Promise.all([summary, detail]) 平行載入 → useAutoRefresh 5min 自動刷新
  • 上游歷史: SQL 查全量 → Python get_workcenter_group() 逐行分類 + order 4-11 過濾

Goals / Non-Goals

Goals:

  • 消除首次查詢的雙倍 Oracle 管線執行P0
  • 保護高成本路由免受暴衝流量P1a
  • 確保 UI 篩選變更不會汙染進行中的 API 呼叫P1b
  • 新查詢自動取消舊的進行中請求P2a
  • 善用 Oracle Server 做 workcenter 分類支援全線歷程追蹤P2b
  • 基礎測試覆蓋防止回歸P3

Non-Goals:

  • 不改變 API response 格式
  • 不重構 query_analysis() 管線邏輯
  • 不加入前端 UI 新功能
  • 不處理 export_csv() 的串流效能(目前可接受)
  • 不做 DuckDB 中間層或背景預計算

Decisions

D1: 分散式鎖策略 — Redis SET NX 輪詢等待

選擇: 使用既有 try_acquire_lock() + 輪詢 cache_get() 等待模式。 替代方案: (A) Pub/Sub 通知 — 複雜度高,需新增 channel 管理;(B) 前端序列化 — 改 Promise.all 為先 summary 再 detail但仍有自動刷新與手動查詢並行問題。 理由: 鎖機制在 service 層統一保護所有入口包含未來新路由fail-open 設計確保 Redis 故障不阻塞。輪詢 0.5s 間隔在 5-35s 典型管線執行時間下損耗可忽略。

D2: Rate limit 預設值 — 依路由成本分級

選擇: /analysis 6/60s、/detail 15/60s、/export 3/60s。 理由: /analysis 冷查詢 35s每分鐘最多 6 次已足夠(含自動刷新)。/detail 分頁翻頁頻率高但走快取15 次寬裕。/export 觸發全量串流3 次防誤操作。/loss-reasons 已有 24h 快取,無需限速。

D3: 篩選分離 — committedFilters ref 快照

選擇: 新增 committedFilters ref按「查詢」時從 filters reactive 快照。所有 API 函式讀 committedFilters替代方案: (A) deep watch + debounce — 會在使用者輸入中途觸發查詢;(B) URL params 持久化 — 此頁面不需要書籤分享功能。 理由: 最小改動,與 resource-history/App.vuebuildQueryString() 模式一致。filters reactive 繼續作為 UI 雙向繫結,committedFilters 是「上次查詢使用的參數」。

D4: AbortController — keyed signal 設計

選擇: 'msd-analysis' key 用於查詢summary + detail page 1 共用),'msd-detail' key 用於獨立翻頁。 理由: 新查詢取消舊查詢的所有請求(含翻頁中的 detail翻頁取消前一次翻頁但不影響進行中的查詢。與 wip-detail/App.vue 相同模式。

D5: 上游歷史 SQL 端分類 — CASE WHEN 全線保留

選擇: SQL CTE 內加 CASE WHENWORKCENTERNAME 分類為 WORKCENTER_GROUP12 組 + NULL fallbackPython 端直接讀取分類結果,不過濾任何站點。 替代方案: (A) Oracle 自訂函式 — 需 DBA 部署;(B) 維持 Python 端分類但移除過濾 — 仍有 10K+ 行逐行 regex 開銷。 理由: CASE WHEN 在 Oracle 查詢引擎內原生執行,無 row-by-row function call 開銷。分類邏輯與 workcenter_groups.py 的 patterns 完全對齊,但需注意 CASE 順序exclude-first: 元件切割切割 之前)。

Risks / Trade-offs

  • [P0 鎖等待超時] 若管線執行 >90s極大日期範圍等待方可能超時後自行查詢 → 緩解API_TIMEOUT 本身 120s鎖 TTL 120s 會自動釋放,最壞情況退化為當前行為(雙查詢)
  • [P2b SQL 分類與 Python 不一致]workcenter_groups.py 新增/修改 pattern 但忘記同步 SQL → 緩解SQL 的 NULL fallback 確保不會遺失行,僅分類名稱可能為 NULL
  • [Rate limit 誤擋] 高頻翻頁或自動刷新可能觸發限速 → 緩解:/detail 15/60s 已足夠正常翻頁(每 4s 一頁),自動刷新 5min 間隔遠低於 /analysis 6/60s 門檻