188 lines
5.3 KiB
Python
188 lines
5.3 KiB
Python
"""
|
|
POI Refresh Scheduler
|
|
|
|
Background scheduler for periodic POI context refresh.
|
|
Runs every hour to check for and execute pending POI refresh jobs.
|
|
"""
|
|
|
|
import asyncio
|
|
from typing import Optional
|
|
from datetime import datetime, timezone
|
|
import structlog
|
|
|
|
from app.services.poi_refresh_service import POIRefreshService
|
|
|
|
logger = structlog.get_logger()
|
|
|
|
|
|
class POIRefreshScheduler:
|
|
"""
|
|
POI Refresh Scheduler
|
|
|
|
Background task that periodically checks for and executes
|
|
pending POI refresh jobs.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
poi_refresh_service: Optional[POIRefreshService] = None,
|
|
check_interval_seconds: int = 3600, # 1 hour
|
|
max_concurrent_jobs: int = 5
|
|
):
|
|
"""
|
|
Initialize POI refresh scheduler.
|
|
|
|
Args:
|
|
poi_refresh_service: POI refresh service instance
|
|
check_interval_seconds: Seconds between checks (default: 3600 = 1 hour)
|
|
max_concurrent_jobs: Max concurrent job executions (default: 5)
|
|
"""
|
|
self.poi_refresh_service = poi_refresh_service or POIRefreshService()
|
|
self.check_interval_seconds = check_interval_seconds
|
|
self.max_concurrent_jobs = max_concurrent_jobs
|
|
|
|
self._task: Optional[asyncio.Task] = None
|
|
self._running = False
|
|
|
|
logger.info(
|
|
"POI Refresh Scheduler initialized",
|
|
check_interval_seconds=check_interval_seconds,
|
|
max_concurrent_jobs=max_concurrent_jobs
|
|
)
|
|
|
|
async def start(self):
|
|
"""Start the scheduler background task"""
|
|
if self._running:
|
|
logger.warning("POI Refresh Scheduler already running")
|
|
return
|
|
|
|
self._running = True
|
|
self._task = asyncio.create_task(self._run_scheduler())
|
|
|
|
logger.info("POI Refresh Scheduler started")
|
|
|
|
async def stop(self):
|
|
"""Stop the scheduler background task"""
|
|
if not self._running:
|
|
return
|
|
|
|
self._running = False
|
|
|
|
if self._task:
|
|
self._task.cancel()
|
|
try:
|
|
await self._task
|
|
except asyncio.CancelledError:
|
|
pass
|
|
|
|
logger.info("POI Refresh Scheduler stopped")
|
|
|
|
async def _run_scheduler(self):
|
|
"""Main scheduler loop"""
|
|
logger.info("POI Refresh Scheduler loop started")
|
|
|
|
while self._running:
|
|
try:
|
|
await self._process_cycle()
|
|
except Exception as e:
|
|
logger.error(
|
|
"POI refresh scheduler cycle failed",
|
|
error=str(e),
|
|
exc_info=True
|
|
)
|
|
|
|
# Wait for next cycle
|
|
try:
|
|
await asyncio.sleep(self.check_interval_seconds)
|
|
except asyncio.CancelledError:
|
|
break
|
|
|
|
logger.info("POI Refresh Scheduler loop ended")
|
|
|
|
async def _process_cycle(self):
|
|
"""Process one scheduler cycle"""
|
|
cycle_start = datetime.now(timezone.utc)
|
|
|
|
logger.debug(
|
|
"POI refresh scheduler cycle started",
|
|
timestamp=cycle_start.isoformat()
|
|
)
|
|
|
|
# Process pending jobs
|
|
result = await self.poi_refresh_service.process_pending_jobs(
|
|
max_concurrent=self.max_concurrent_jobs
|
|
)
|
|
|
|
cycle_end = datetime.now(timezone.utc)
|
|
cycle_duration = (cycle_end - cycle_start).total_seconds()
|
|
|
|
if result["total_jobs"] > 0:
|
|
logger.info(
|
|
"POI refresh scheduler cycle completed",
|
|
total_jobs=result["total_jobs"],
|
|
successful=result["successful"],
|
|
failed=result["failed"],
|
|
cycle_duration_seconds=cycle_duration
|
|
)
|
|
else:
|
|
logger.debug(
|
|
"POI refresh scheduler cycle completed (no jobs)",
|
|
cycle_duration_seconds=cycle_duration
|
|
)
|
|
|
|
async def trigger_immediate_check(self):
|
|
"""Trigger an immediate check for pending jobs (bypasses schedule)"""
|
|
logger.info("POI refresh scheduler immediate check triggered")
|
|
|
|
try:
|
|
result = await self.poi_refresh_service.process_pending_jobs(
|
|
max_concurrent=self.max_concurrent_jobs
|
|
)
|
|
|
|
logger.info(
|
|
"POI refresh scheduler immediate check completed",
|
|
total_jobs=result["total_jobs"],
|
|
successful=result["successful"],
|
|
failed=result["failed"]
|
|
)
|
|
|
|
return result
|
|
except Exception as e:
|
|
logger.error(
|
|
"POI refresh scheduler immediate check failed",
|
|
error=str(e),
|
|
exc_info=True
|
|
)
|
|
raise
|
|
|
|
@property
|
|
def is_running(self) -> bool:
|
|
"""Check if scheduler is running"""
|
|
return self._running
|
|
|
|
|
|
# Global scheduler instance
|
|
_scheduler_instance: Optional[POIRefreshScheduler] = None
|
|
|
|
|
|
def get_scheduler() -> POIRefreshScheduler:
|
|
"""Get global scheduler instance (singleton)"""
|
|
global _scheduler_instance
|
|
|
|
if _scheduler_instance is None:
|
|
_scheduler_instance = POIRefreshScheduler()
|
|
|
|
return _scheduler_instance
|
|
|
|
|
|
async def start_scheduler():
|
|
"""Start global POI refresh scheduler"""
|
|
scheduler = get_scheduler()
|
|
await scheduler.start()
|
|
|
|
|
|
async def stop_scheduler():
|
|
"""Stop global POI refresh scheduler"""
|
|
scheduler = get_scheduler()
|
|
await scheduler.stop()
|