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
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import time
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
@@ -168,6 +169,9 @@ class ProgressService:
|
|||||||
- Support for different progress types (download, scan, queue)
|
- Support for different progress types (download, scan, queue)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Minimum interval between broadcasts in seconds (300ms)
|
||||||
|
MIN_BROADCAST_INTERVAL: float = 0.3
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize the progress service."""
|
"""Initialize the progress service."""
|
||||||
# Active progress operations: id -> ProgressUpdate
|
# Active progress operations: id -> ProgressUpdate
|
||||||
@@ -182,6 +186,9 @@ class ProgressService:
|
|||||||
str, List[Callable[[ProgressEvent], None]]
|
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
|
# Lock for thread-safe operations
|
||||||
self._lock = asyncio.Lock()
|
self._lock = asyncio.Lock()
|
||||||
|
|
||||||
@@ -389,11 +396,21 @@ class ProgressService:
|
|||||||
update.status = ProgressStatus.IN_PROGRESS
|
update.status = ProgressStatus.IN_PROGRESS
|
||||||
update.updated_at = datetime.now(timezone.utc)
|
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)
|
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:
|
if should_broadcast:
|
||||||
|
self._last_broadcast_time[progress_id] = time.monotonic()
|
||||||
room = _get_room_for_progress_type(update.type)
|
room = _get_room_for_progress_type(update.type)
|
||||||
event = ProgressEvent(
|
event = ProgressEvent(
|
||||||
event_type=f"{update.type.value}_progress",
|
event_type=f"{update.type.value}_progress",
|
||||||
@@ -442,6 +459,7 @@ class ProgressService:
|
|||||||
|
|
||||||
# Move to history
|
# Move to history
|
||||||
del self._active_progress[progress_id]
|
del self._active_progress[progress_id]
|
||||||
|
self._last_broadcast_time.pop(progress_id, None)
|
||||||
self._add_to_history(update)
|
self._add_to_history(update)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -497,6 +515,7 @@ class ProgressService:
|
|||||||
|
|
||||||
# Move to history
|
# Move to history
|
||||||
del self._active_progress[progress_id]
|
del self._active_progress[progress_id]
|
||||||
|
self._last_broadcast_time.pop(progress_id, None)
|
||||||
self._add_to_history(update)
|
self._add_to_history(update)
|
||||||
|
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -548,6 +567,7 @@ class ProgressService:
|
|||||||
|
|
||||||
# Move to history
|
# Move to history
|
||||||
del self._active_progress[progress_id]
|
del self._active_progress[progress_id]
|
||||||
|
self._last_broadcast_time.pop(progress_id, None)
|
||||||
self._add_to_history(update)
|
self._add_to_history(update)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|||||||
Reference in New Issue
Block a user