feat: add time-based throttling to progress broadcasts

Add 300ms minimum interval between progress broadcasts to reduce
WebSocket message volume. Broadcasts are sent immediately for
significant changes (>=1% or forced), otherwise throttled.

- Add MIN_BROADCAST_INTERVAL class constant (0.3s)
- Track last broadcast time per progress_id using time.monotonic()
- Clean up broadcast timestamps when progress completes/fails/cancels
This commit is contained in:
2026-02-17 17:24:32 +01:00
parent 76f02ec822
commit 1c39dd5c6a

View File

@@ -8,6 +8,7 @@ to connected clients.
from __future__ import annotations
import asyncio
import time
from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
@@ -168,6 +169,9 @@ class ProgressService:
- Support for different progress types (download, scan, queue)
"""
# Minimum interval between broadcasts in seconds (300ms)
MIN_BROADCAST_INTERVAL: float = 0.3
def __init__(self):
"""Initialize the progress service."""
# Active progress operations: id -> ProgressUpdate
@@ -182,6 +186,9 @@ class ProgressService:
str, List[Callable[[ProgressEvent], None]]
] = {}
# Track last broadcast time per progress_id for time-based throttling
self._last_broadcast_time: Dict[str, float] = {}
# Lock for thread-safe operations
self._lock = asyncio.Lock()
@@ -389,11 +396,21 @@ class ProgressService:
update.status = ProgressStatus.IN_PROGRESS
update.updated_at = datetime.now(timezone.utc)
# Only broadcast if significant change or forced
# Time-based throttle: broadcast at most every 300ms,
# or immediately for significant changes / forced broadcasts
now = time.monotonic()
last_broadcast = self._last_broadcast_time.get(progress_id, 0.0)
time_since_last = now - last_broadcast
percent_change = abs(update.percent - old_percent)
should_broadcast = force_broadcast or percent_change >= 1.0
should_broadcast = (
force_broadcast
or percent_change >= 1.0
or time_since_last >= self.MIN_BROADCAST_INTERVAL
)
if should_broadcast:
self._last_broadcast_time[progress_id] = time.monotonic()
room = _get_room_for_progress_type(update.type)
event = ProgressEvent(
event_type=f"{update.type.value}_progress",
@@ -442,6 +459,7 @@ class ProgressService:
# Move to history
del self._active_progress[progress_id]
self._last_broadcast_time.pop(progress_id, None)
self._add_to_history(update)
logger.info(
@@ -497,6 +515,7 @@ class ProgressService:
# Move to history
del self._active_progress[progress_id]
self._last_broadcast_time.pop(progress_id, None)
self._add_to_history(update)
logger.error(
@@ -548,6 +567,7 @@ class ProgressService:
# Move to history
del self._active_progress[progress_id]
self._last_broadcast_time.pop(progress_id, None)
self._add_to_history(update)
logger.info(