From b9de3b7dd76a8fcadee7e5e7f060557503a98d38 Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Mon, 23 Feb 2026 20:00:42 -0800 Subject: [PATCH] Reduce default poll time and add DM ack clearing to standard poll --- app/event_handlers.py | 4 ++-- app/radio_sync.py | 9 +++++++-- tests/test_event_handlers.py | 6 +++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/event_handlers.py b/app/event_handlers.py index 8dd0167..86323b9 100644 --- a/app/event_handlers.py +++ b/app/event_handlers.py @@ -35,7 +35,7 @@ def track_pending_ack(expected_ack: str, message_id: int, timeout_ms: int) -> No ) -def _cleanup_expired_acks() -> None: +def cleanup_expired_acks() -> None: """Remove expired pending ACKs.""" now = time.time() expired = [] @@ -268,7 +268,7 @@ async def on_ack(event: "Event") -> None: logger.debug("Received ACK with code %s", ack_code) - _cleanup_expired_acks() + cleanup_expired_acks() if ack_code in _pending_acks: message_id, _, _ = _pending_acks.pop(ack_code) diff --git a/app/radio_sync.py b/app/radio_sync.py index 5bfea74..949e304 100644 --- a/app/radio_sync.py +++ b/app/radio_sync.py @@ -16,6 +16,7 @@ from contextlib import asynccontextmanager from meshcore import EventType +from app.event_handlers import cleanup_expired_acks from app.models import Contact from app.radio import RadioOperationBusyError, radio_manager from app.repository import ( @@ -31,8 +32,8 @@ logger = logging.getLogger(__name__) # Message poll task handle _message_poll_task: asyncio.Task | None = None -# Message poll interval in seconds -MESSAGE_POLL_INTERVAL = 5 +# Message poll interval in seconds (10s gives DM ACKs plenty of time to arrive) +MESSAGE_POLL_INTERVAL = 10 # Periodic advertisement task handle _advert_task: asyncio.Task | None = None @@ -323,6 +324,10 @@ async def _message_poll_loop(): try: await asyncio.sleep(MESSAGE_POLL_INTERVAL) + # Clean up expired pending ACKs every poll cycle so they don't + # accumulate when no ACKs arrive (e.g. all recipients out of range). + cleanup_expired_acks() + if radio_manager.is_connected and not is_polling_paused(): mc = radio_manager.meshcore if mc is not None: diff --git a/tests/test_event_handlers.py b/tests/test_event_handlers.py index a80e796..ca5e263 100644 --- a/tests/test_event_handlers.py +++ b/tests/test_event_handlers.py @@ -12,8 +12,8 @@ import pytest from app.database import Database from app.event_handlers import ( _active_subscriptions, - _cleanup_expired_acks, _pending_acks, + cleanup_expired_acks, register_event_handlers, track_pending_ack, ) @@ -81,7 +81,7 @@ class TestAckTracking: _pending_acks["expired"] = (1, time.time() - 100, 1000) # Created 100s ago, 1s timeout _pending_acks["valid"] = (2, time.time(), 60000) # Created now, 60s timeout - _cleanup_expired_acks() + cleanup_expired_acks() assert "expired" not in _pending_acks assert "valid" in _pending_acks @@ -92,7 +92,7 @@ class TestAckTracking: # 2x buffer = 20 seconds, so should NOT be expired yet _pending_acks["recent"] = (1, time.time() - 5, 10000) - _cleanup_expired_acks() + cleanup_expired_acks() assert "recent" in _pending_acks