Switch task progress to throttled event-driven updates

Replace timer-based _monitor_progress (1s sleep loop) with a
ProgressReporter class that publishes on actual state changes,
throttled to at most 1 publish per 250ms. A background flush
every 2s keeps ETA/elapsed current during quiet periods.

Switch WebSocket forwarder from get_message() polling (1s timeout)
to async pubsub.listen() for instant Redis-to-WebSocket delivery.

Combined latency improvement: ~1.5s average → ~250ms.
This commit is contained in:
Viktor Barzin 2026-02-10 21:24:33 +00:00
parent b816f695f0
commit 902f1b0852
No known key found for this signature in database
GPG key ID: 0EB088298288D958
2 changed files with 196 additions and 81 deletions

View file

@ -109,20 +109,18 @@ async def ws_task_progress(websocket: WebSocket) -> None:
async def _forward_pubsub() -> None:
"""Read from Redis pub/sub and forward to the WebSocket."""
while True:
message = await pubsub.get_message(
ignore_subscribe_messages=True, timeout=1.0
)
if message and message["type"] == "message":
try:
data = json.loads(message["data"])
except (json.JSONDecodeError, ValueError):
logger.debug("Malformed pubsub message, skipping")
continue
try:
await websocket.send_json({"type": "task_update", **data})
except Exception:
break
async for message in pubsub.listen():
if message["type"] != "message":
continue
try:
data = json.loads(message["data"])
except (json.JSONDecodeError, ValueError):
logger.debug("Malformed pubsub message, skipping")
continue
try:
await websocket.send_json({"type": "task_update", **data})
except Exception:
break
async def _handle_client_messages() -> None:
"""Read messages from the client (subscribe, ping)."""