fix(mesh): don't reset reconnect backoff for sessions that die young
Extracted from beff5dd5 on archy-hwconfig (the rest of that commit is flash-feature code staying on its branch): a device that connects then drops within seconds — e.g. mid-boot-loop — kept resetting backoff to 5s forever, and every retry's open() toggles DTR/RTS which itself resets ESP32-family boards. Backoff now only resets after a session survives STABLE_SESSION_THRESHOLD (20s), so an unstable device gets progressively longer quiet gaps to actually finish booting. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
729cee573a
commit
449ed7c1f7
@ -87,6 +87,18 @@ const RECONNECT_DELAY_INIT: Duration = Duration::from_secs(5);
|
|||||||
/// Maximum reconnect delay (cap for exponential backoff).
|
/// Maximum reconnect delay (cap for exponential backoff).
|
||||||
const RECONNECT_DELAY_MAX: Duration = Duration::from_secs(60);
|
const RECONNECT_DELAY_MAX: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
|
/// Minimum time a session must run before we trust it enough to reset
|
||||||
|
/// backoff to the minimum. Without this gate, a device that connects then
|
||||||
|
/// fails again within a couple of seconds (e.g. mid-boot-loop) never backs
|
||||||
|
/// off — every retry immediately re-opens the port, which toggles DTR/RTS
|
||||||
|
/// (resets many ESP32 boards' MCU on native-USB and CP2102/CH340
|
||||||
|
/// auto-reset-circuit boards alike), turning a device that's merely
|
||||||
|
/// unstable into a self-sustaining boot loop that outlasts whatever
|
||||||
|
/// triggered the original instability. Confirmed live 2026-07-23: a Heltec
|
||||||
|
/// V3 stuck retrying every ~5-15s for 5+ minutes after a failed firmware
|
||||||
|
/// flash left it in a marginal state.
|
||||||
|
const STABLE_SESSION_THRESHOLD: Duration = Duration::from_secs(20);
|
||||||
|
|
||||||
/// Number of consecutive write failures before we consider the device dead
|
/// Number of consecutive write failures before we consider the device dead
|
||||||
/// and trigger a reconnection cycle.
|
/// and trigger a reconnection cycle.
|
||||||
const MAX_CONSECUTIVE_WRITE_FAILURES: u32 = 3;
|
const MAX_CONSECUTIVE_WRITE_FAILURES: u32 = 3;
|
||||||
@ -560,6 +572,7 @@ pub fn spawn_mesh_listener(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let session_start = std::time::Instant::now();
|
||||||
match session::run_mesh_session(
|
match session::run_mesh_session(
|
||||||
&state,
|
&state,
|
||||||
&data_dir,
|
&data_dir,
|
||||||
@ -582,13 +595,14 @@ pub fn spawn_mesh_listener(
|
|||||||
{
|
{
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
info!("Mesh session ended cleanly");
|
info!("Mesh session ended cleanly");
|
||||||
// Session was established before ending — reset backoff
|
// Only trust a session that actually ran for a while —
|
||||||
reconnect_delay = RECONNECT_DELAY_INIT;
|
// see STABLE_SESSION_THRESHOLD's doc comment.
|
||||||
|
if session_start.elapsed() >= STABLE_SESSION_THRESHOLD {
|
||||||
|
reconnect_delay = RECONNECT_DELAY_INIT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Check if session was ever connected (vs failed to open)
|
if session_start.elapsed() >= STABLE_SESSION_THRESHOLD {
|
||||||
let was_connected = state.status.read().await.device_connected;
|
|
||||||
if was_connected {
|
|
||||||
reconnect_delay = RECONNECT_DELAY_INIT;
|
reconnect_delay = RECONNECT_DELAY_INIT;
|
||||||
}
|
}
|
||||||
error!("Mesh session error: {} (retry in {:?})", e, reconnect_delay);
|
error!("Mesh session error: {} (retry in {:?})", e, reconnect_delay);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user