ExMCP.Reliability.Retry (ex_mcp v0.10.0)
View SourceRetry logic with exponential backoff for MCP operations.
Provides configurable retry strategies to handle transient failures in distributed systems. Supports exponential backoff with jitter to prevent thundering herd problems.
Usage
# Simple retry with defaults
Retry.with_retry(fn ->
ExMCP.Client.call_tool(client, "tool", %{})
end)
# Custom configuration
Retry.with_retry(
fn -> risky_operation() end,
max_attempts: 5,
initial_delay: 100,
max_delay: 10_000,
backoff_factor: 2,
jitter: true
)
# With custom retry condition
Retry.with_retry(
fn -> http_request() end,
should_retry?: fn
{:error, %{status: status}} when status in 500..599 -> true
{:error, :timeout} -> true
_ -> false
end
)Strategies
- Exponential backoff: Delay increases exponentially with each attempt
- Jitter: Random variation to prevent synchronized retries
- Circuit breaker integration: Can be combined with circuit breakers
Summary
Functions
Calculates the delay for a given attempt using exponential backoff.
Retry configuration specifically for MCP operations.
Merges client default retry policy with operation-specific overrides.
Executes multiple operations with retry, stopping on first success.
Executes a function with linear retry (fixed delay).
Executes a function with retry logic.
Creates a retry-wrapped version of a function.
Types
@type retry_opts() :: [ max_attempts: pos_integer(), initial_delay: pos_integer(), max_delay: pos_integer(), backoff_factor: number(), jitter: boolean(), should_retry?: (any() -> boolean()), on_retry: (pos_integer(), any() -> any()) ]
Functions
@spec calculate_delay( pos_integer(), keyword() ) :: pos_integer()
Calculates the delay for a given attempt using exponential backoff.
Useful for custom retry implementations.
Retry configuration specifically for MCP operations.
Returns retry options optimized for MCP protocol operations.
Merges client default retry policy with operation-specific overrides.
Operation-specific values take precedence over client defaults.
Examples
iex> ExMCP.Reliability.Retry.merge_policies(
...> [max_attempts: 5, initial_delay: 100],
...> [max_attempts: 3]
...> )
[max_attempts: 3, initial_delay: 100]
@spec with_fallback([function()], retry_opts()) :: {:ok, any()} | {:error, :all_failed}
Executes multiple operations with retry, stopping on first success.
Useful for fallback scenarios where you have multiple ways to achieve the same result.
Example
Retry.with_fallback([
fn -> primary_service_call() end,
fn -> secondary_service_call() end,
fn -> fallback_local_data() end
])
Executes a function with linear retry (fixed delay).
Options
:max_attempts- Maximum number of attempts (default: 3):delay- Fixed delay between attempts in ms (default: 1000):should_retry?- Function to determine if retry should occur:on_retry- Callback function called before each retry
@spec with_retry(function(), retry_opts()) :: {:ok, any()} | {:error, any()}
Executes a function with retry logic.
Options
:max_attempts- Maximum number of attempts (default: 3):initial_delay- Initial delay in ms (default: 100):max_delay- Maximum delay in ms (default: 5000):backoff_factor- Multiplier for exponential backoff (default: 2):jitter- Add randomization to delays (default: true):should_retry?- Function to determine if retry should occur (default: retry on any error):on_retry- Callback function called before each retry with (attempt, error)
Returns
{:ok, result}if operation succeeds{:error, reason}if all retries are exhausted
@spec wrap(function(), retry_opts()) :: function()
Creates a retry-wrapped version of a function.
Useful for wrapping functions that should always be retried.
Example
retryable_call = Retry.wrap(fn -> unstable_api_call() end, max_attempts: 5)
# Later...
result = retryable_call.()