fix(mqtt): let a retried setup consume the reconnect allowance

reconnect_attempted_this_cycle is computed once before the maintenance loop and
only maintainSlotConnection() was setting it. The deferred-setup retry armed the
15 s cross-slot guard via _last_slot_reconnect_ms but left the local flag false,
so a disconnected slot later in the same pass still saw "no reconnect yet" and
started a second TLS handshake. Two concurrent ~40 KB sessions is exactly the
contention the guard prevents, and it landed in the one situation where internal
heap is already known to be short -- a failed allocation is why the retry runs.

Set on success only: setupSlot() returns true only after client->connect(), so
success means a handshake was launched. A failed retry launches nothing and
continues to spend only setup_retry_this_cycle, which rate-limits the allocation
attempts without consuming the handshake allowance.
This commit is contained in:
agessaman committed 2026-08-04 14:03:21 -07:00
1 parent e242cc6fd3
commit 6be84eb4eb
1 file changed
+11 -1
+11 -1
View File
@@ -2047,7 +2047,17 @@ void MQTTBridge::maintainSlotConnections() {
setup_retry_this_cycle = true;
MQTT_DEBUG_PRINTLN("MQTT%d retrying deferred setup (int_heap=%d)", i + 1,
(int)heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
if (setupSlot(i)) _last_slot_reconnect_ms = now_millis;
if (setupSlot(i)) {
// A successful setup ends in connect(), so it spends this cycle's single
// handshake allowance as well as arming the 15 s cross-slot guard. Without
// the local flag, a disconnected slot later in this same pass would start a
// second concurrent TLS handshake — the contention the guard exists to
// prevent, and most damaging here because a failed allocation is why we are
// retrying at all. A failed setup launches nothing and so spends only
// setup_retry_this_cycle.
_last_slot_reconnect_ms = now_millis;
reconnect_attempted_this_cycle = true;
}
}
continue;
}