fix(km): keep backfill reconciler loop alive
This commit is contained in:
@@ -25,7 +25,9 @@ Feature Flag:
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
import structlog
|
||||
|
||||
from src.core.config import settings
|
||||
|
||||
@@ -16,19 +16,20 @@ P1-1 C1 修復 2026-04-28 ogt + Claude Sonnet 4.6
|
||||
"""
|
||||
|
||||
import json
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
import src.jobs.km_backfill_reconciler_job as reconciler_job
|
||||
from src.jobs.km_backfill_reconciler_job import (
|
||||
run_km_backfill_reconciler,
|
||||
run_km_backfill_reconciler_loop,
|
||||
)
|
||||
from src.services.km_writer import (
|
||||
KM_BACKFILL_DLQ_KEY,
|
||||
_backfill_path_a_approval_safe,
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Helper
|
||||
# =============================================================================
|
||||
@@ -136,6 +137,32 @@ async def test_reconciler_empty_dlq():
|
||||
assert result["failed"] == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reconciler_loop_can_sleep(monkeypatch: pytest.MonkeyPatch):
|
||||
"""loop 必須能 sleep;避免少 import asyncio 導致 background task 啟動即死亡。"""
|
||||
|
||||
class StopLoop(Exception):
|
||||
pass
|
||||
|
||||
calls = 0
|
||||
|
||||
async def fake_run_once():
|
||||
nonlocal calls
|
||||
calls += 1
|
||||
return {"processed": 0, "success": 0, "failed": 0}
|
||||
|
||||
async def fake_sleep(_seconds: int):
|
||||
raise StopLoop
|
||||
|
||||
monkeypatch.setattr(reconciler_job, "run_km_backfill_reconciler", fake_run_once)
|
||||
monkeypatch.setattr(reconciler_job.asyncio, "sleep", fake_sleep)
|
||||
|
||||
with pytest.raises(StopLoop):
|
||||
await run_km_backfill_reconciler_loop()
|
||||
|
||||
assert calls == 1
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 5. ENABLE_KM_BACKFILL_RECONCILER=false → 跳過
|
||||
# =============================================================================
|
||||
|
||||
@@ -3858,3 +3858,33 @@ ruff check apps/api/src/core/logging.py apps/api/src/services/failover_alerter.p
|
||||
### 注意
|
||||
|
||||
- `telegram_gateway.py` 全檔仍有大量既有 ruff 債,本次只針對 token 外洩與 MarkdownV2 400 風險做最小安全修補,避免在 6000+ 行 gateway 巨檔混入無關機械改動。
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-06(台北)— KM backfill reconciler background loop 修復
|
||||
|
||||
**觸發**:production API 啟動後出現 `Task exception was never retrieved`,`run_km_backfill_reconciler_loop()` 因 `NameError: name 'asyncio' is not defined` 在第一次 sleep 前死亡,導致 `km:backfill:dlq` 補救 loop 沒有持續運作。
|
||||
|
||||
### 已修正
|
||||
|
||||
| 範圍 | 結果 |
|
||||
|------|------|
|
||||
| `km_backfill_reconciler_job.py` | 補上 `import asyncio`,讓 5 分鐘循環 sleep 可正常執行 |
|
||||
| 回歸測試 | 新增 `test_reconciler_loop_can_sleep()`,用 fake sleep 主動中止 loop,驗證 loop 至少能跑一次 reconciler 並進入 sleep |
|
||||
|
||||
### 驗證
|
||||
|
||||
```text
|
||||
pytest apps/api/tests/test_km_writer_backfill_reconciler.py -q
|
||||
# 8 passed
|
||||
|
||||
py_compile apps/api/src/jobs/km_backfill_reconciler_job.py apps/api/tests/test_km_writer_backfill_reconciler.py
|
||||
# 通過
|
||||
|
||||
ruff check apps/api/src/jobs/km_backfill_reconciler_job.py apps/api/tests/test_km_writer_backfill_reconciler.py
|
||||
# All checks passed
|
||||
```
|
||||
|
||||
### 影響
|
||||
|
||||
- KM / PlayBook / RAG 飛輪的 backfill 補救鏈恢復可持續執行,避免 DLQ 堆積後造成知識庫關聯缺口。
|
||||
|
||||
Reference in New Issue
Block a user