ExMacOSControl.Retry (ExMacOSControl v0.1.2)
View SourceRetry logic for transient failures in macOS automation.
This module provides automatic retry functionality with configurable backoff strategies to handle transient failures like timeouts. It's particularly useful when automating macOS operations that may temporarily fail due to system state or application responsiveness.
Supported Backoff Strategies
:exponential- Doubles the wait time between each retry (default):linear- Uses a constant wait time between retries
When to Use Retry Logic
Retry logic is appropriate for:
- Timeout errors that may succeed on subsequent attempts
- Operations that depend on application state that may change
- Network-dependent operations within scripts
- UI automation that may be affected by system responsiveness
Do NOT use retry logic for:
- Syntax errors (these won't be fixed by retrying)
- Permission errors (user intervention required)
- Not found errors (resources won't appear by retrying)
Examples
# Basic retry with exponential backoff (default)
Retry.with_retry(fn ->
ExMacOSControl.run_applescript(script)
end)
# Custom max attempts with linear backoff
Retry.with_retry(fn ->
ExMacOSControl.run_applescript(script, timeout: 5000)
end, max_attempts: 5, backoff: :linear)
# Using with application modules
Retry.with_retry(fn ->
ExMacOSControl.Finder.list_windows()
end, max_attempts: 3)Telemetry
This module emits telemetry events for retry operations:
[:ex_macos_control, :retry, :start]- When retry logic begins[:ex_macos_control, :retry, :attempt]- On each retry attempt[:ex_macos_control, :retry, :stop]- When retry logic completes[:ex_macos_control, :retry, :error]- When all retries are exhausted
Event Metadata
attempt- Current attempt number (1-indexed)max_attempts- Maximum number of attempts configuredbackoff- Backoff strategy in usesleep_time- Time slept before retry (in milliseconds)error- Error that triggered retry or final error
Summary
Functions
Executes a function with automatic retry on timeout errors.
Functions
@spec with_retry(fun :: (-> {:ok, any()} | {:error, any()}), opts :: keyword()) :: {:ok, any()} | {:error, any()}
Executes a function with automatic retry on timeout errors.
Parameters
fun- A zero-arity function that returns{:ok, result}or{:error, error}opts- Keyword list of options::max_attempts- Maximum number of attempts (default: 3):backoff- Backoff strategy,:exponentialor:linear(default::exponential)
Returns
{:ok, result}- If the function succeeds within max attempts{:error, error}- If all attempts fail or a non-retryable error occurs
Retry Behavior
Only timeout errors (errors with type: :timeout) are retried. All other errors
are returned immediately without retrying.
Examples
# With default options (3 attempts, exponential backoff)
iex> Retry.with_retry(fn -> {:ok, "success"} end)
{:ok, "success"}
# With timeout error that succeeds on retry
iex> state = :ets.new(:test, [:public])
iex> :ets.insert(state, {:attempts, 0})
iex> fun = fn ->
...> [{:attempts, count}] = :ets.lookup(state, :attempts)
...> :ets.insert(state, {:attempts, count + 1})
...> if count < 2 do
...> {:error, %{type: :timeout, message: "timeout"}}
...> else
...> {:ok, "success"}
...> end
...> end
iex> Retry.with_retry(fun)
{:ok, "success"}
# With non-timeout error (no retry)
iex> Retry.with_retry(fn -> {:error, %{type: :syntax_error}} end)
{:error, %{type: :syntax_error}}
# With custom options
iex> Retry.with_retry(fn -> {:ok, "done"} end, max_attempts: 5, backoff: :linear)
{:ok, "done"}