From 53bae5684e79ad425e4e05bcf7d4fd18b16437ac Mon Sep 17 00:00:00 2001 From: agessaman Date: Tue, 4 Aug 2026 14:04:08 -0700 Subject: [PATCH] fix(mqtt): measure the setup-retry interval from the failure last_reconnect_attempt starts at zero and teardownSlot() re-zeroes it, so the retry gate in maintainSlotConnections() reduced to "uptime >= SLOT_SETUP_RETRY_INTERVAL". Past 60 s of uptime a failed setup was therefore retried on the next maintenance pass rather than 60 s later -- in the same task iteration for a live reconfigure, since reconfigure processing runs before maintenance in the loop. setupSlot() now stamps last_reconnect_attempt on each failure that represents a real attempt (client allocation, and both JWT token paths), so all three callers get the interval measured from the failure. The bounds check and the !enabled early return are not attempts and stay unstamped. The reconnect ladder reads this field only for slots with initial_connect_done set, which a failed setup never sets, so reconnect timing is unaffected. The retry path's own pre-call stamp is kept as a backstop for any future false-returning path that does not stamp itself. --- src/helpers/bridges/MQTTBridge.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/helpers/bridges/MQTTBridge.cpp b/src/helpers/bridges/MQTTBridge.cpp index 90ffbf71..89373341 100644 --- a/src/helpers/bridges/MQTTBridge.cpp +++ b/src/helpers/bridges/MQTTBridge.cpp @@ -1759,9 +1759,18 @@ bool MQTTBridge::setupSlot(int index) { return false; } + // Every failure below is a real attempt, so stamp it: the retry interval in + // maintainSlotConnections() measures from last_reconnect_attempt, which starts at 0 + // and is re-zeroed by teardownSlot(). Left unstamped, the gate degenerates to + // "uptime >= SLOT_SETUP_RETRY_INTERVAL" and a failure past that point is retried on + // the very next maintenance pass — the same task iteration, for a live reconfigure. + // The reconnect ladder never reads this field for an unactivated slot (it is gated + // on initial_connect_done), so stamping here cannot perturb reconnect timing. + // First setup for this slot allocates its persistent client; later ones reuse it. if (!ensureSlotClient(index)) { MQTT_DEBUG_PRINTLN("MQTT%d: client allocation failed - will retry", index + 1); + slot.last_reconnect_attempt = millis(); return false; } @@ -1825,6 +1834,7 @@ bool MQTTBridge::setupSlot(int index) { if (slot.preset->auth_type == MQTT_AUTH_JWT) { if (!createSlotAuthToken(index) || !slot.auth_token || slot.auth_token[0] == '\0') { MQTT_DEBUG_PRINTLN("MQTT%d: no usable JWT token - will retry", index + 1); + slot.last_reconnect_attempt = millis(); return false; } slot.client->setCredentials(_jwt_username, slot.auth_token); @@ -1939,6 +1949,7 @@ bool MQTTBridge::setupSlot(int index) { // JWT auth for custom slot — same rule as the preset JWT path above. if (!createSlotAuthToken(index) || !slot.auth_token || slot.auth_token[0] == '\0') { MQTT_DEBUG_PRINTLN("MQTT%d: no usable JWT token - will retry", index + 1); + slot.last_reconnect_attempt = millis(); return false; } slot.client->setCredentials(_jwt_username, slot.auth_token);