Files
Task_Reporter/openspec/changes/archive/2025-12-07-optimize-websocket-db-sessions/design.md
egg 599802b818 feat: Add Chat UX improvements with notifications and @mention support
- Add ActionBar component with expandable toolbar for mobile
- Add @mention functionality with autocomplete dropdown
- Add browser notification system (push, sound, vibration)
- Add NotificationSettings modal for user preferences
- Add mention badges on room list cards
- Add ReportPreview with Markdown rendering and copy/download
- Add message copy functionality with hover actions
- Add backend mentions field to messages with Alembic migration
- Add lots field to rooms, remove templates
- Optimize WebSocket database session handling
- Various UX polish (animations, accessibility)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 08:20:37 +08:00

131 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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. 是否需要實作連線健康檢查端點?