### Ruby DSL Integration with ChronoMachines (ApiClient Example) Source: https://github.com/seuros/chrono_machines/blob/master/README.md Provides a practical example of ChronoMachines' DSL integration within an ApiClient class. It demonstrates defining multiple named policies (`standard_api`, `critical_api`) and applying them using `with_chrono_policy`, including an example of inline options for a specific, one-off scenario. ```ruby class ApiClient include ChronoMachines::DSL # Define policies at class level chrono_policy :standard_api, max_attempts: 5, base_delay: 0.1, multiplier: 2 chrono_policy :critical_api, max_attempts: 10, base_delay: 0.05, max_delay: 5 def fetch_user_data(id) with_chrono_policy(:standard_api) do api_request("/users/#{id}") end end def emergency_shutdown # Use inline options for one-off scenarios with_chrono_policy(max_attempts: 1, base_delay: 0) do shutdown_api_call end end end ``` -------------------------------- ### Ruby Minitest Setup with ChronoMachines TestHelper Source: https://github.com/seuros/chrono_machines/blob/master/README.md Sets up a Minitest test class to integrate with ChronoMachines. It requires the `chrono_machines/test_helper` and includes `ChronoMachines::TestHelper` to automatically reset configuration before each test, ensuring isolation. ```ruby require 'chrono_machines/test_helper' class MyLibraryTest < Minitest::Test include ChronoMachines::TestHelper def setup super # Important: calls ChronoMachines config reset # Your setup code here end end ``` -------------------------------- ### Install ChronoMachines Gem Source: https://github.com/seuros/chrono_machines/blob/master/README.md This snippet shows how to add the ChronoMachines gem to your project's Gemfile for installation. It's a standard Ruby dependency management step. ```bash gem 'chrono_machines' ``` -------------------------------- ### Ruby ChronoMachines Integration and Custom Policy Source: https://github.com/seuros/chrono_machines/blob/master/README.md Shows an integration example in Ruby, setting up a base test class with `ChronoMachines::TestHelper` and then defining a specific test that uses a custom retry policy. The `ChronoMachines.configure` block defines `:test_policy` with `max_attempts: 2`, and `ChronoMachines.retry` executes a block with this policy. ```ruby # In your gem's test_helper.rb require 'minitest/autorun' require 'chrono_machines/test_helper' class TestBase < Minitest::Test include ChronoMachines::TestHelper def setup super # Reset any additional state end end # In your specific tests class RetryServiceTest < TestBase def test_retry_with_custom_policy # ChronoMachines config is automatically reset # You can safely define test-specific policies ChronoMachines.configure do |config| config.define_policy(:test_policy, max_attempts: 2) end result = ChronoMachines.retry(:test_policy) do "success" end assert_equal "success", result end end ``` -------------------------------- ### Integrate ChronoMachines Test Helper in Ruby Source: https://context7.com/seuros/chrono_machines/llms.txt This snippet demonstrates how to integrate ChronoMachines' testing utilities into your Ruby test suite, typically within `test/test_helper.rb`. It includes instructions for requiring the helper and including it in your test base class. The examples show how to use `ChronoMachines.retry` within tests and verify its behavior, including exception handling and delay calculations. ```ruby # test/test_helper.rb require 'chrono_machines/test_helper' class ActiveSupport::TestCase include ChronoMachines::TestHelper end # test/services/api_client_test.rb class ApiClientTest < ActiveSupport::TestCase def test_retries_on_timeout client = ApiClient.new call_count = 0 # Configuration is automatically reset before this test result = ChronoMachines.retry( max_attempts: 3, base_delay: 0.001 # Short delay for test speed ) do call_count += 1 raise Net::TimeoutError if call_count < 2 { status: 'success', data: 'response' } end assert_equal({ status: 'success', data: 'response' }, result) assert_equal 2, call_count end def test_delay_calculation_within_jitter_range executor = ChronoMachines::Executor.new(base_delay: 0.1, multiplier: 2) # Test first attempt delay (0 to 0.1s with full jitter) delay = executor.send(:calculate_delay, 1) assert_cm_delay_range(delay, 0.0, 0.1, "First attempt delay incorrect") # Test second attempt delay (0 to 0.2s with full jitter) delay = executor.send(:calculate_delay, 2) assert_cm_delay_range(delay, 0.0, 0.2, "Second attempt delay incorrect") end def test_preserves_original_exception error = Net::TimeoutError.new('Connection timeout') begin ChronoMachines.retry(max_attempts: 2, base_delay: 0.001) do raise error end flunk 'Expected MaxRetriesExceededError' rescue ChronoMachines::MaxRetriesExceededError => e assert_equal 2, e.attempts assert_equal error, e.original_exception assert_instance_of Net::TimeoutError, e.original_exception end end end ``` -------------------------------- ### ChronoMachines with Custom RNG for no_std in Rust Source: https://github.com/seuros/chrono_machines/blob/master/ext/chrono_machines_native/core/README.md Illustrates how to use chrono_machines in a `no_std` environment by providing a custom random number generator (RNG). This example uses `rand::rngs::SmallRng` and `calculate_delay_with_rng`. ```rust use chrono_machines::Policy; use rand::rngs::SmallRng; use rand::SeedableRng; let policy = Policy::default(); let mut rng = SmallRng::seed_from_u64(12345); // Calculate delay with 50% jitter (0.5) let delay = policy.calculate_delay_with_rng(1, 0.5, &mut rng); ``` -------------------------------- ### Policy-Based Configuration with ChronoMachines Source: https://github.com/seuros/chrono_machines/blob/master/README.md Shows how to configure and use named retry policies globally within ChronoMachines. This allows defining reusable retry strategies that can be applied to different operations by their names, promoting consistency. ```ruby # Configure global policies ChronoMachines.configure do |config| config.define_policy(:aggressive, max_attempts: 10, base_delay: 0.01, multiplier: 1.5) config.define_policy(:conservative, max_attempts: 3, base_delay: 1.0, multiplier: 2) config.define_policy(:database, max_attempts: 5, retryable_exceptions: [ActiveRecord::ConnectionError]) end # Use named policies result = ChronoMachines.retry(:database) do User.find(user_id) end ``` -------------------------------- ### Basic ChronoMachines Policy Usage in Rust Source: https://github.com/seuros/chrono_machines/blob/master/ext/chrono_machines_native/core/README.md Demonstrates the basic usage of the `Policy` struct in Rust to configure and calculate a retry delay with full jitter. It sets parameters like max attempts, base delay, multiplier, and maximum delay. ```rust use chrono_machines::Policy; let policy = Policy { max_attempts: 5, base_delay_ms: 100, multiplier: 2.0, max_delay_ms: 10_000, }; // Calculate delay for first retry using full jitter (1.0) let delay_ms = policy.calculate_delay(1, 1.0); println!("Wait {}ms before retry", delay_ms); ``` -------------------------------- ### Test ChronoMachines DSL Policy Definition and Inline Options in Ruby Source: https://github.com/seuros/chrono_machines/blob/master/README.md Tests the integration of ChronoMachines' DSL for defining retry policies within classes and using inline options for overriding or specifying policies directly. It verifies that the defined policies are correctly applied and that inline options take precedence. ```ruby require "minitest/autorun" class DSLTestExample < Minitest::Test class TestService include ChronoMachines::DSL chrono_policy :test_policy, max_attempts: 2, base_delay: 0.001 def risky_operation with_chrono_policy(:test_policy) do # Simulated operation yield if block_given? end end end def test_dsl_policy_definition service = TestService.new call_count = 0 result = service.risky_operation do call_count += 1 raise "Fail" if call_count < 2 "Success" end assert_equal "Success", result assert_equal 2, call_count end def test_dsl_with_inline_options service = TestService.new assert_raises(ChronoMachines::MaxRetriesExceededError) do service.with_chrono_policy(max_attempts: 1) do raise "Always fails" end end end end ``` -------------------------------- ### Direct Retry with ChronoMachines in Ruby Source: https://github.com/seuros/chrono_machines/blob/master/README.md Illustrates how to use ChronoMachines for direct retry operations within Ruby code. It shows simple exponential backoff and more advanced configurations including retryable exceptions and callback hooks for on_retry and on_failure events. ```ruby # Simple retry with exponential backoff result = ChronoMachines.retry(max_attempts: 3, base_delay: 0.1) do fetch_external_data end # Advanced configuration result = ChronoMachines.retry( max_attempts: 5, base_delay: 0.2, multiplier: 3, max_delay: 30, retryable_exceptions: [Net::TimeoutError, HTTPError], on_retry: ->(exception:, attempt:, next_delay:) { Rails.logger.warn "Retry #{attempt}: #{exception.message}, waiting #{next_delay}s" }, on_failure: ->(exception:, attempts:) { Metrics.increment('api.retry.exhausted', tags: ["attempts:#{attempts}"]) } ) do external_api_call end ``` -------------------------------- ### Async Framework Integration for API Calls (Ruby) Source: https://context7.com/seuros/chrono_machines/llms.txt Demonstrates using ChronoMachines with the async gem for non-blocking, concurrent API requests. It defines policies for API interactions and shows how to fetch multiple user records or perform parallel API calls, with automatic retry logic applied to each. ```ruby require 'chrono_machines' require 'async' class AsyncApiClient include ChronoMachines::DSL chrono_policy :async_api, max_attempts: 4, base_delay: 0.2, retryable_exceptions: [Net::TimeoutError, Net::HTTPServerError] def fetch_multiple_users(user_ids) Async do # ChronoMachines automatically uses async-aware sleep tasks = user_ids.map do |user_id| Async do with_chrono_policy(:async_api) do response = HTTP.get("https://api.example.com/users/#{user_id}") JSON.parse(response.body) end end end # Wait for all concurrent requests to complete tasks.map(&:wait) end.wait end def parallel_api_calls Async do |task| results = [] # Multiple concurrent API calls with individual retry logic task.async do results << with_chrono_policy(:async_api) do HTTP.get('https://api.example.com/orders').body end end task.async do results << with_chrono_policy(:async_api) do HTTP.get('https://api.example.com/products').body end end task.async do results << with_chrono_policy(:async_api) do HTTP.get('https://api.example.com/customers').body end end task.wait # Wait for all tasks results end.wait end end # Usage client = AsyncApiClient.new users = client.fetch_multiple_users([1, 2, 3, 4, 5]) puts "Fetched #{users.count} users concurrently" ``` -------------------------------- ### Ruby DSL Integration with ChronoMachines Source: https://github.com/seuros/chrono_machines/blob/master/README.md Demonstrates integrating ChronoMachines policies directly into Ruby classes using its DSL. It defines retry policies at the class level and applies them within methods using `with_chrono_policy`. This approach encapsulates retry logic within the service class. ```ruby class PaymentService include ChronoMachines::DSL chrono_policy :stripe_payment, max_attempts: 5, base_delay: 0.1, multiplier: 2 def charge(amount) with_chrono_policy(:stripe_payment) do Stripe::Charge.create(amount: amount) end end end # Or use it directly result = ChronoMachines.retry(max_attempts: 3) do perform_risky_operation end ``` -------------------------------- ### Add ChronoMachines Dependency to Cargo.toml Source: https://github.com/seuros/chrono_machines/blob/master/ext/chrono_machines_native/core/README.md This snippet shows how to add the chrono_machines crate to your project's dependencies in `Cargo.toml` for standard usage. ```toml [dependencies] chrono_machines = "0.1" ``` -------------------------------- ### Direct Retry Execution with ChronoMachines Source: https://context7.com/seuros/chrono_machines/llms.txt Execute blocks of code with automatic retry logic using exponential backoff and full jitter. Supports basic defaults and advanced configuration including custom exceptions, retry callbacks, failure callbacks, and success callbacks. It also demonstrates error handling with preserved exception context. ```ruby require 'chrono_machines' # Basic retry with defaults (3 attempts, 0.1s base delay, 2x multiplier) result = ChronoMachines.retry do HTTParty.get('https://api.example.com/data') end # Advanced configuration with all options result = ChronoMachines.retry( max_attempts: 5, base_delay: 0.2, multiplier: 3, max_delay: 30, jitter_factor: 1.0, retryable_exceptions: [Net::TimeoutError, Net::HTTPServerError, Errno::ECONNREFUSED], on_retry: ->(exception:, attempt:, next_delay:) { Rails.logger.warn("Retry attempt #{attempt} after #{exception.class}: waiting #{next_delay}s") Metrics.increment('api.retry', tags: ["attempt:#{attempt}"]) }, on_failure: ->(exception:, attempts:) { PagerDuty.trigger("API call failed after #{attempts} attempts: #{exception.message}") Rails.cache.write('api_down', true, expires_in: 5.minutes) }, on_success: ->(result:, attempts:) { Metrics.histogram('api.success_attempts', attempts) } ) do response = HTTP.timeout(5).get('https://api.example.com/critical-data') JSON.parse(response.body) end # Error handling with preserved exception context begin ChronoMachines.retry(max_attempts: 3) do Stripe::Charge.create(amount: 1000, currency: 'usd') end rescue ChronoMachines::MaxRetriesExceededError => e puts "Failed after #{e.attempts} attempts" puts "Original error: #{e.original_exception.class} - #{e.original_exception.message}" handle_payment_failure(e.original_exception) end ``` -------------------------------- ### Ruby: Database Connection Resilience with Retries Source: https://context7.com/seuros/chrono_machines/llms.txt This Ruby code demonstrates how to configure automatic retries for database connection errors using ChronoMachines. It defines a retry policy with maximum attempts, delays, and specific exceptions to catch. It also includes callbacks for when a retry occurs or when the maximum retries are exceeded, logging errors and potentially notifying external services. ```ruby class UserRepository include ChronoMachines::DSL chrono_policy :db_connection, max_attempts: 5, base_delay: 0.1, multiplier: 2, max_delay: 5, retryable_exceptions: [ ActiveRecord::ConnectionTimeoutError, ActiveRecord::StatementInvalid, PG::ConnectionBad, PG::UnableToSend, Mysql2::Error::ConnectionError ], on_retry: ->(exception:, attempt:, next_delay:) { # Release stale connections before retry ActiveRecord::Base.connection_pool.release_connection Rails.logger.warn("[UserRepo] DB retry #{attempt}/5: #{exception.class} - waiting #{next_delay.round(2)}s") }, on_failure: ->(exception:, attempts:) { Honeybadger.notify(exception, context: { component: 'user_repository', attempts: attempts }) StatsD.increment('database.connection.exhausted') } def find_user(id) with_chrono_policy(:db_connection) do User.find(id) end end def create_user(attributes) with_chrono_policy(:db_connection) do User.create!(attributes) end rescue ChronoMachines::MaxRetriesExceededError => e Rails.logger.error("Failed to create user after #{e.attempts} attempts: #{e.original_exception.message}") raise DatabaseUnavailableError, "Unable to create user, please try again later" end def transaction_with_retry(&block) with_chrono_policy(:db_connection) do ActiveRecord::Base.transaction(&block) end end end # Usage repo = UserRepository.new user = repo.find_user(123) new_user = repo.create_user(email: 'user@example.com', name: 'John Doe') ``` -------------------------------- ### Global Policy Configuration with ChronoMachines Source: https://context7.com/seuros/chrono_machines/llms.txt Define reusable retry policies globally for consistent behavior across your application. Policies can be named and configured with specific retry parameters, including retryable exceptions and callbacks. These named policies can then be applied to different retry operations. ```ruby # config/initializers/chrono_machines.rb ChronoMachines.configure do |config| # Aggressive retry for critical services config.define_policy(:critical_service, max_attempts: 10, base_delay: 0.05, multiplier: 1.5, max_delay: 5, retryable_exceptions: [Net::TimeoutError, Errno::ECONNREFUSED] ) # Conservative retry for external APIs config.define_policy(:external_api, max_attempts: 3, base_delay: 1.0, multiplier: 2, max_delay: 30, retryable_exceptions: [Net::HTTPServerError, Net::HTTPTooManyRequests] ) # Database-specific retry config.define_policy(:database, max_attempts: 5, base_delay: 0.1, retryable_exceptions: [ ActiveRecord::ConnectionTimeoutError, ActiveRecord::DisconnectedError, PG::ConnectionBad, PG::UnableToSend ], on_retry: ->(exception:, attempt:, next_delay:) { ActiveRecord::Base.connection_pool.release_connection Rails.logger.warn("DB retry #{attempt}: #{exception.class}") } ) end # Use named policies throughout the application user = ChronoMachines.retry(:database) do User.find(user_id) end response = ChronoMachines.retry(:external_api) do HTTParty.post('https://api.external.com/webhook', body: data.to_json) end ``` -------------------------------- ### Define Class-Level Retry Policies with DSL in Ruby Source: https://context7.com/seuros/chrono_machines/llms.txt This snippet shows how to use the ChronoMachines DSL to define reusable retry policies at the class level. Policies can be named and configured with parameters like max attempts, delays, and specific exceptions to retry on. It also demonstrates how to use these policies within class methods and handle `MaxRetriesExceededError`. ```ruby class PaymentProcessor include ChronoMachines::DSL # Define class-level retry policies chrono_policy :stripe_api, max_attempts: 5, base_delay: 0.1, multiplier: 2, retryable_exceptions: [Stripe::APIConnectionError, Stripe::RateLimitError], on_retry: ->(exception:, attempt:, next_delay:) { StatsD.increment('payment.stripe.retry', tags: ["attempt:#{attempt}"]) }, on_failure: ->(exception:, attempts:) { AdminMailer.payment_system_failure(exception, attempts).deliver_later } chrono_policy :idempotency_check, max_attempts: 3, base_delay: 0.05 def charge_customer(amount, customer_id) # Use named policy with_chrono_policy(:stripe_api) do Stripe::Charge.create( amount: amount, currency: 'usd', customer: customer_id, idempotency_key: generate_idempotency_key ) end rescue ChronoMachines::MaxRetriesExceededError => e # Fallback to queuing for later processing FailedPaymentQueue.enqueue(amount: amount, customer_id: customer_id, error: e.message) raise PaymentError, "Unable to process payment, queued for retry" end def verify_transaction(transaction_id) # Use inline options for one-off scenarios with_chrono_policy(max_attempts: 2, base_delay: 0.1) do Stripe::Charge.retrieve(transaction_id) end end def emergency_refund(charge_id) # No retry for immediate operations with_chrono_policy(max_attempts: 1, base_delay: 0) do Stripe::Refund.create(charge: charge_id) end end end # Usage processor = PaymentProcessor.new charge = processor.charge_customer(5000, 'cus_12345') ``` -------------------------------- ### Implement Fallback Mechanism on Failure Source: https://github.com/seuros/chrono_machines/blob/master/README.md Shows how to implement fallback logic using the `on_failure` callback. When retries are exhausted, this mechanism can serve cached data or perform alternative actions, ensuring service availability even during external service outages. ```ruby # Execute fallback logic when retries are exhausted ChronoMachines.retry( max_attempts: 3, on_failure: ->(exception:, attempts:) { # Fallback doesn't throw - original exception is still raised Rails.cache.write("fallback_data_#{user_id}", cached_response, expires_in: 5.minutes) SlackNotifier.notify("API down, serving cached data for user #{user_id}") } ) do fetch_fresh_user_data end ``` -------------------------------- ### Implement Retry Policy for HTTP API Integration Source: https://github.com/seuros/chrono_machines/blob/master/README.md Sets up a retry policy named `:weather_api` for interacting with an external weather API. It specifies retry attempts, delays, relevant exceptions, and a failure callback to cache data when the API is down. ```ruby class WeatherService include ChronoMachines::DSL chrono_policy :weather_api, max_attempts: 4, base_delay: 0.2, max_delay: 10, retryable_exceptions: [Net::TimeoutError, Net::HTTPServerError], on_failure: ->(exception:, attempts:) { # Serve stale data when API is completely down Rails.cache.write("weather_service_down", true, expires_in: 5.minutes) } def current_weather(location) with_chrono_policy(:weather_api) do response = HTTP.timeout(connect: 2, read: 5) .get("https://api.weather.com/#{location}") JSON.parse(response.body) end rescue ChronoMachines::MaxRetriesExceededError # Return cached data if available Rails.cache.fetch("weather_#{location}", expires_in: 1.hour) do { temperature: "Unknown", status: "Service Unavailable" } end end end ``` -------------------------------- ### Define Named Retry Policy for Database Connections Source: https://github.com/seuros/chrono_machines/blob/master/README.md Defines a reusable retry policy named `:db_connection` specifically for handling database connection errors. It includes a list of relevant exceptions and a custom logging callback for retries. ```ruby class DatabaseService include ChronoMachines::DSL chrono_policy :db_connection, max_attempts: 5, base_delay: 0.1, retryable_exceptions: [ ActiveRecord::ConnectionTimeoutError, ActiveRecord::DisconnectedError, PG::ConnectionBad ], on_retry: ->(exception:, attempt:, next_delay:) { Rails.logger.warn "DB retry #{attempt}: #{exception.class}" } def find_user(id) with_chrono_policy(:db_connection) do User.find(id) end end end ``` -------------------------------- ### Full Jitter Exponential Backoff Explanation Source: https://github.com/seuros/chrono_machines/blob/master/README.md Illustrates ChronoMachines' implementation of full jitter exponential backoff. Unlike predictable delays, full jitter introduces randomness to the delay between retries, effectively preventing the 'thundering herd' problem. ```ruby # Instead of predictable delays that create thundering herds: # Attempt 1: 100ms # Attempt 2: 200ms # Attempt 3: 400ms # ChronoMachines uses full jitter: # Attempt 1: random(0, 100ms) # Attempt 2: random(0, 200ms) # Attempt 3: random(0, 400ms) ``` -------------------------------- ### Disable Default Features for no_std in Cargo.toml Source: https://github.com/seuros/chrono_machines/blob/master/ext/chrono_machines_native/core/README.md Shows how to configure chrono_machines in `Cargo.toml` to disable default features, making it compatible with `no_std` environments. This requires using `calculate_delay_with_rng` and providing your own RNG. ```toml [dependencies] chrono_machines = { version = "0.1", default-features = false } ``` -------------------------------- ### Define Custom Retry Policy with Callbacks Source: https://github.com/seuros/chrono_machines/blob/master/README.md Configures a retry policy with custom callbacks for success, retry, and failure events. This allows for detailed monitoring and logging of retry attempts, including sending notifications via PagerDuty on failure. ```ruby policy_options = { max_attempts: 5, on_success: ->(result:, attempts:) { Metrics.histogram('operation.attempts', attempts) Rails.logger.info "Operation succeeded after #{attempts} attempts" }, on_retry: ->(exception:, attempt:, next_delay:) { Metrics.increment('operation.retry', tags: ["attempt:#{attempt}"]) Honeybadger.notify(exception, context: { attempt: attempt, next_delay: next_delay }) }, on_failure: ->(exception:, attempts:) { Metrics.increment('operation.failure', tags: ["final_attempts:#{attempts}"]) PagerDuty.trigger("Operation failed after #{attempts} attempts: #{exception.message}") } } ChronoMachines.retry(policy_options) do critical_operation end ``` -------------------------------- ### Test ChronoMachines Retry Callbacks in Ruby Source: https://github.com/seuros/chrono_machines/blob/master/README.md Tests the various callbacks provided by ChronoMachines for retry operations. It verifies that the `on_retry` callback is invoked with the correct attempt number, delay, and exception message. It also tests `on_success` and `on_failure` callbacks to ensure they capture the correct results, attempts, and exceptions. ```ruby require "minitest/autorun" class CallbackTest < Minitest::Test def test_calls_retry_callback_with_correct_context retry_calls = [] call_count = 0 result = ChronoMachines.retry( max_attempts: 3, base_delay: 0.001, # Short delay for tests on_retry: ->(exception:, attempt:, next_delay:) { retry_calls << { attempt: attempt, delay: next_delay, exception_message: exception.message } } ) do call_count += 1 raise "Fail" if call_count < 2 "Success" end assert_equal "Success", result assert_equal 1, retry_calls.length assert_equal 1, retry_calls.first[:attempt] assert retry_calls.first[:delay] > 0 assert_equal "Fail", retry_calls.first[:exception_message] end def test_calls_success_callback success_called = false result_captured = nil attempts_captured = nil result = ChronoMachines.retry( on_success: ->(result:, attempts:) { success_called = true result_captured = result attempts_captured = attempts } ) do "Operation succeeded" end assert success_called assert_equal "Operation succeeded", result_captured assert_equal 1, attempts_captured end def test_calls_failure_callback failure_called = false exception_captured = nil assert_raises(ChronoMachines::MaxRetriesExceededError) do ChronoMachines.retry( max_attempts: 2, on_failure: ->(exception:, attempts:) { failure_called = true exception_captured = exception } ) do raise "Always fails" end end assert failure_called assert_equal "Always fails", exception_captured.message end end ``` -------------------------------- ### Handle Max Retries Exceeded Exception Source: https://github.com/seuros/chrono_machines/blob/master/README.md Demonstrates how to catch `ChronoMachines::MaxRetriesExceededError` to gracefully handle operations that fail after exhausting all retry attempts. It allows access to the original exception and retry context for detailed error reporting and alternative handling. ```ruby begin ChronoMachines.retry(max_attempts: 3) do risky_operation end rescue ChronoMachines::MaxRetriesExceededError => e # Access the original exception and retry context Rails.logger.error "Failed after #{e.attempts} attempts: #{e.original_exception.message}" # The original exception is preserved case e.original_exception when Net::TimeoutError handle_timeout_failure when HTTPError handle_http_failure end end ``` -------------------------------- ### Test Payment Service Retries on Timeout in Ruby Source: https://github.com/seuros/chrono_machines/blob/master/README.md Tests the PaymentService's ability to retry a payment charge on a timeout error using Minitest and Mocha. It mocks Stripe's charge creation to raise a timeout, then succeed, and verifies that the service handles the retry and returns the correct result. It also mocks `robust_sleep` to prevent test delays. ```ruby require "minitest/autorun" require "mocha/minitest" class PaymentServiceTest < Minitest::Test def setup @service = PaymentService.new end def test_retries_payment_on_timeout charge_response = { id: "ch_123", amount: 100 } Stripe::Charge.expects(:create) .raises(Net::TimeoutError).once .then.returns(charge_response) # Mock sleep to avoid test delays ChronoMachines::Executor.any_instance.expects(:robust_sleep).at_least_once result = @service.charge(100) assert_equal charge_response, result end def test_respects_max_attempts Stripe::Charge.expects(:create) .raises(Net::TimeoutError).times(3) assert_raises(ChronoMachines::MaxRetriesExceededError) do @service.charge(100) end end def test_preserves_original_exception original_error = Net::TimeoutError.new("Connection timed out") Stripe::Charge.expects(:create).raises(original_error).times(3) begin @service.charge(100) flunk "Expected MaxRetriesExceededError to be raised" rescue ChronoMachines::MaxRetriesExceededError => e assert_equal 3, e.attempts assert_equal original_error, e.original_exception assert_equal "Connection timed out", e.original_exception.message end end end ``` -------------------------------- ### Ruby: HTTP API Client with Circuit Breaking and Retries Source: https://context7.com/seuros/chrono_machines/llms.txt This Ruby code implements a robust HTTP client for a weather API using ChronoMachines. It configures retry logic for network and server errors, along with callbacks to log retries, increment metrics, and trigger a circuit breaker on failure. When the circuit is open, it serves cached data; on success, it updates the cache. Fallback strategies are employed when retries are exhausted. ```ruby class WeatherApiClient include ChronoMachines::DSL chrono_policy :weather_api, max_attempts: 4, base_delay: 0.3, multiplier: 2, max_delay: 15, retryable_exceptions: [ Net::TimeoutError, Net::HTTPServerError, Net::HTTPServiceUnavailable, Errno::ECONNREFUSED, Errno::ETIMEDOUT ], on_retry: ->(exception:, attempt:, next_delay:) { Rails.logger.info("[WeatherAPI] Retry #{attempt}: #{exception.class}, next attempt in #{next_delay.round(1)}s") Metrics.increment('weather_api.retry', tags: ["attempt:#{attempt}", "exception:#{exception.class}"]) }, on_failure: ->(exception:, attempts:) { Rails.logger.error("[WeatherAPI] Failed after #{attempts} attempts: #{exception.message}") Rails.cache.write('weather_api_circuit_open', true, expires_in: 5.minutes) SlackNotifier.alert("Weather API unavailable after #{attempts} retries") }, on_success: ->(result:, attempts:) { Metrics.histogram('weather_api.success_attempts', attempts) Rails.cache.delete('weather_api_circuit_open') if attempts > 1 } def current_weather(latitude, longitude) # Check circuit breaker if circuit_open? Rails.logger.warn("[WeatherAPI] Circuit open, returning cached data") return cached_weather(latitude, longitude) end with_chrono_policy(:weather_api) do response = HTTP.timeout(connect: 2, read: 5) .get("https://api.weather.com/v1/current", params: { lat: latitude, lon: longitude, apikey: ENV['WEATHER_API_KEY'] }) raise Net::HTTPServerError unless response.status.success? weather_data = JSON.parse(response.body) cache_weather(latitude, longitude, weather_data) weather_data end rescue ChronoMachines::MaxRetriesExceededError => e Rails.logger.error("Weather API exhausted: #{e.original_exception.class}") cached_weather(latitude, longitude) || default_weather_response end def forecast(location, days: 5) with_chrono_policy(:weather_api) do HTTP.timeout(5).get("https://api.weather.com/v1/forecast/#{location}/#{days}") end end private def circuit_open? Rails.cache.read('weather_api_circuit_open') == true end def cache_weather(lat, lon, data) Rails.cache.write("weather:#{lat}:#{lon}", data, expires_in: 30.minutes) end def cached_weather(lat, lon) Rails.cache.read("weather:#{lat}:#{lon}") end def default_weather_response { temperature: nil, conditions: 'Unknown', status: 'Service Unavailable' } end end # Usage client = WeatherApiClient.new weather = client.current_weather(37.7749, -122.4194) puts "Temperature: #{weather['temperature']}°C" ``` -------------------------------- ### Configure Email Delivery Job Retry Policy in Ruby Source: https://github.com/seuros/chrono_machines/blob/master/README.md Defines a background job for email delivery with a custom retry policy using ChronoMachines. The policy specifies maximum attempts, base and maximum delay, retryable exceptions, and a failure callback to move jobs to a dead-letter queue. ```ruby class EmailDeliveryJob include ChronoMachines::DSL chrono_policy :email_delivery, max_attempts: 8, base_delay: 1, multiplier: 1.5, max_delay: 300, # 5 minutes max retryable_exceptions: [Net::SMTPServerBusy, Net::SMTPTemporaryError], on_failure: ->(exception:, attempts:) { # Move to dead letter queue after all retries DeadLetterQueue.push(job_data, reason: exception.message) } def perform(email_data) with_chrono_policy(:email_delivery) do EmailService.deliver(email_data) end end end ``` -------------------------------- ### Background Job Retry with Dead Letter Queue (Ruby) Source: https://context7.com/seuros/chrono_machines/llms.txt Implements reliable background job processing using ChronoMachines for exponential backoff and retries, with a dead letter queue for failed jobs. It configures Sidekiq to disable its own retries to leverage ChronoMachines' policies. Handles specific network exceptions and logs retry/failure events. ```ruby class EmailDeliveryWorker include Sidekiq::Worker include ChronoMachines::DSL chrono_policy :email_smtp, max_attempts: 8, base_delay: 2, multiplier: 1.5, max_delay: 300, # 5 minutes max between retries retryable_exceptions: [ Net::SMTPServerBusy, Net::SMTPTemporaryError, Net::SMTPAuthenticationError, Timeout::Error ], on_retry: ->(exception:, attempt:, next_delay:) { Rails.logger.info( "[EmailWorker] SMTP retry #{attempt}/8: #{exception.class}", next_delay: next_delay, exception_message: exception.message ) StatsD.increment('email.delivery.retry', tags: ["attempt:#{attempt}"]) }, on_failure: ->(exception:, attempts:) { Rails.logger.error("[EmailWorker] Failed after #{attempts} attempts: #{exception.message}") StatsD.increment('email.delivery.failed') } def perform(email_data) email_id = email_data['id'] with_chrono_policy(:email_smtp) do EmailService.deliver( to: email_data['recipient'], subject: email_data['subject'], body: email_data['body'], attachments: email_data['attachments'] ) Email.find(email_id).update!(status: 'delivered', delivered_at: Time.current) Rails.logger.info("[EmailWorker] Successfully delivered email #{email_id}") end rescue ChronoMachines::MaxRetriesExceededError => e # Move to dead letter queue after exhausting retries Email.find(email_id).update!( status: 'failed', error_message: e.original_exception.message, failed_at: Time.current ) DeadLetterQueue.push( worker: self.class.name, email_data: email_data, error: e.original_exception.message, attempts: e.attempts, timestamp: Time.current.iso8601 ) AdminMailer.delivery_failure_notification(email_id, e.attempts).deliver_later raise e # Re-raise for Sidekiq to track end end # Sidekiq configuration # config/initializers/sidekiq.rb Sidekiq.configure_server do |config| # Disable Sidekiq's built-in retry since we use ChronoMachines config.options[:max_retries] = 0 end # Enqueue jobs EmailDeliveryWorker.perform_async({ 'id' => 123, 'recipient' => 'user@example.com', 'subject' => 'Welcome', 'body' => 'Welcome to our service!', 'attachments' => [] }) ``` -------------------------------- ### Ruby ChronoMachines Custom Delay Assertion Source: https://github.com/seuros/chrono_machines/blob/master/README.md Demonstrates how to use the `assert_cm_delay_range` custom assertion provided by `ChronoMachines::TestHelper`. This assertion verifies that a calculated delay falls within a specified minimum and maximum range, including jitter. ```ruby def test_delay_calculation executor = ChronoMachines::Executor.new(base_delay: 0.1, multiplier: 2) delay = executor.send(:calculate_delay, 1) # Assert delay is within expected jitter range assert_cm_delay_range(delay, 0.0, 0.1, "First attempt delay out of range") end ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.