Files
DashBoard/openspec/changes/archive/2026-02-26-admin-perf-vue-migration-monitoring-gaps/design.md
egg 07ced80fb0 feat(admin-perf): full Vue SPA migration + slow-query/memory monitoring gaps
Remove Jinja2 template fallback (1249 lines) — /admin/performance now serves
Vue SPA exclusively via send_from_directory.

Backend:
- Add _SLOW_QUERY_WAITING counter with get_slow_query_waiting_count()
- Record slow-path latency in read_sql_df_slow/iter via record_query_latency()
- Extend metrics_history schema with slow_query_active, slow_query_waiting,
  worker_rss_bytes columns + ALTER TABLE migration for existing DBs
- Add cleanup_archive_logs() with configurable ARCHIVE_LOG_DIR/KEEP_COUNT
- Integrate archive cleanup into MetricsHistoryCollector 50-min cycle

Frontend:
- Add slow_query_active and slow_query_waiting StatCards to connection pool
- Add slow_query_active trend line to pool trend chart
- Add Worker memory (RSS MB) trend chart with preprocessing
- Update modernization gate check path to frontend style.css

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:48:54 +08:00

4.0 KiB
Raw Blame History

Context

2026-02-25 的 server crash 暴露出 pool 隔離架構變更後的監控盲區。event_fetcher 和 lineage_engine 已遷移到 read_sql_df_slow(獨立連線 + semaphore但 metrics_history 快照只記錄 pool 相關指標slow query 並行數、排隊數、Worker RSS 完全無歷史紀錄。

同時 /admin/performance 仍保留 1249 行 Jinja template 作為 Vue SPA fallback但 SPA 已是唯一使用的版本build artifact 存在於 static/dist/admin-performance.html),兩套 UI 增加維護成本且 Jinja 版功能遠不及 SPA。

logs/archive/ 目錄累積 rotated log 檔案無自動清理,是唯一會無限增長的儲存。

Goals / Non-Goals

Goals:

  • 移除 Jinja fallback統一為 Vue SPA 單一架構
  • 讓 slow query 並行數、排隊數、Worker RSS 成為可觀測的歷史趨勢指標
  • 讓 P50/P95/P99 反映所有查詢路徑pool + slow path
  • 解決 archive log 無限增長問題

Non-Goals:

  • 不修改 /admin/pages(仍為 Jinja template
  • 不新增 async job queue 面板P1後續 change 處理)
  • 不新增 event cache hit/miss 計數器P2
  • 不增加即時告警或 webhook 通知機制

Decisions

D1SQLite schema migration 策略

選擇:啟動時執行 ALTER TABLE ADD COLUMN IF NOT EXISTS(容錯 "duplicate column" error

替代方案version table + migration script → 過度工程SQLite 只有 3 天保留,加欄是向後相容的

理由:新欄位 nullable舊 row 自動為 NULL不影響既有查詢。MetricsHistoryStore.initialize() 已在啟動時執行 CREATE TABLE IF NOT EXISTS加入 ALTER TABLE 語句自然整合。

D2RSS 記憶體取得方式

選擇resource.getrusage(resource.RUSAGE_SELF).ru_maxrss * 1024Python stdlibLinux 上單位為 KB

替代方案 A:讀取 /proc/self/status VmRSS → 平台相依,解析 overhead 替代方案 Bpsutil.Process().memory_info().rss → 需新增外部依賴

理由resource 模組為 Python 標準庫,無需額外依賴。ru_maxrss 在 Linux 上返回 KB乘以 1024 轉為 bytes。

D3Semaphore 排隊計數器實作

選擇:在 read_sql_df_slow() 的 semaphore.acquire() 前後遞增/遞減 _SLOW_QUERY_WAITING atomic counter

流程

_SLOW_QUERY_WAITING += 1
acquired = semaphore.acquire(timeout=60)
_SLOW_QUERY_WAITING -= 1
if not acquired: raise RuntimeError
_SLOW_QUERY_ACTIVE += 1
... execute query ...
_SLOW_QUERY_ACTIVE -= 1

理由:與既有 _SLOW_QUERY_ACTIVE 模式一致,使用 threading.Lock 保護。

D4Archive log cleanup 整合位置

選擇:整合到 MetricsHistoryCollector._run() 的 cleanup cycle每 ~100 intervals ≈ 50 分鐘)

替代方案:獨立 cron job → 需額外 crontab 配置,不自包含

理由:已有 daemon thread 定期 cleanup SQLite加入 archive cleanup 邏輯一致且自包含。

D5移除 Jinja fallback 的安全性

選擇:直接移除 fallbackadmin_routes.py 改為只 send_from_directory(dist_dir, "admin-performance.html")

理由

  • Vue SPA build artifact 已存在(static/dist/admin-performance.html2026-02-26 更新)
  • frontend/package.json build script 已包含 admin-performance entry
  • CI/deploy 流程必包含 npx vite build
  • 若 build 失敗,/health/frontend-shell 已有 asset readiness 檢查可偵測

Risks / Trade-offs

  • [Risk] Build 失敗時 /admin/performance 返回 404 → 既有 /health/frontend-shell 檢查 + deploy script 驗證。移除 fallback 反而讓問題更早暴露。
  • [Risk] ALTER TABLE 在 SQLite 大表上可能慢 → metrics_history 最多 50K rowsALTER TABLE 即時完成。
  • [Trade-off] ru_maxrss 是 peak RSS非 current RSS → 在 Linux 上 ru_maxrss 是 process lifetime 的 max RSS。改用 /proc/self/status 的 VmRSS 可取得 current但需 file I/O。鑑於每 30 秒收集一次且 max RSS 更能反映記憶體壓力,接受此 trade-off。若日後需要 current RSS可改讀 /proc/self/status