perf: throttle CONFIG_SND during mass peer login bursts

Widen monitor CONFIG push debounce from 0.3s to 2s when five or more
peers connect within ten seconds, coalescing updates during fast ramps.
pull/1/head
Rodrigo Pérez 4 months ago
parent 2235203e22
commit 102f248ac9

@ -0,0 +1,38 @@
"""Adaptive debounce for CONFIG_SND pushes during mass peer login."""
from __future__ import annotations
import time
from collections import deque
# Normal peer connect/disconnect: near-realtime monitor update.
CONFIG_PUSH_DEBOUNCE_NORMAL_SEC = 0.3
# Many peers logging in at once: coalesce harder (monitor need not be instant).
CONFIG_PUSH_DEBOUNCE_BURST_SEC = 2.0
CONFIG_PUSH_BURST_WINDOW_SEC = 10.0
CONFIG_PUSH_BURST_MIN_CONNECTS = 5
class ConfigPushThrottle:
"""Widen CONFIG_SND debounce when peer connect rate is high."""
def __init__(self) -> None:
self._connect_times: deque[float] = deque()
def note_peer_connected(self, *, now: float | None = None) -> None:
"""Record a peer reaching CONNECTION=YES (MASTER login)."""
t = time.time() if now is None else now
self._connect_times.append(t)
self._prune(t)
def debounce_seconds(self, *, now: float | None = None) -> float:
t = time.time() if now is None else now
self._prune(t)
if len(self._connect_times) >= CONFIG_PUSH_BURST_MIN_CONNECTS:
return CONFIG_PUSH_DEBOUNCE_BURST_SEC
return CONFIG_PUSH_DEBOUNCE_NORMAL_SEC
def _prune(self, now: float) -> None:
cutoff = now - CONFIG_PUSH_BURST_WINDOW_SEC
while self._connect_times and self._connect_times[0] < cutoff:
self._connect_times.popleft()

@ -70,6 +70,7 @@ from ...domain.talker_alias import (
from ...domain.mesh_routing import MeshEgress, MeshIngress, PeerMeshConfig
from ..mesh.dmre_v5 import parse_dmre_trailer
from ..mesh.registry import MeshCodecRegistry
from ..config_push_throttle import ConfigPushThrottle
from ..mesh.obp_v1 import (
build_bcka,
build_bcve,
@ -228,6 +229,7 @@ class HBPProtocol(DatagramProtocol):
self._downlink_index = None
self._connected_peer_count = 0
self._config_push_delayed = None
self._config_push_throttle = ConfigPushThrottle()
self._refresh_connected_peer_count()
else:
self._peers = {}
@ -736,7 +738,8 @@ class HBPProtocol(DatagramProtocol):
"""Schedule debounced CONFIG_SND when MASTER peer list or OPTIONS change."""
if self._config_push_delayed is not None:
return
self._config_push_delayed = reactor.callLater(0.3, self._flush_config_to_monitor)
delay = self._config_push_throttle.debounce_seconds()
self._config_push_delayed = reactor.callLater(delay, self._flush_config_to_monitor)
def _flush_config_to_monitor(self) -> None:
self._config_push_delayed = None
@ -1171,6 +1174,7 @@ class HBPProtocol(DatagramProtocol):
logger.info("(%s) Peer %s (%s) has sent repeater configuration, Package ID: %s, Software ID: %s, Desc: %s", self._system, _this_peer["CALLSIGN"], _this_peer["RADIO_ID"], self._peers[_peer_id]["PACKAGE_ID"].decode("utf8", errors="replace").rstrip(), self._peers[_peer_id]["SOFTWARE_ID"].decode("utf8", errors="replace").rstrip(), self._peers[_peer_id]["DESCRIPTION"].decode("utf8", errors="replace").rstrip())
self._refresh_connected_peer_count()
self._mark_downlink_index_dirty()
self._config_push_throttle.note_peer_connected()
self._push_config_to_monitor()
else:
self.transport.write(b"".join([MSTNAK, _peer_id]), _sockaddr)

@ -0,0 +1,34 @@
"""CONFIG_SND adaptive debounce during peer login bursts."""
from __future__ import annotations
from adn_server.infrastructure.config_push_throttle import (
CONFIG_PUSH_BURST_MIN_CONNECTS,
CONFIG_PUSH_DEBOUNCE_BURST_SEC,
CONFIG_PUSH_DEBOUNCE_NORMAL_SEC,
ConfigPushThrottle,
)
def test_normal_debounce_with_few_connects() -> None:
throttle = ConfigPushThrottle()
base = 1_700_000_000.0
for i in range(CONFIG_PUSH_BURST_MIN_CONNECTS - 1):
throttle.note_peer_connected(now=base + i)
assert throttle.debounce_seconds(now=base + 10) == CONFIG_PUSH_DEBOUNCE_NORMAL_SEC
def test_burst_debounce_after_many_connects_in_window() -> None:
throttle = ConfigPushThrottle()
base = 1_700_000_000.0
for i in range(CONFIG_PUSH_BURST_MIN_CONNECTS):
throttle.note_peer_connected(now=base + i * 0.5)
assert throttle.debounce_seconds(now=base + 5) == CONFIG_PUSH_DEBOUNCE_BURST_SEC
def test_burst_window_expires() -> None:
throttle = ConfigPushThrottle()
base = 1_700_000_000.0
for i in range(CONFIG_PUSH_BURST_MIN_CONNECTS):
throttle.note_peer_connected(now=base + i)
assert throttle.debounce_seconds(now=base + 20) == CONFIG_PUSH_DEBOUNCE_NORMAL_SEC
Loading…
Cancel
Save

Powered by TurnKey Linux.