Module: LanguageOperator::Retryable

Included in:
Client::Base
Defined in:
lib/language_operator/retryable.rb

Overview

Mixin module to provide retry logic with exponential backoff for operations that may fail transiently.

Examples:

Basic usage

class MyService
  include LanguageOperator::Retryable

  def connect
    with_retry do
      # Connection logic that might fail
      make_connection
    end
  end
end

Custom retry configuration

with_retry(max_attempts: 5, base_delay: 2.0) do
  risky_operation
end

With custom error handling

with_retry(on_retry: ->(error, attempt) { log_error(error, attempt) }) do
  api_call
end

Constant Summary collapse

DEFAULT_MAX_ATTEMPTS =

Default retry configuration

3
DEFAULT_BASE_DELAY =
1.0
DEFAULT_MAX_DELAY =
30.0

Instance Method Summary collapse

Instance Method Details

#with_retry(max_attempts: DEFAULT_MAX_ATTEMPTS, base_delay: DEFAULT_BASE_DELAY, max_delay: DEFAULT_MAX_DELAY, rescue_errors: [StandardError], on_retry: nil) { ... } ⇒ Object

Execute a block with retry logic and exponential backoff.

Examples:

result = with_retry(max_attempts: 5) do
  fetch_from_api
end

Yields:

  • Block to execute with retry logic

Raises:

  • Last error encountered if all retries are exhausted

Parameters:

  • (defaults to: DEFAULT_MAX_ATTEMPTS)

    Maximum number of attempts (default: 3)

  • (defaults to: DEFAULT_BASE_DELAY)

    Base delay in seconds for exponential backoff (default: 1.0)

  • (defaults to: DEFAULT_MAX_DELAY)

    Maximum delay between retries in seconds (default: 30.0)

  • (defaults to: [StandardError])

    Array of error classes to rescue (default: StandardError)

  • (defaults to: nil)

    Optional callback called on each retry attempt with (error, attempt, delay)

Returns:

  • Result of the block if successful



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/language_operator/retryable.rb', line 49

def with_retry(
  max_attempts: DEFAULT_MAX_ATTEMPTS,
  base_delay: DEFAULT_BASE_DELAY,
  max_delay: DEFAULT_MAX_DELAY,
  rescue_errors: [StandardError],
  on_retry: nil
)
  attempt = 0
  last_error = nil

  loop do
    attempt += 1

    begin
      return yield
    rescue *rescue_errors => e
      last_error = e

      raise e if attempt >= max_attempts

      # Calculate delay with exponential backoff: base_delay * 2^(attempt-1)
      delay = [base_delay * (2**(attempt - 1)), max_delay].min

      # Call the retry callback if provided
      on_retry&.call(e, attempt, delay)

      sleep(delay)
    end
  end
end

#with_retry_or_nil(max_attempts: DEFAULT_MAX_ATTEMPTS, base_delay: DEFAULT_BASE_DELAY, max_delay: DEFAULT_MAX_DELAY, rescue_errors: [StandardError], on_retry: nil, on_failure: nil) { ... } ⇒ Object

Execute a block with retry logic and return nil on failure instead of raising.

Examples:

result = with_retry_or_nil(on_failure: ->(err, tries) { log_failure(err) }) do
  optional_operation
end

return unless result

Yields:

  • Block to execute with retry logic

Parameters:

  • (defaults to: DEFAULT_MAX_ATTEMPTS)

    Maximum number of attempts (default: 3)

  • (defaults to: DEFAULT_BASE_DELAY)

    Base delay in seconds for exponential backoff (default: 1.0)

  • (defaults to: DEFAULT_MAX_DELAY)

    Maximum delay between retries in seconds (default: 30.0)

  • (defaults to: [StandardError])

    Array of error classes to rescue (default: StandardError)

  • (defaults to: nil)

    Optional callback called on each retry attempt with (error, attempt, delay)

  • (defaults to: nil)

    Optional callback called when all retries are exhausted with (error, attempts)

Returns:

  • Result of the block if successful, nil if all retries failed



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/language_operator/retryable.rb', line 97

def with_retry_or_nil(
  max_attempts: DEFAULT_MAX_ATTEMPTS,
  base_delay: DEFAULT_BASE_DELAY,
  max_delay: DEFAULT_MAX_DELAY,
  rescue_errors: [StandardError],
  on_retry: nil,
  on_failure: nil
)
  attempt = 0
  last_error = nil

  loop do
    attempt += 1

    begin
      return yield
    rescue *rescue_errors => e
      last_error = e

      if attempt >= max_attempts
        on_failure&.call(e, attempt)
        return nil
      end

      # Calculate delay with exponential backoff
      delay = [base_delay * (2**(attempt - 1)), max_delay].min

      # Call the retry callback if provided
      on_retry&.call(e, attempt, delay)

      sleep(delay)
    end
  end
end