56 lines
1.6 KiB
Python
56 lines
1.6 KiB
Python
"""Synchronous WebSocket broadcast helper for Celery workers.
|
|
|
|
Celery tasks run outside the FastAPI event loop. This module provides a
|
|
sync Redis publish so tasks can notify connected clients without asyncio.
|
|
"""
|
|
import traceback
|
|
from datetime import datetime
|
|
|
|
import redis as sync_redis
|
|
|
|
from ..core.config import settings
|
|
from ..core.logging import get_logger
|
|
from ..services.websocket import JobStatusUpdate
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
def broadcast_status_update(
|
|
job_id: str,
|
|
status: str,
|
|
job_title: str | None = None,
|
|
message: str | None = None,
|
|
progress: int | None = None,
|
|
) -> None:
|
|
"""Publish a job-status event to Redis so WebSocket subscribers receive it.
|
|
|
|
Swallows all exceptions — a broadcast failure must never abort a task.
|
|
"""
|
|
try:
|
|
update = JobStatusUpdate(
|
|
job_id=job_id,
|
|
status=status,
|
|
updated_at=datetime.utcnow(),
|
|
job_title=job_title,
|
|
message=message,
|
|
progress=progress,
|
|
)
|
|
payload = update.model_dump_json()
|
|
|
|
redis_client = sync_redis.Redis.from_url(
|
|
settings.redis_url,
|
|
encoding="utf-8",
|
|
decode_responses=True,
|
|
)
|
|
try:
|
|
redis_client.publish("job_status_updates", payload)
|
|
redis_client.publish(f"job_status_updates:{job_id}", payload)
|
|
finally:
|
|
redis_client.close()
|
|
|
|
logger.info("Broadcast status update job=%s status=%s", job_id, status)
|
|
|
|
except Exception:
|
|
logger.error(
|
|
"Failed to broadcast status update job=%s\n%s", job_id, traceback.format_exc()
|
|
)
|