# Design: Optimize WebSocket Database Sessions ## Context Task Reporter 使用 WebSocket 進行即時通訊,目前實作在 WebSocket 連線期間持有單一資料庫 Session。這在少量用戶時可行,但隨著用戶增加會造成連線池耗盡。 **現況分析:** - 連線池: 5 + 10 = 15 個連線 - WebSocket 持有 Session 直到斷線 - 50 用戶同時在線 = 需要 50 個 Session - 結果: 連線池耗盡,後續請求阻塞 ## Goals / Non-Goals **Goals:** - 支援 100+ 並發 WebSocket 連線 - 資料庫操作即時寫入(不使用佇列) - 修復 sequence_number 競爭條件 - 可配置的連線池參數 **Non-Goals:** - 不改用 async SQLAlchemy(保持簡單) - 不實作訊息佇列(維持即時寫入) - 不改變 API 介面 ## Decisions ### Decision 1: 短期 Session 模式 **選擇:** 每次 DB 操作使用獨立 Session,操作完成立即釋放。 **實作:** ```python # database.py @contextmanager def get_db_context(): db = SessionLocal() try: yield db finally: db.close() # router.py (改前) db = next(get_db()) while True: message = create_message(db, ...) # 共用 Session # router.py (改後) while True: with get_db_context() as db: message = create_message(db, ...) # 每次獨立 ``` **替代方案考慮:** 1. **連線池擴大**: 只增加 pool_size 到 100+ - 優點: 最少改動 - 缺點: 浪費資源,MySQL 連線數有限 2. **Async SQLAlchemy**: 使用 aiomysql - 優點: 真正非阻塞 - 缺點: 需要大幅重構,增加複雜度 3. **訊息佇列**: Redis/內存佇列 + 批次寫入 - 優點: 最高效能 - 缺點: 複雜度高,可能丟失資料 ### Decision 2: Sequence Number 鎖定策略 **選擇:** 使用 `SELECT ... FOR UPDATE` 鎖定 + 重試機制 ```python def create_message(db, room_id, ...): max_retries = 3 for attempt in range(max_retries): try: # 使用 FOR UPDATE 鎖定該房間的最大 sequence max_seq = db.execute( text("SELECT MAX(sequence_number) FROM tr_messages WHERE room_id = :room_id FOR UPDATE"), {"room_id": room_id} ).scalar() next_seq = (max_seq or 0) + 1 # ... create message ... db.commit() return message except IntegrityError: db.rollback() if attempt == max_retries - 1: raise ``` **替代方案:** 1. **AUTO_INCREMENT 子欄位**: 每個房間獨立計數器表 - 需要額外表,增加 JOIN 成本 2. **樂觀鎖**: 使用版本號重試 - 高並發時重試次數可能很高 ### Decision 3: 連線池配置 **選擇:** 環境變數可配置 ```python # 生產環境建議值 DB_POOL_SIZE=20 # 常駐連線 DB_MAX_OVERFLOW=30 # 額外連線 (總共最多 50) DB_POOL_TIMEOUT=10 # 等待連線秒數 DB_POOL_RECYCLE=1800 # 30 分鐘回收 ``` ## Risks / Trade-offs | 風險 | 影響 | 緩解措施 | |------|------|----------| | 頻繁取得/釋放 Session 增加開銷 | 輕微效能下降 | 連線池的 `pool_pre_ping` 減少無效連線 | | FOR UPDATE 可能造成鎖等待 | 高並發時延遲 | 設定合理的鎖等待超時 | | 每次操作獨立事務 | 無法跨操作 rollback | 本系統每個操作獨立,無此需求 | ## Migration Plan 1. **Phase 1**: 部署新連線池配置(無風險) 2. **Phase 2**: 新增 context manager(無風險) 3. **Phase 3**: 修改 WebSocket router(需測試) 4. **Phase 4**: 修復 sequence 鎖定(需測試) **Rollback:** 每個 Phase 獨立,可單獨回滾。 ## Open Questions 1. 是否需要連線池監控指標(如 Prometheus metrics)? 2. 是否需要實作連線健康檢查端點?