Skip to content

fix(mqtt): stop link outages from climbing the reconnect ladder - #98

Merged
agessaman merged 3 commits into
observer-firmware-devfrom
fix/mqtt-backoff-link-outage
Nov 12, 2026
Merged

agessaman merged 3 commits into
observer-firmware-devfrom
fix/mqtt-backoff-link-outage

Conversation

@agessaman

@agessaman agessaman commented Nov 12, 2026 •

Copy link
Copy Markdown
Owner

Problem

Every Wi-Fi outage pushed every MQTT slot one level up the reconnect backoff ladder, even though each reconnect after link-up succeeded.

The level rises just before each due reconnect and is cleared only by a session that stays up for 120 s. The link-recovery retry (advanceBackoffForLinkRecovery) could not trip the breaker, but it still added a level. A node whose link dropped every couple of minutes therefore walked its slots to level 5 and toward the breaker with a healthy broker. On a Heltec V4, ten drops 150 s apart took one slot to level 5 with failures at max: 2.

Fix

At the link-down edge, before the bridge closes the live clients, MQTTConnectionPolicy::outageCausedByLink() marks a slot whose outage belongs to the link:

  • the slot was still connected, or its session ended within 20 s of the edge on either side (the socket usually fails several seconds before the 10 s status poll reports the drop);
  • and the link had been up for at least 30 s, measured to the session's end when the slot recorded one. A slot still marked connected at the edge has no such time, so it needs 40 s at the polled edge (the status poll can trail the real drop by 10 s).

The link-recovery retry of a marked slot keeps its level and failure count where they were. The reconnecting log line adds , link outage: held.

Unchanged on purpose:

  • A slot that was already down before the drop still advances.
  • A link that held for under 30 s is treated as flapping and still climbs.
  • Refusals, handshake failures and quick drops while the link stays up climb and trip the breaker as before.
  • A level a broker earned is kept, not forgiven.
  • A handshake that fails right after link-up counts as broker evidence and climbs one level.

Cost: one byte per slot and four bytes on the bridge, all written by the bridge task.

Testing

  • 766 native tests pass (16 new in test_mqtt_connection_policy).
  • Builds: heltec_v4_repeater_observer_mqtt, Heltec_v3_repeater_observer_mqtt.
  • Heltec V4, five slots, with a scratch Wi-Fi drop hook (not in this PR). The run was on fbfb35d23, before the last commit added the 10 s poll allowance:
Scenario Result
10 drops, link up 80-91 s between them 50 of 50 link-up retries at level 0; no failed reconnect, no breaker
3 re-drops 12-15 s after link-up every slot advanced to 1, 2, 3, not held
Slot 5 pointed at a closed port, then a drop slot 5 climbed 1, 2, 3 and went to 4 on the drop; slots 1-4 held at 0

Heap was flat across the 10 drops (126 K free, 115 K largest block after).

Not tested: Heltec V3, Station G3 and Ethernet boards on hardware; a session that ends more than 20 s before Wi-Fi reports the drop (it behaves as before); the 120 s clear of levels raised by flapping was not observed directly.

A Wi-Fi drop cut every slot's session, and the link-recovery retry advanced
each slot's backoff level. Sessions shorter than the 120 s stable clear never
reset it, so a node whose link flapped every couple of minutes walked every
slot up to the 300 s rung and toward the breaker while the broker was fine.

At the link-down edge, before live clients are closed, a slot that was still
connected or whose session ended within 20 s of the edge (either side, since
the socket usually fails before the polled edge) is marked as a link-caused
outage. Its recovery retry then holds the ladder where it was. Slots already
failing before the drop, and any drop when the link had been up under 30 s,
advance as before. Refusals, handshake failures and quick drops while the
link stays up are unchanged.
The 10 s status poll reports the link-down edge after the socket has
already died, so measuring the floor to the edge could excuse a link
that really held for less than kLinkFlapFloorMs. Use the outage stamp
when the slot has one inside the current link-up period.
A slot still marked connected at the polled link-down edge has no outage
stamp, so the floor measured the polled edge as uptime. Require the poll
slack (10 s) in that case; a stamped session end keeps the 30 s floor.
@agessaman
agessaman merged commit d68baea into observer-firmware-dev Nov 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant