fix: close Audio Router self-echo PUBACK/subscription-delivery race
publish_router_mode registered its self-echo correlation marker before publishing but removed it in a finally block the instant the QoS 1 publish() call observed its PUBACK. A broker may legally deliver a Controller's own subscription copy of that same publish after the PUBACK, since MQTT does not order the two; when that happened, the Controller mistook its own late echo for live operator intent and relinquished automatic restoration ownership. Keep the marker claimable for a short, bounded mqtt.router_echo_grace_seconds window (default 2s) after the PUBACK instead of clearing it immediately. Genuine external/operator commands are unaffected: once the grace window elapses, a matching payload is still correctly treated as operator intent and ownership is relinquished as before. Verified with deterministic fake-clock unit tests plus a real Mosquitto broker integration test (tests/test_router_echo_mosquitto_integration.py, skipped automatically when Docker is unavailable).
This commit is contained in:
@@ -345,8 +345,12 @@ when all of these remain true:
|
||||
- No live, non-self mode command and no conflicting state change indicated an
|
||||
operator override.
|
||||
|
||||
Controller subscribes to both Router mode set and state topics. Its exact
|
||||
in-flight echo is recognized; every other live mode-set publication is treated
|
||||
Controller subscribes to both Router mode set and state topics. Its own
|
||||
mode-set publication is correlated by exact payload and stays recognized as
|
||||
a self-echo both while the publish is in flight and, because a broker may
|
||||
legally deliver a subscription echo after the PUBACK that acknowledged the
|
||||
same publish, for a short `mqtt.router_echo_grace_seconds` window afterward
|
||||
(default 2 seconds). Every other live mode-set publication is treated
|
||||
conservatively as operator intent, even if it repeats `video`. A retained set
|
||||
message is ignored as stale intent. Controller does not publish Router state;
|
||||
it accepts a received live or retained state message as the Router's current
|
||||
|
||||
Reference in New Issue
Block a user