Catch channel cache issues on set_channel failure during eviction

This commit is contained in:
Jack Kingsman
2026-03-13 11:09:23 -07:00
parent 9c2b6f0744
commit 2369e69e0a
2 changed files with 75 additions and 5 deletions

View File

@@ -82,12 +82,19 @@ async def send_channel_message_with_effective_scope(
else ""
),
)
set_result = await mc.commands.set_channel(
channel_idx=channel_slot,
channel_name=channel.name,
channel_secret=key_bytes,
)
try:
set_result = await mc.commands.set_channel(
channel_idx=channel_slot,
channel_name=channel.name,
channel_secret=key_bytes,
)
except Exception:
if evicted_channel_key is not None:
radio_manager.invalidate_cached_channel_slot(evicted_channel_key)
raise
if set_result.type == EventType.ERROR:
if evicted_channel_key is not None:
radio_manager.invalidate_cached_channel_slot(evicted_channel_key)
logger.warning(
"Failed to set channel on radio slot %d before %s: %s",
channel_slot,

View File

@@ -950,6 +950,69 @@ class TestRadioExceptionMidSend:
)
assert len(messages) == 0
@pytest.mark.asyncio
async def test_channel_send_set_channel_exception_invalidates_evicted_cached_slot(
self, test_db
):
"""Eviction-path configure exceptions drop the stale cached slot owner."""
from app.repository import ChannelRepository
mc = _make_mc(name="TestNode")
chan_key_a = "de" * 16
chan_key_b = "ef" * 16
await ChannelRepository.upsert(key=chan_key_a, name="#alpha")
await ChannelRepository.upsert(key=chan_key_b, name="#bravo")
radio_manager.max_channels = 1
radio_manager._connection_info = "Serial: /dev/ttyUSB0"
radio_manager.note_channel_slot_loaded(chan_key_a, 0)
mc.commands.set_channel = AsyncMock(side_effect=TimeoutError("Radio not responding"))
with (
patch("app.routers.messages.require_connected", return_value=mc),
patch.object(radio_manager, "_meshcore", mc),
):
with pytest.raises(TimeoutError):
await send_channel_message(
SendChannelMessageRequest(channel_key=chan_key_b, text="Never sent")
)
assert radio_manager.get_cached_channel_slot(chan_key_a) is None
assert radio_manager.get_cached_channel_slot(chan_key_b) is None
mc.commands.send_chan_msg.assert_not_called()
@pytest.mark.asyncio
async def test_channel_send_set_channel_error_invalidates_evicted_cached_slot(self, test_db):
"""Eviction-path configure error results also drop the stale cached slot owner."""
mc = _make_mc(name="TestNode")
chan_key_a = "fa" * 16
chan_key_b = "fb" * 16
await ChannelRepository.upsert(key=chan_key_a, name="#alpha")
await ChannelRepository.upsert(key=chan_key_b, name="#bravo")
radio_manager.max_channels = 1
radio_manager._connection_info = "Serial: /dev/ttyUSB0"
radio_manager.note_channel_slot_loaded(chan_key_a, 0)
mc.commands.set_channel = AsyncMock(
return_value=MagicMock(type=EventType.ERROR, payload="radio busy")
)
with (
patch("app.routers.messages.require_connected", return_value=mc),
patch.object(radio_manager, "_meshcore", mc),
pytest.raises(HTTPException) as exc_info,
):
await send_channel_message(
SendChannelMessageRequest(channel_key=chan_key_b, text="Never sent")
)
assert exc_info.value.status_code == 500
assert radio_manager.get_cached_channel_slot(chan_key_a) is None
assert radio_manager.get_cached_channel_slot(chan_key_b) is None
mc.commands.send_chan_msg.assert_not_called()
class TestConcurrentChannelSends:
"""Test that concurrent channel sends are serialized by the radio operation lock.