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:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user