"""Phase 4: Global Digest 워커. 7일 뉴스를 country × topic 으로 묶어 cluster-level LLM 요약을 생성하고 global_digests / digest_topics 테이블에 저장한다. - APScheduler cron (매일 04:00 KST) + 수동 호출 공용 진입점 - PIPELINE_HARD_CAP = 600초 hard cap 으로 cron stuck 절대 방지 - 단독 실행: `python -m workers.digest_worker` """ import asyncio from core.utils import setup_logger from services.digest.pipeline import run_digest_pipeline logger = setup_logger("digest_worker") PIPELINE_HARD_CAP = 600 # 10분 hard cap async def run() -> None: """APScheduler + 수동 호출 공용 진입점. pipeline 자체는 timeout 으로 감싸지 않음 (per-call timeout 은 summarizer 가 처리). 여기서는 전체 hard cap 만 강제. """ try: result = await asyncio.wait_for( run_digest_pipeline(), timeout=PIPELINE_HARD_CAP, ) logger.info(f"[global_digest] 워커 완료: {result}") except asyncio.TimeoutError: logger.error( f"[global_digest] HARD CAP {PIPELINE_HARD_CAP}s 초과 — 워커 강제 중단. " f"기존 digest 는 commit 시점에만 갱신되므로 그대로 유지됨. " f"다음 cron 실행에서 재시도." ) except Exception as e: logger.exception(f"[global_digest] 워커 실패: {e}") if __name__ == "__main__": asyncio.run(run())