From eb0f6cdc8542e0afcce60f8aa0c42abd95886e15 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 31 Jan 2026 15:10:42 +0100 Subject: [PATCH] Integrate scheduler service into FastAPI lifespan - Start scheduler service during app startup - Gracefully stop scheduler during app shutdown - Track scheduler in initialized services - Scheduler starts after background loader - Scheduler shutdown with 5s timeout --- src/server/fastapi_app.py | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/server/fastapi_app.py b/src/server/fastapi_app.py index 0cd91f2..c3c5458 100644 --- a/src/server/fastapi_app.py +++ b/src/server/fastapi_app.py @@ -117,7 +117,8 @@ async def lifespan(_application: FastAPI): initialized = { 'database': False, 'services': False, - 'background_loader': False + 'background_loader': False, + 'scheduler': False } # Startup @@ -265,6 +266,18 @@ async def lifespan(_application: FastAPI): initialized['background_loader'] = True logger.info("Background loader service started") + # Initialize and start scheduler service + from src.server.services.scheduler_service import get_scheduler_service + + scheduler_service = get_scheduler_service() + try: + await scheduler_service.start() + initialized['scheduler'] = True + logger.info("Scheduler service started") + except Exception as e: + logger.warning("Failed to start scheduler service: %s", e) + # Continue - scheduler is optional + # Run media scan only on first run await perform_media_scan_if_needed(background_loader) else: @@ -322,7 +335,23 @@ async def lifespan(_application: FastAPI): elapsed = time.monotonic() - shutdown_start return max(0.0, SHUTDOWN_TIMEOUT - elapsed) - # 1. Stop background loader service (only if initialized) + # 1. Stop scheduler service (only if initialized) + if initialized['scheduler']: + try: + from src.server.services.scheduler_service import get_scheduler_service + scheduler_service = get_scheduler_service() + logger.info("Stopping scheduler service...") + await asyncio.wait_for( + scheduler_service.stop(), + timeout=min(5.0, remaining_time()) + ) + logger.info("Scheduler service stopped") + except asyncio.TimeoutError: + logger.warning("Scheduler service shutdown timed out") + except Exception as e: # pylint: disable=broad-exception-caught + logger.error("Error stopping scheduler service: %s", e, exc_info=True) + + # 2. Stop background loader service (only if initialized) if initialized['background_loader']: try: from src.server.utils.dependencies import _background_loader_service @@ -338,7 +367,7 @@ async def lifespan(_application: FastAPI): except Exception as e: # pylint: disable=broad-exception-caught logger.error("Error stopping background loader service: %s", e, exc_info=True) - # 2. Broadcast shutdown notification via WebSocket + # 3. Broadcast shutdown notification via WebSocket try: ws_service = get_websocket_service() logger.info("Broadcasting shutdown notification to WebSocket clients...") @@ -352,7 +381,7 @@ async def lifespan(_application: FastAPI): except Exception as e: # pylint: disable=broad-exception-caught logger.error("Error during WebSocket shutdown: %s", e, exc_info=True) - # 3. Shutdown download service and persist active downloads + # 4. Shutdown download service and persist active downloads try: from src.server.services.download_service import ( # noqa: E501 _download_service_instance,