# backend/app/worker/runner.py — 스케줄러 전용 컨테이너 진입점 (phase-15) # WORKER_ENABLED=true 일 때만 주기 잡 실행. APScheduler 있으면 사용, 없으면 단순 루프 폴백. import time from ..config import get_settings from ..observability.logging import configure_logging, get_logger from .main import register def _fallback_loop() -> None: """APScheduler 미설치 시: sync_interval 마다 능동/동기화 잡 실행.""" from sqlmodel import Session from ..db import engine from .jobs.proactive import run_proactive from .sync_jobs import run_periodic_sync log = get_logger("ari.worker") interval = max(60, get_settings().sync_interval_minutes * 60) while True: try: with Session(engine) as s: run_proactive(s) run_periodic_sync(s) log.info("worker.tick", extra={"event": "worker.tick"}) except Exception: # 잡 실패가 워커를 죽이지 않음 log.exception("worker.tick failed") time.sleep(interval) def main() -> None: configure_logging(get_settings().log_level) log = get_logger("ari.worker") if not get_settings().worker_enabled: log.info("WORKER_ENABLED=false — 워커 미가동(수동 트리거 /api/worker/run/{job} 사용)") return try: from apscheduler.schedulers.blocking import BlockingScheduler # type: ignore sched = BlockingScheduler(timezone="UTC") register(sched) log.info("worker scheduler started (APScheduler)") sched.start() except ImportError: log.info("APScheduler 미설치 — 단순 루프 폴백") _fallback_loop() if __name__ == "__main__": main()