Auto-reconnect logic for the bench's BEAM dist connection.
When a probe says the dist connection has dropped (:alive_epmd_only or
:alive_dist_only after an RPC timeout), we want to attempt to reconnect
automatically rather than leaving the bench in a stuck state for the rest
of the run.
This module is pure logic — no GenServer, no timers. The bench polling
loop calls tick/2 once per cycle, which decides whether to attempt a
reconnect based on the current reachability and elapsed time since the
last attempt. This keeps the reconnect logic testable and lets the
caller control the cadence.
Backoff schedule (defaults)
1st attempt: immediate
2nd attempt: 2 s after 1st
3rd attempt: 4 s after 2nd
4th attempt: 8 s after 3rd
Subsequent: 30 s capReset to immediate on a successful reconnect.
Summary
Functions
Returns the backoff (in ms) that applies to the next attempt.
Initialise a reconnector for node with cookie. Optional :max_backoff_ms.
Record that the most recent reconnect attempt succeeded — resets the backoff counter and bumps the total_reconnects counter.
Decide whether the caller should attempt a reconnect right now, given the
current probe state and current time. Returns {action, updated_reconnector}.
Types
@type t() :: %MobDev.Bench.Reconnector{ attempts: non_neg_integer(), cookie: atom(), last_attempt_ms: integer() | nil, max_backoff_ms: pos_integer(), node: atom(), total_reconnects: non_neg_integer() }
Functions
@spec current_backoff_ms(t()) :: non_neg_integer()
Returns the backoff (in ms) that applies to the next attempt.
iex> r = MobDev.Bench.Reconnector.new(:node@host, :secret)
iex> MobDev.Bench.Reconnector.current_backoff_ms(r)
0
iex> r = %{MobDev.Bench.Reconnector.new(:node@host, :secret) | attempts: 3}
iex> MobDev.Bench.Reconnector.current_backoff_ms(r)
8000
Initialise a reconnector for node with cookie. Optional :max_backoff_ms.
Record that the most recent reconnect attempt succeeded — resets the backoff counter and bumps the total_reconnects counter.
@spec tick(t(), MobDev.Bench.Probe.t() | atom(), integer()) :: {:no_action | :attempt, t()}
Decide whether the caller should attempt a reconnect right now, given the
current probe state and current time. Returns {action, updated_reconnector}.
Actions:
:no_action— connection is healthy or it's not time yet:attempt— caller should tryNode.connect(reconnector.node)now
After a successful reconnect, call record_success/1 to reset the backoff.