Module: ActivePostgres::RetryHelper

Defined in:
lib/active_postgres/retry_helper.rb

Defined Under Namespace

Classes: RetryExhausted

Instance Method Summary collapse

Instance Method Details

#retry_with_backoff(max_attempts: 3, initial_delay: 1.0, max_delay: 30.0, backoff_factor: 2.0, on: [StandardError]) { ... } ⇒ Object

Retry a block with exponential backoff

Parameters:

  • max_attempts (Integer) (defaults to: 3)

    Maximum number of attempts

  • initial_delay (Float) (defaults to: 1.0)

    Initial delay in seconds

  • max_delay (Float) (defaults to: 30.0)

    Maximum delay between retries

  • backoff_factor (Float) (defaults to: 2.0)

    Multiplier for exponential backoff

  • on (Array<Class>) (defaults to: [StandardError])

    Exception classes to retry on

Yields:

  • Block to retry

Returns:

  • Result of the block



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/active_postgres/retry_helper.rb', line 13

def retry_with_backoff(max_attempts: 3, initial_delay: 1.0, max_delay: 30.0,
                       backoff_factor: 2.0, on: [StandardError])
  attempt = 0
  delay = initial_delay

  begin
    attempt += 1
    yield
  rescue *on => e
    if attempt < max_attempts
      puts "  Attempt #{attempt}/#{max_attempts} failed: #{e.message}"
      puts "  Retrying in #{delay.round(1)}s..."
      sleep delay
      delay = [delay * backoff_factor, max_delay].min
      retry
    else
      puts "  All #{max_attempts} attempts failed"
      raise RetryExhausted, "Failed after #{max_attempts} attempts: #{e.message}"
    end
  end
end

#wait_for(timeout: 60, interval: 3, description: 'condition') { ... } ⇒ Boolean

Wait for a condition to be true with timeout

Parameters:

  • timeout (Float) (defaults to: 60)

    Maximum time to wait in seconds

  • interval (Float) (defaults to: 3)

    Time between checks in seconds

  • description (String) (defaults to: 'condition')

    Description of what we’re waiting for

Yields:

  • Block that should return true when condition is met

Returns:

  • (Boolean)

    true if condition met, false if timeout



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/active_postgres/retry_helper.rb', line 41

def wait_for(timeout: 60, interval: 3, description: 'condition')
  deadline = Time.now + timeout
  attempts = 0

  while Time.now < deadline
    attempts += 1

    begin
      return true if yield
    rescue StandardError => e
      puts "  Check #{attempts} raised error: #{e.message}" if (attempts % 10).zero?
    end

    remaining = (deadline - Time.now).to_i
    puts "  Waiting for #{description}... (#{remaining}s remaining)" if (attempts % 5).zero?
    sleep interval
  end

  puts "  ⚠️  Timeout waiting for #{description} after #{timeout}s"
  false
end

#with_timeout(timeout: 300, description: 'operation') { ... } ⇒ Object

Execute a block with a timeout

Parameters:

  • timeout (Float) (defaults to: 300)

    Timeout in seconds

  • description (String) (defaults to: 'operation')

    Description of the operation

Yields:

  • Block to execute

Returns:

  • Result of the block



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/active_postgres/retry_helper.rb', line 68

def with_timeout(timeout: 300, description: 'operation')
  result = nil
  thread = Thread.new { result = yield }

  unless thread.join(timeout)
    thread.kill
    raise Timeout::Error, "#{description} timed out after #{timeout}s"
  end

  result
end