Class: A2A::Client::Middleware::RetryInterceptor
- Inherits:
-
Object
- Object
- A2A::Client::Middleware::RetryInterceptor
- Defined in:
- lib/a2a/client/middleware/retry_interceptor.rb
Constant Summary collapse
- DEFAULT_RETRYABLE_ERRORS =
Default retryable error classes
[ A2A::Errors::TimeoutError, A2A::Errors::HTTPError, A2A::Errors::TransportError, A2A::Errors::AgentUnavailable, A2A::Errors::ResourceExhausted, Faraday::TimeoutError, Faraday::ConnectionFailed ].freeze
Instance Attribute Summary collapse
-
#backoff_multiplier ⇒ Object
readonly
Returns the value of attribute backoff_multiplier.
-
#initial_delay ⇒ Object
readonly
Returns the value of attribute initial_delay.
-
#max_attempts ⇒ Object
readonly
Returns the value of attribute max_attempts.
-
#max_delay ⇒ Object
readonly
Returns the value of attribute max_delay.
-
#retryable_errors ⇒ Object
readonly
Returns the value of attribute retryable_errors.
Instance Method Summary collapse
-
#calculate_delay(attempt) ⇒ Float
Calculate delay for the given attempt.
-
#call(request, context, next_middleware) ⇒ Object
Execute request with retry logic.
-
#initialize(max_attempts: 3, initial_delay: 1.0, max_delay: 60.0, backoff_multiplier: 2.0, retryable_errors: nil) ⇒ RetryInterceptor
constructor
Initialize retry interceptor.
-
#retryable_error?(error) ⇒ Boolean
Check if an error is retryable.
-
#should_retry?(error, attempt) ⇒ Boolean
Check if an error should trigger a retry.
-
#stats ⇒ Hash
Get retry statistics.
-
#validate_configuration! ⇒ Object
private
Validate configuration parameters.
Constructor Details
#initialize(max_attempts: 3, initial_delay: 1.0, max_delay: 60.0, backoff_multiplier: 2.0, retryable_errors: nil) ⇒ RetryInterceptor
Initialize retry interceptor
34 35 36 37 38 39 40 41 42 43 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 34 def initialize(max_attempts: 3, initial_delay: 1.0, max_delay: 60.0, backoff_multiplier: 2.0, retryable_errors: nil) @max_attempts = max_attempts @initial_delay = initial_delay @max_delay = max_delay @backoff_multiplier = backoff_multiplier @retryable_errors = retryable_errors || DEFAULT_RETRYABLE_ERRORS validate_configuration! end |
Instance Attribute Details
#backoff_multiplier ⇒ Object (readonly)
Returns the value of attribute backoff_multiplier.
13 14 15 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 13 def backoff_multiplier @backoff_multiplier end |
#initial_delay ⇒ Object (readonly)
Returns the value of attribute initial_delay.
13 14 15 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 13 def initial_delay @initial_delay end |
#max_attempts ⇒ Object (readonly)
Returns the value of attribute max_attempts.
13 14 15 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 13 def max_attempts @max_attempts end |
#max_delay ⇒ Object (readonly)
Returns the value of attribute max_delay.
13 14 15 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 13 def max_delay @max_delay end |
#retryable_errors ⇒ Object (readonly)
Returns the value of attribute retryable_errors.
13 14 15 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 13 def retryable_errors @retryable_errors end |
Instance Method Details
#calculate_delay(attempt) ⇒ Float
Calculate delay for the given attempt
118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 118 def calculate_delay(attempt) return 0 if attempt <= 1 # Exponential backoff: initial_delay * (backoff_multiplier ^ (attempt - 2)) delay = @initial_delay * (@backoff_multiplier**(attempt - 2)) # Add jitter to prevent thundering herd jitter = delay * 0.1 * rand delay += jitter # Cap at max_delay [delay, @max_delay].min end |
#call(request, context, next_middleware) ⇒ Object
Execute request with retry logic
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 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 52 def call(request, context, next_middleware) attempt = 0 last_error = nil loop do attempt += 1 begin return next_middleware.call(request, context) rescue StandardError => e last_error = e # Check if we should retry raise e unless should_retry?(e, attempt) delay = calculate_delay(attempt) context[:retry_attempt] = attempt context[:retry_delay] = delay sleep(delay) if delay.positive? next # Re-raise the error if we shouldn't retry or max attempts reached end end end |
#retryable_error?(error) ⇒ Boolean
Check if an error is retryable
109 110 111 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 109 def retryable_error?(error) @retryable_errors.any? { |error_class| error.is_a?(error_class) } end |
#should_retry?(error, attempt) ⇒ Boolean
Check if an error should trigger a retry
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 85 def should_retry?(error, attempt) return false if attempt >= @max_attempts return false unless retryable_error?(error) # Check for specific HTTP status codes that shouldn't be retried if error.respond_to?(:status_code) case error.status_code when 400, 401, 403, 404, 422 # Client errors - don't retry return false when 429 # Rate limited - should retry return true when 500..599 # Server errors - should retry return true end end true end |
#stats ⇒ Hash
Get retry statistics
136 137 138 139 140 141 142 143 144 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 136 def stats { max_attempts: @max_attempts, initial_delay: @initial_delay, max_delay: @max_delay, backoff_multiplier: @backoff_multiplier, retryable_errors: @retryable_errors.map(&:name) } end |
#validate_configuration! ⇒ Object (private)
Validate configuration parameters
150 151 152 153 154 155 156 157 |
# File 'lib/a2a/client/middleware/retry_interceptor.rb', line 150 def validate_configuration! raise ArgumentError, "max_attempts must be positive" if @max_attempts <= 0 raise ArgumentError, "initial_delay must be non-negative" if @initial_delay.negative? raise ArgumentError, "max_delay must be positive" if @max_delay <= 0 raise ArgumentError, "backoff_multiplier must be positive" if @backoff_multiplier <= 0 raise ArgumentError, "initial_delay cannot be greater than max_delay" if @initial_delay > @max_delay end |