From 8be332728e76ab928f15636efdb645ebc300118b Mon Sep 17 00:00:00 2001 From: OoO Date: Wed, 29 Apr 2026 19:56:48 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20ADR-016=20daily=5Fsales=20cache=20finge?= =?UTF-8?q?rprint=20+=204/28-29=20history=20log=20+=20CLAUDE.md=20ADR=20?= =?UTF-8?q?=E7=B4=A2=E5=BC=95=E8=A3=9C=20015/016?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 docs/adr/ADR-016:daily_sales cache TTL → TTL+DB fingerprint,棄用 N-POST broadcast hack(命中率 9.4%),引述 debugger/web-researcher/migration-engineer 三方共識;殘留 HIGH-2(app.py 43 處 _SALES_PROCESSED_CACHE)列管 - docs/memory/history_logs.md 新增 4/28-29 條目:app.py -10.8%、4 個改進版死碼揭發、 cache 機制重構、hook 9 PoC 強化、12 Agent 並行作戰實踐 - CLAUDE.md 補上 ADR-015/016 索引項 Memory(個人 auto-memory,未入 repo)同步: - feedback_flask_blueprint_shadow.md(first-registered wins) - feedback_cache_invalidation_pattern.md(DB fingerprint vs N-POST) - feedback_agent_dispatch_order.md(critic 必須先於 fullstack) - feedback_cloud_vs_local_automation.md(LAN 專案禁雲端 routine) - project_phase3e_refactor_progress.md(11 commits 戰果) --- CLAUDE.md | 4 +- .../ADR-016-daily-sales-cache-fingerprint.md | 73 +++++++++++++++++++ docs/memory/history_logs.md | 10 +++ 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 docs/adr/ADR-016-daily-sales-cache-fingerprint.md diff --git a/CLAUDE.md b/CLAUDE.md index add9813..5548dbf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -66,7 +66,9 @@ ssh wooo@192.168.0.110 "ssh ollama@192.168.0.188 \"\ * **ADR-011: Docker --remove-orphans 危險停用決策**(避免 Gitea Action 刪除非 Compose 管理的容器,如資料庫) * **ADR-012: Elephant Alpha 模型整合**(三智能體核心決策大腦,具備防爆走護欄與 Telegram 稽核通知) * **ADR-013: AIOps 自動修復服務**(實作 `AutoHealService` 處理容器異常關閉與效能瓶頸,通過 SSH Jump Executor 在跳板機隔離操作機制) -* **ADR-014: Autonomous Code Repair**(結合 Elephant Alpha 進行系統錯誤日誌Traceback掃描,自動觸發 110 主機上 Aider 自動化執行程式碼修復,並依賴 Git + Gitea CI/CD 回滾防線進行安全發布) | +* **ADR-014: Autonomous Code Repair**(結合 Elephant Alpha 進行系統錯誤日誌Traceback掃描,自動觸發 110 主機上 Aider 自動化執行程式碼修復,並依賴 Git + Gitea CI/CD 回滾防線進行安全發布) +* **ADR-015: Telegram Bot Menu 復活**(OpenClawAwoooI_Bot 三專案共用、cmd:/menu:/await: callback prefix) +* **ADR-016: daily_sales cache fingerprint**(gunicorn 多 worker cache 失效改 DB MAX/COUNT 指紋;棄用 N-POST 9.4% 命中 hack;3971fd4 已落地) | | **PPT 簡報系統 V2(ADR-014)** | [docs/adr/ADR-014-ppt-report-system-v2.md](docs/adr/ADR-014-ppt-report-system-v2.md) | ## PPT 簡報系統(9 種,V2) diff --git a/docs/adr/ADR-016-daily-sales-cache-fingerprint.md b/docs/adr/ADR-016-daily-sales-cache-fingerprint.md new file mode 100644 index 0000000..52e58f7 --- /dev/null +++ b/docs/adr/ADR-016-daily-sales-cache-fingerprint.md @@ -0,0 +1,73 @@ +# ADR-016: daily_sales cache 失效策略改用 DB Fingerprint + +- **狀態**: ✅ 已實作 +- **日期**: 2026-04-29 +- **相關 commit**: 8fefea0, 3971fd4 +- **相關 ADR**: ADR-008(188 多容器拓撲)、ADR-011(跨專案隔離) + +## 決策 + +`daily_sales_snapshot` 相關 cache 從「TTL-only」改為「TTL + DB fingerprint」雙閘失效機制: + +```python +def _get_data_fingerprint(engine, table_name='daily_sales_snapshot'): + SELECT MAX(snapshot_date)::text, COUNT(*) FROM "{table_name}" + return (max_date, count) # 任一變動 fingerprint 跳號 + +def _is_cache_valid(cache_key, engine=None, table_name=...): + 1. cache 不存在 → False + 2. TTL 過期(>300s)→ False + 3. DB fingerprint 不一致 → False + 4. fingerprint 查詢失敗 → 保守維持(return True) + 5. 全通過 → True +``` + +cache 寫入時同步記錄 `fingerprint` 欄位。 + +## 背景:為什麼不能用 N-POST broadcast hack + +舊方案:import 完成後從 `services/import_service.py` 連續 POST `/api/daily_sales/clear_cache` N 次(N = `GUNICORN_WORKERS=4`),希望命中所有 worker 各自清掉 module-level dict。 + +**實測不可靠**(debugger + web-researcher + migration-engineer 三方共識): + +- gunicorn sync worker 用 OS kernel `accept()` queue 派送,不是 round-robin +- 4 次 POST 命中全部 4 worker 機率 = 4!/4^4 ≈ **9.4%**(coupon collector) +- 需 N=20 才能拉到 ~94% 覆蓋率,N=50 才到 99.4% +- 仍永遠不到 100%,且每次 import 多 50 個 internal HTTP 完全是浪費 + +DB fingerprint 把問題從「跨 worker 廣播」翻轉成「各 worker 自查 source of truth」: +- DB 是唯一一致狀態 +- 每次 cache 命中前查一次 `MAX(snapshot_date), COUNT(*)`(< 5ms,PG 索引) +- 資料寫入 → fingerprint 跳號 → 4 worker 下一次 request 各自偵測失效並重載 +- 強一致,無 N-POST 浪費 + +## 影響 + +| 項目 | 變動 | +|------|------| +| cache 一致性窗口 | 5 min TTL → **1 個 request 週期**(強一致)| +| 每次頁面讀取額外成本 | +1 次 `SELECT MAX/COUNT`(< 5ms)| +| 跨 worker 同步機制 | 廣播(不可靠)→ DB pull(強一致)| +| `/api/daily_sales/clear_cache` 角色 | 必要清除路徑 → ops escape hatch | +| 端點安全 | 無 auth → `@login_required` + CSRF(雙重攔截,POST 無 token 回 400)| + +## 殘留技術債(未在本 ADR 範圍內) + +- **`app.py` 仍有 43 處 `_SALES_PROCESSED_CACHE` 引用**(sales_analysis 等獨立 cache,與 daily_sales_routes 中的同名 dict 是兩份不同 module-local dict) +- 後續應抽 `services/cache_manager.py` 統一管理,套用同樣的 fingerprint 模式 +- 列入 P10 Q1 後段任務 + +## 備選方案與否決理由 + +| 方案 | 否決理由 | +|------|---------| +| Redis pub/sub | 違反「先查官方文件 → 一次只動一個依賴」鐵律;新增 SPOF;KM 用 pgvector 不該被當訊息 broker(職責汙染)| +| File mtime sentinel | 容器化下 volume mount 設定繁瑣;NFS/iCloud 環境時序不穩 | +| PostgreSQL `LISTEN/NOTIFY` | 需 background thread,sync worker 不友善;待 QPS 提升或升級 gthread 後再評估 | +| 維持 N-POST hack | 9.4% 命中率本質不可靠 | + +## 紀念踩坑 + +- 8fefea0 在 critic 審查完成前已被 fullstack-engineer push(Agent dispatch 順序失誤)→ 引入 4 個 critic HIGH finding +- 3971fd4 補丁修復 HIGH-3(auth)+ HIGH-4(fingerprint),HIGH-2(app.py 43 處同名 cache)作為已知殘留技術債列管 +- HIGH-1(Flask 雙註冊衝突)為 critic 過慮:web-researcher PoC 證實 first-registered wins 不會 raise,bp 版本 shadow 為死碼 diff --git a/docs/memory/history_logs.md b/docs/memory/history_logs.md index c89584a..102d9a7 100644 --- a/docs/memory/history_logs.md +++ b/docs/memory/history_logs.md @@ -10,6 +10,16 @@ ## 📅 詳細更新日誌 (考古存檔) +### 2026-04-28~29:Phase 3e 重構大戰 + daily_sales cache 隱形 bug 根除 +- **app.py 縮減 -10.8%**: 7,386 → 6,590 行,11 commits 全綠零 502。 +- **抽 Blueprint**: `/api/categories` → `category_routes.py` (8fce73b);`/api/test_url` + `/brand_assets` → `misc_routes.py` (e676840)。 +- **揭發 4 個改進版死碼**: refactor-specialist 28 條雙重 URL 等價性矩陣指出 `/growth_analysis`、`/daily_sales`、`/api/export/excel/abc`、`/api/run_task` 在 routes/*.py 已重構但因 Flask first-registered-wins 規則被 app.py 同 URL `@app.route` shadow 從未生效。本輪先啟用 daily_sales (8fefea0),餘三條待後續。 +- **daily_sales cache 機制重構** (ADR-016): 「匯入 15323 筆但業績頁面看不到」隱形 bug 根因 = gunicorn 4 worker 各持獨立 module-level dict。先嘗試 N-POST broadcast hack 命中率僅 9.4%(debugger + web-researcher + migration-engineer 三方共識);改用 DB fingerprint (`MAX(snapshot_date), COUNT(*)`) 雙閘失效 (3971fd4),強一致無浪費。 +- **clear_cache endpoint 加 `@login_required`**: 修 critic HIGH-3,外部 POST 無 auth 回 400(CSRF + login 雙重攔截)。 +- **hook 9 PoC 強化** (136e65b): vuln-verifier 對 `.claude/hooks/momo-prod-guard.js` 跑 9 條 PoC 8 條繞過 → 補強 heredoc / writable CTE / multi-statement / pg_read_file / COPY FROM PROGRAM / `/run/secrets` / `/proc/*/environ` 全擋。 +- **12 Agent 並行作戰實踐**: 8 完成 + 4 rate-limited;學到 critic 必須先於 fullstack-engineer 完成(feedback_agent_dispatch_order.md)。 +- **殘留 HIGH-2 技術債**: app.py 仍 43 處 `_SALES_PROCESSED_CACHE`(sales_analysis 等獨立 cache)待後續抽 `services/cache_manager.py`。 + ### 2026-04-28:AI 引擎穩定性與 CI/CD 幽靈容器大規模修復 - **幽靈容器排除**: 發現 `docker-compose.yml` 漏掛載 `routes` 目錄,導致 Telegram Bot 長期執行舊代碼。 - **埠位衝突修復**: 移除 `depends_on: postgres` 以解決與 orphan `momo-db` 的 5432 埠位競爭問題。