# ADR-009:Embedding Retry Queue 持久化(DB-backed) - **Status**: Accepted - **Date**: 2026-04-19 - **Decision Maker**: 統帥 - **Author**: Claude ## Context ADR-007(AI 學習雙寫規範)要求 `store_insight()` 必須同時寫入 `ai_insights` DB 表與 `embedding` 欄位。 初版實作(2026-04-18)使用 Python `queue.Queue()` 記憶體佇列 + daemon thread worker: ```python _embedding_queue = Queue() # 記憶體 threading.Thread(target=_embedding_worker, daemon=True).start() ``` **破洞**: - Python 進程重啟(K8s Pod 滾動重啟、gunicorn reload、OOM kill)→ queue 全部遺失 - Ollama 主機 192.168.0.111 短暫斷線 → 該筆 insight 永遠沒 embedding - 違反 ADR-007「雙寫必達」精神:DB 有寫但 KM 沒寫 → RAG 永遠找不到這筆洞察 ## Decision **Embedding queue 改為 DB 持久化**(`embedding_retry_queue` 表),worker 改為輪詢批次處理。 | 面向 | 記憶體 Queue(舊) | DB Queue(新) | |---|---|---| | 持久化 | ❌ 重啟遺失 | ✅ 持久化 | | Retry | ❌ 無 | ✅ attempts 欄位 + 上限 5 次 | | 觀測性 | ❌ 無法查詢 | ✅ SQL 即可 | | 跨進程 | ❌ 每 Pod 各自一份 | ✅ 多 Pod 共享同一 queue | | 吞吐 | ✅ 最快 | ✅ 批次 10 筆/分鐘已足夠 | ## Schema(Migration 011) ```sql CREATE TABLE embedding_retry_queue ( id SERIAL PRIMARY KEY, target_table VARCHAR(50) NOT NULL, -- ai_insights / conversations target_id INTEGER NOT NULL, text_content TEXT NOT NULL, model VARCHAR(50) DEFAULT 'bge-m3:latest', status VARCHAR(20) DEFAULT 'pending', -- pending / processing / done / failed attempts INTEGER DEFAULT 0, last_error TEXT, created_at TIMESTAMP, updated_at TIMESTAMP, processed_at TIMESTAMP ); ``` ## Worker 流程 ``` Worker Loop (每 60 秒): SELECT * FROM embedding_retry_queue WHERE status='pending' AND attempts < 5 ORDER BY created_at LIMIT 10 ↓ 對每筆: UPDATE status='processing' 呼叫 Ollama bge-m3 生成 embedding ↓ 成功 → UPDATE target SET embedding = vec; UPDATE retry_queue SET status='done', processed_at=NOW() 失敗 → UPDATE retry_queue SET attempts=attempts+1, last_error=... 若 attempts ≥ 5 → status='failed'(人工處理) ``` ## Consequences ### Positive - 即使 Pod 重啟也不會丟 embedding 任務 - 失敗累積可視化(可寫 Grafana 面板監控 `status='failed'` 數量) - 多 Pod 共用同一 queue,不重複處理(processing 狀態互斥) ### Negative - 延遲從「毫秒」拉長到「最多 60 秒」(輪詢間隔) - 緩解:AI 洞察不需要即時 embedding,RAG 查詢 < 1 分鐘延遲可接受 - DB 需多一張表(小幅維運成本) ## Related ADRs - ADR-007(雙寫規範):本 ADR 是其 Step 4 的持久化實作 - ADR-003(本地 embedding):worker 呼叫的後端