# INV-1: Redis Key Inventory **版本**:v1.0 初稿 **日期**:2026-05-03(台北) **範圍**:`apps/api/src/` 全 codebase grep **用途**:Phase 2 三階段雙寫遷移的完整 Redis key 清單 --- ## 使用方式 本 INV 列出所有 Redis key pattern、TTL、用途、寫入點、讀取點、是否硬碼跨兩處。 Phase 2 遷移按批次執行:P0(Critical)→ P1(高風險)→ P2(業務資料)→ P3(低影響) --- ## 1. P0 — Critical(必須優先遷移) | Key Pattern | 用途 | TTL | 寫入點 | 讀取點 | 問題 | 新 Pattern(Phase 2 後)| |-------------|------|-----|--------|--------|------|------------------------| | `ollama:current_primary` | Ollama primary URL 選擇 | 無 | `ollama_auto_recovery.py` (兩處!) | `ollama_failover_manager.py` | 🔴 P0-11:第二定義 at ollama_auto_recovery.py:230;三層 GCP 拓撲無法塞入單一 URL | `platform:ollama:topology`(JSON:primary/secondary/fallback)| | `ollama:gemini_daily_count:{date}` | Gemini 每日使用計數 | 24h | `ollama_auto_recovery.py` | same | 🔴 無 project 前綴,多 tenant 共用 | `platform:ollama:gemini_daily_count:{date}` | | `telegram:polling:leader` | Telegram polling pod 鎖 | ~30s | `telegram_gateway.py:55` | same | 🔴 platform_resource,應明確標記 | `platform:telegram:polling:leader` | | `telegram_silence:{target}` | 告警靜默控制 | 設定值 | `telegram_gateway.py:4079`(用 SILENCE_KEY_PREFIX)| `decision_manager.py:240`(🔴 硬碼 `telegram_silence:{target}`,未 import 常數)| P1-24:兩處定義不一致 | `{project_id}:telegram:silence:{target}` | | `ai_rate:total_cost:{provider}` | 費用上限控制 | 無 | `ai_rate_limiter.py` | `ai_router.py` | 🔴 無 project 前綴,AWOOOI 費用計數會與 EwoooC 混算 | `{project_id}:ai_rate:total_cost:{provider}` | ## 2. P1 — 高風險(Phase 2 第二批) | Key Pattern | 用途 | TTL | 寫入點 | 讀取點 | 問題 | 新 Pattern | |-------------|------|-----|--------|--------|------|------------| | `approval:{run_id}:*` | approval 審核狀態 | 15min | `multi_sig_redis.py` | same | 🔴 P1-15:無 trace_id;approval_token 未簽章 | `{project_id}:approval:{run_id}:*`(Phase 8 後加 jti)| | `incident:{incident_id}` | incident working memory | 24h | `incident_service.py` | `incident_service.py:603`(SCAN `incident:*`)| 🔴 P1-18:SCAN 無 prefix 篩選 | `{project_id}:incident:{incident_id}` | | `consensus:{*}` | consensus engine 狀態 | varies | `consensus_engine.py`(CONSENSUS_PREFIX="consensus:")| same | 🔴 P0-12:無 project 前綴,multi-tenant 共用 | `{project_id}:consensus:{*}` | | `playbook:{playbook_id}` | playbook cache | 1h | `playbook_repository.py` | same | 🟠 無 project,但目前 AWOOOI 只有一個 tenant | `{project_id}:playbook:{playbook_id}` | | `playbook:embedding:index` | embedding index | 無 | `playbook_repository.py` | same | 🟠 無 project | `{project_id}:playbook:embedding:index` | | `playbook:index:alert:{alert_name}` | alert → playbook 映射 | 1h | `playbook_repository.py` | same | 🟠 無 project | `{project_id}:playbook:index:alert:{alert_name}` | | `playbook:embedding:{playbook_id}` | embedding vector | 無 | `playbook_repository.py` | same | 🟠 無 project | `{project_id}:playbook:embedding:{playbook_id}` | | `km:backfill:dlq` | KM backfill dead letter queue | 無 | `km_service.py` | same | 🟠 無 project | `{project_id}:km:backfill:dlq` | ## 3. P2 — 業務資料(Phase 2 第三批) | Key Pattern | 用途 | TTL | 寫入點 | 讀取點 | 問題 | 新 Pattern | |-------------|------|-----|--------|--------|------|------------| | `anomaly:disposition:{alert_id}` | 告警處置決定 | 7d | `anomaly_counter.py:790` | same | 🔴 P1-17:AnomalyCounter 全域單例,6 個 prefix 無 tenant | `{project_id}:anomaly:disposition:{alert_id}` | | `anomaly:metadata:{alert_id}` | 告警 metadata | 7d | `anomaly_counter.py` | same | 同上 | `{project_id}:anomaly:metadata:{alert_id}` | | `anomaly:permanent_fix:{alert_id}` | 永久修復紀錄 | 30d | `anomaly_counter.py` | same | 同上 | `{project_id}:anomaly:permanent_fix:{alert_id}` | | `anomaly:repair_count:{alert_id}` | 修復計數 | 7d | `anomaly_counter.py` | same | 同上 | `{project_id}:anomaly:repair_count:{alert_id}` | | `anomaly:repair_history:{alert_id}` | 修復歷史 | 30d | `anomaly_counter.py` | same | 同上 | `{project_id}:anomaly:repair_history:{alert_id}` | | `anomaly:timeline:{alert_id}` | 告警時間線 | 7d | `anomaly_counter.py` | same | 同上 | `{project_id}:anomaly:timeline:{alert_id}` | | `hermes:session:{chat_id}:{user_id}` | Hermes 對話狀態 | 30min | `hermes/nl_gateway.py:146` | same | 🔴 P1-16:無 project 前綴 | `{project_id}:hermes:session:{chat_id}:{user_id}` | | `hermes:rl:{chat_id}` | Hermes rate limit | 1min | `hermes/nl_gateway.py:163` | same | 🔴 P1-16 | `{project_id}:hermes:rl:{chat_id}` | | `hermes:approvers` | Hermes 審核者清單 | 無 | `hermes/nl_gateway.py:7` | same | 🔴 P1-16 | `{project_id}:hermes:approvers` | | `session:{session_id}` | agent session | varies | `agent_sessions service` | same | 🟠 無 project | `{project_id}:session:{session_id}` | | `tg_sent:{fingerprint}` | Telegram 去重 dedup | 10min | `telegram_gateway.py` | same | 🟢 ADR-109 已定(dedup 相容性);注意 EwoooC Telegram 需要不同 fingerprint scope | `{project_id}:tg_sent:{fingerprint}` | | `telegram:{user_id}:{*}` | Telegram session / callback | varies | `telegram_gateway.py` | same | 🟠 無 project | `{project_id}:telegram:{user_id}:{*}` | ## 4. P3 — 低影響(Phase 2 最後) | Key Pattern | 用途 | TTL | 問題 | 新 Pattern | |-------------|------|-----|------|------------| | `playbook:*`(SCAN)| playbook 全量掃描 | — | 🟠 SCAN 無 prefix 篩選 | 禁止裸 SCAN,改 `SCAN {project_id}:playbook:*` | | `approval:123:lock`(測試用)| 測試 key | — | 🔵 hardcode 測試字串,清理 | 移除 | --- ## 5. Platform Resource(不按 project 隔離,明確標記) | Key Pattern | 理由 | 備註 | |-------------|------|------| | `platform:telegram:polling:leader` | 全域 polling leader 鎖,不屬於任何 tenant | ADR-111 platform_resource | | `platform:ollama:topology` | GCP-A/GCP-B/Local 三層路由狀態,所有 tenant 共用 | ADR-111 + ADR-110 | | `platform:ollama:gemini_daily_count:{date}` | 全 platform Gemini 緊急路由計數 | ADR-111 | --- ## 6. 遷移執行順序(三階段) ``` Phase A(雙寫,30 天觀察): 新 key 寫入 + 舊 key 繼續寫入 讀取仍以舊 key 為主 監控:舊 key 讀取次數 / 新 key 命中次數 Phase B(雙讀,14 天): 新 key 為主,舊 key 為 fallback 讀不到新 key → 讀舊 key,並回填新 key 監控:舊 key fallback 率必須 < 1% Phase C(移除舊 key 寫入): 停止寫入舊 key 舊 key 自然過期 監控:確認舊 key 0 讀取後才進行 ``` --- ## 7. 驗收標準 - [ ] 所有 P0 key 完成 Phase A 雙寫 - [ ] `decision_manager.py:240` 改為 import `telegram_gateway.SILENCE_KEY_PREFIX` - [ ] `ollama:current_primary` 改為 `platform:ollama:topology`(JSON 格式) - [ ] 無任何裸 `SCAN *` 或 `SCAN prefix:*`(改為 `SCAN {project_id}:prefix:*`) *最後更新:2026-05-03(台北)*