"""Redis service: connection pool and job progress utilities.""" import json import os import uuid from typing import Optional from arq import create_pool from arq.connections import ArqRedis, RedisSettings _pool: Optional[ArqRedis] = None def _get_redis_settings() -> RedisSettings: """Parse REDIS_URL env var into ARQ RedisSettings.""" url = os.environ.get("REDIS_URL", "redis://localhost:6379/0") return RedisSettings.from_dsn(url) async def get_arq_pool() -> ArqRedis: """Get or create the shared ARQ Redis connection pool.""" global _pool if _pool is None: _pool = await create_pool(_get_redis_settings()) return _pool async def close_arq_pool() -> None: """Close the shared ARQ Redis pool (call on app shutdown).""" global _pool if _pool is not None: await _pool.aclose() _pool = None async def enqueue_job(function_name: str, **kwargs) -> Optional[str]: """Enqueue a job via ARQ. Returns the ARQ job ID.""" pool = await get_arq_pool() job = await pool.enqueue_job(function_name, **kwargs) return job.job_id if job else None async def publish_job_progress( job_id: uuid.UUID, progress: int, message: str, status: str = "processing", ) -> None: """Publish a progress event to Redis pub/sub for SSE consumers.""" pool = await get_arq_pool() channel = f"job:{job_id}:progress" payload = json.dumps({ "job_id": str(job_id), "progress": progress, "message": message, "status": status, }) await pool.publish(channel, payload.encode())