Class: ComplyanceSDK::Retry::RetryStrategy

Inherits:
Object
  • Object
show all
Defined in:
lib/complyance_sdk/retry/retry_strategy.rb

Overview

Advanced retry strategy with exponential backoff, jitter, and circuit breaker Ruby equivalent of the Java RetryStrategy

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config, circuit_breaker = nil, logger = nil) ⇒ RetryStrategy

Initialize a new retry strategy

Parameters:



17
18
19
20
21
# File 'lib/complyance_sdk/retry/retry_strategy.rb', line 17

def initialize(config, circuit_breaker = nil, logger = nil)
  @config = config
  @circuit_breaker = circuit_breaker
  @logger = logger || Logger.new(STDOUT)
end

Instance Attribute Details

#circuit_breakerObject (readonly)

Returns the value of attribute circuit_breaker.



10
11
12
# File 'lib/complyance_sdk/retry/retry_strategy.rb', line 10

def circuit_breaker
  @circuit_breaker
end

#configObject (readonly)

Returns the value of attribute config.



10
11
12
# File 'lib/complyance_sdk/retry/retry_strategy.rb', line 10

def config
  @config
end

#loggerObject (readonly)

Returns the value of attribute logger.



10
11
12
# File 'lib/complyance_sdk/retry/retry_strategy.rb', line 10

def logger
  @logger
end

Instance Method Details

#circuit_breaker_stateSymbol?

Get the current circuit breaker state (for monitoring)

Returns:

  • (Symbol, nil)

    The circuit breaker state or nil if no circuit breaker



108
109
110
# File 'lib/complyance_sdk/retry/retry_strategy.rb', line 108

def circuit_breaker_state
  @circuit_breaker&.state
end

#circuit_breaker_statsString

Get circuit breaker statistics (for monitoring)

Returns:

  • (String)

    Circuit breaker statistics string



115
116
117
118
119
120
121
122
# File 'lib/complyance_sdk/retry/retry_strategy.rb', line 115

def circuit_breaker_stats
  if @circuit_breaker
    stats = @circuit_breaker.stats
    "CircuitBreaker{state=#{stats[:state]}, failures=#{stats[:failure_count]}, last_failure=#{stats[:last_failure_time]}}"
  else
    'Circuit breaker disabled'
  end
end

#execute(operation_name = 'operation', context = {}) { ... } ⇒ Object

Execute a block with retry logic

Parameters:

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

    Name of the operation for logging

  • context (Hash) (defaults to: {})

    Context information for logging/debugging

Yields:

  • The block to execute

Returns:

  • The result of the block



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/complyance_sdk/retry/retry_strategy.rb', line 29

def execute(operation_name = 'operation', context = {})
  attempt = 1
  last_exception = nil

  while attempt <= @config.max_attempts
    begin
      @logger.debug("Attempting operation '#{operation_name}' (attempt #{attempt}/#{@config.max_attempts})")

      result = if @circuit_breaker
                 @circuit_breaker.execute { yield }
               else
                 yield
               end

      if attempt > 1
        @logger.info("Operation '#{operation_name}' succeeded on attempt #{attempt}")
      end

      return result
    rescue ComplyanceSDK::Exceptions::SDKException => e
      last_exception = e

      # Check if this error should be retried
      unless should_retry?(e, attempt)
        @logger.debug("Operation '#{operation_name}' failed with non-retryable error: #{e.message}")
        raise e
      end

      # If this was the last attempt, don't wait
      break if attempt >= @config.max_attempts

      # Calculate delay and wait
      delay = calculate_delay(attempt)
      @logger.warn("Operation '#{operation_name}' failed on attempt #{attempt} (#{e.message}), retrying in #{(delay * 1000).round}ms")

      sleep(delay)
    rescue => e
      # Handle non-SDK exceptions
      @logger.error("Unexpected error in operation '#{operation_name}': #{e.message}")

      error_detail = {
        code: :processing_error,
        message: "Unexpected error: #{e.message}",
        suggestion: "This appears to be an unexpected error. Please contact support if it persists",
        context: { original_exception: e.class.name }
      }
      
      raise ComplyanceSDK::Exceptions::SDKException.new(
        "Unexpected error: #{e.message}",
        context: error_detail
      )
    end

    attempt += 1
  end

  # All retries exhausted
  @logger.error("Operation '#{operation_name}' failed after #{@config.max_attempts} attempts")
  
  if last_exception
    raise last_exception
  else
    error_detail = {
      code: :max_retries_exceeded,
      message: "Max retries exceeded",
      suggestion: "Operation failed after #{@config.max_attempts} attempts",
      context: { attempts: @config.max_attempts }
    }
    
    raise ComplyanceSDK::Exceptions::SDKException.new(
      "Max retries exceeded",
      context: error_detail
    )
  end
end

#reset_circuit_breaker!Object

Reset the circuit breaker (for testing/administrative purposes)



125
126
127
128
129
130
131
132
# File 'lib/complyance_sdk/retry/retry_strategy.rb', line 125

def reset_circuit_breaker!
  if @circuit_breaker
    @circuit_breaker.reset!
    @logger.info('Circuit breaker reset')
  else
    @logger.warn('Circuit breaker reset requested but no circuit breaker configured')
  end
end