fix(services): back off a service that does not start instead of retiring it

Review found that marking a not-running service inactive for good also retired services that only returned early for a transient reason: packet capture and the map uploader give up after waiting 30 s for the radio, and the bridges return when meshcore is momentarily unset, which is exactly when the health loop restarts them. Such a service now records a restart failure, so the next attempt waits out service_restart_backoff_seconds; a transient failure recovers, a missing configuration costs one attempt per backoff instead of one every 5 s. The restart decision is _service_restart_due, which the tests now exercise directly.

Also from the review: packet capture and the map uploader never cleared the should_exit flag stop() sets, so after a restart their background loops exited at once while the service reported healthy; start() now clears it. The Discord and Telegram bridges close the HTTP session they open when they return early because meshcore is not available.
This commit is contained in:
agessaman committed 2026-10-01 11:47:47 -07:00
1 parent a3b0f56639
commit 3ff24a93f6
7 files changed
+123 -34

No files matched your search

@@ -338,6 +338,9 @@ class DiscordBridgeService(BaseServicePlugin):
self.logger.info("Subscribed to CHANNEL_MSG_RECV events")
else:
self.logger.error("Cannot subscribe to events - meshcore not available")
if self.http_session is not None:
await self.http_session.close()
self.http_session = None
return
# Register for bot-sent channel messages so bot responses are bridged too
@@ -221,6 +221,9 @@ class MapUploaderService(BaseServicePlugin):
self.logger.info("Map uploader service is disabled")
return
# stop() sets this; clear it so a restart's background work actually runs.
self.should_exit = False
# Check dependencies
if not AIOHTTP_AVAILABLE:
self.logger.error("aiohttp is required for map uploader service. Install with: pip install aiohttp")
@@ -944,6 +944,9 @@ class PacketCaptureService(BaseServicePlugin):
self.logger.info("Packet capture service is disabled")
return
# stop() sets this; clear it so a restart's background loops actually run.
self.should_exit = False
# Wait for bot to be connected (with timeout)
max_wait = 30 # seconds
wait_time: float = 0
@@ -239,6 +239,9 @@ class TelegramBridgeService(BaseServicePlugin):
self.logger.info("Subscribed to CHANNEL_MSG_RECV events")
else:
self.logger.error("Cannot subscribe to events - meshcore not available")
if self.http_session is not None:
await self.http_session.close()
self.http_session = None
return
# Register for bot-sent channel messages so bot responses are bridged too