Module: Smith::PersistenceAdapters::Retry
- Defined in:
- lib/smith/persistence_adapters/retry.rb
Overview
Generic retry-with-exponential-backoff wrapper used by adapter store/fetch/delete/store_versioned operations to survive transient I/O errors. After attempts exhausted, raises Smith::PersistenceIOError wrapping the underlying cause.
Adapter-agnostic: each adapter passes its own transient: error
class list because Redis transient errors differ from AR transient
errors differ from cache-backend transient errors. The Memory
adapter passes an empty list (it never raises transient errors).
Class Method Summary collapse
- .log_retry(logger, operation, error, retry_context) ⇒ Object
- .schedule_for(policy) ⇒ Object
- .with_retries(operation:, transient:, policy: Smith.config.persistence_retry_policy, logger: Smith.config.logger) ⇒ Object
Class Method Details
.log_retry(logger, operation, error, retry_context) ⇒ Object
45 46 47 48 49 50 51 |
# File 'lib/smith/persistence_adapters/retry.rb', line 45 def log_retry(logger, operation, error, retry_context) logger&.warn( "Smith::PersistenceAdapters::Retry #{operation} " \ "attempt #{retry_context.fetch(:attempt)}/#{retry_context.fetch(:attempts)} failed: " \ "#{error.class}: #{error.}; sleeping #{retry_context.fetch(:delay)}s" ) end |
.schedule_for(policy) ⇒ Object
36 37 38 39 40 41 42 43 |
# File 'lib/smith/persistence_adapters/retry.rb', line 36 def schedule_for(policy) ExponentialBackoff.new( attempts: policy.fetch(:attempts, 3), base_delay: policy.fetch(:base_delay, 0.1), max_delay: policy.fetch(:max_delay, 1.0), jitter: 0 ) end |
.with_retries(operation:, transient:, policy: Smith.config.persistence_retry_policy, logger: Smith.config.logger) ⇒ Object
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/smith/persistence_adapters/retry.rb', line 17 def with_retries(operation:, transient:, policy: Smith.config.persistence_retry_policy, logger: Smith.config.logger) schedule = schedule_for(policy) last_error = nil schedule.attempts.times do |i| return yield rescue *transient => e last_error = e break if i == schedule.attempts - 1 delay = schedule.delay(i + 1) log_retry(logger, operation, e, { attempt: i + 1, attempts: schedule.attempts, delay: }) sleep(delay) end raise Smith::PersistenceIOError.new(operation: operation, cause: last_error) end |