### Bundle Install Circuitbox Source: https://github.com/yammer/circuitbox/blob/main/README.md Execute this command in your terminal after adding the gem to your Gemfile to install it. ```bash $ bundle ``` -------------------------------- ### Circuit Store Initialization Example Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/types.md Example of initializing a CircuitStore, specifically using the MemoryStore implementation. ```ruby Circuitbox::MemoryStore.new ``` -------------------------------- ### Circuit Options Example Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/types.md Example of CircuitOptions used when initializing a CircuitBreaker. Specifies exceptions to catch and the sleep window duration. ```ruby { exceptions: [Error], sleep_window: 60 } ``` -------------------------------- ### Notifier Example Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/types.md Example of a Notifier implementation, showing the use of ActiveSupport for receiving circuit breaker events. ```ruby Circuitbox::Notifier::ActiveSupport ``` -------------------------------- ### Install Circuitbox Gem Directly Source: https://github.com/yammer/circuitbox/blob/main/README.md Install the Circuitbox gem yourself using the RubyGems package manager. ```bash $ gem install circuitbox ``` -------------------------------- ### Basic Faraday Middleware Setup Source: https://github.com/yammer/circuitbox/blob/main/README.md Integrates Circuitbox's Faraday middleware into a Faraday connection. This allows Circuitbox to monitor requests and open circuits based on failures. ```ruby require 'faraday' require 'circuitbox/faraday_middleware' conn = Faraday.new(:url => "http://example.com") do |c| c.use Circuitbox::FaradayMiddleware end response = conn.get "/api" if response.success? # success else # failure or open circuit end ``` -------------------------------- ### Handle OpenCircuitError Example Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/errors.md Illustrates how to handle `OpenCircuitError` when a circuit is open. This example shows making requests until the circuit opens, then attempting another request to trigger the error and handle it by falling back to cached data. ```ruby circuit = Circuitbox.circuit(:payment_api, exceptions: [Timeout]) begin # Make requests that fail, eventually opening the circuit 10.times do circuit.run { call_payment_service } rescue Circuitbox::ServiceFailureError # Keep going until circuit opens end # Circuit is now open circuit.run do call_payment_service end rescue Circuitbox::OpenCircuitError => e puts "Service unavailable: #{e.service}" puts "Error message: #{e.to_s}" # Fallback to cached data or default response use_cached_payment_data end ``` -------------------------------- ### Container Example Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/types.md Illustrates the internal Container type used within the MemoryStore, typically not accessed directly. ```ruby Wrapped in MemoryStore ``` -------------------------------- ### Count Example Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/types.md Example of a Count value, representing request counts such as failure_count or success_count. ```ruby 10 ``` -------------------------------- ### Dynamic Circuit Configuration Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/excon_middleware.md Demonstrates configuring circuit breaker options dynamically, including specific exceptions to track, sleep window, volume threshold, and error threshold. This example shows how the circuit can open after a certain number of failures. ```ruby connection = Excon.new( "http://api.example.com", middlewares: [Circuitbox::ExconMiddleware], circuitbox_circuit_breaker_options: { exceptions: [Excon::Errors::Timeout, Excon::Errors::Socket], sleep_window: 300, volume_threshold: 10, error_threshold: 50 } ) response = connection.request(method: :post, path: "/users", body: JSON.dump(user_data)) # After 10+ requests with 50%+ failures, circuit opens for 5 minutes ``` -------------------------------- ### Configure Circuitbox Monitoring in Rails Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/notifiers.md An initializer example for Rails applications to set up a global subscriber for all circuitbox events. It demonstrates sending metrics to a system like StatsD based on event types. ```ruby # config/initializers/circuitbox.rb if defined?(ActiveSupport::Notifications) ActiveSupport::Notifications.subscribe(/circuitbox/) do |*args| event = ActiveSupport::Notifications::Event.new(*args) circuit = event.payload[:circuit] # Send metrics to monitoring system case event.name when 'open.circuitbox' StatsD.increment("circuitbox.open", tags: ["circuit:#{circuit}"]) when 'close.circuitbox' StatsD.increment("circuitbox.close", tags: ["circuit:#{circuit}"]) when 'success.circuitbox' StatsD.increment("circuitbox.success", tags: ["circuit:#{circuit}"]) when 'failure.circuitbox' StatsD.increment("circuitbox.failure", tags: ["circuit:#{circuit}"]) when 'skipped.circuitbox' StatsD.increment("circuitbox.skipped", tags: ["circuit:#{circuit}"]) when 'warning.circuitbox' message = event.payload[:message] Rails.logger.warn("Circuitbox warning [#{circuit}]: #{message}") when 'run.circuitbox' duration = event.duration StatsD.histogram( "circuitbox.duration", duration, tags: ["circuit:#{circuit}"] ) end end end ``` -------------------------------- ### Define a Circuit with Default Settings Source: https://github.com/yammer/circuitbox/blob/main/README.md This snippet shows how to define a circuit named ':your_service' that wraps an HTTP GET request. It specifies that `Net::ReadTimeout` exceptions should be caught and handled by the circuit. ```ruby Circuitbox.circuit(:your_service, exceptions: [Net::ReadTimeout]) do Net::HTTP.get URI('http://example.com/api/messages') end ``` -------------------------------- ### Thread-Safe Increment Example Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/memory_store.md Demonstrates safe concurrent access to the Memory Store from multiple threads. This example shows how to increment a counter concurrently without race conditions. ```ruby store = Circuitbox::MemoryStore.new # Safe to use from multiple threads threads = Array.new(10) do |i| Thread.new { store.increment("counter") } end threads.each(&:join) store.load("counter") # => 10 ``` -------------------------------- ### Custom Notifier Implementation Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/notifiers.md Implement a custom notifier by defining `notify`, `notify_warning`, and `notify_run` methods. This example shows how to integrate with a metrics system. ```ruby class MetricsNotifier def notify(circuit_name, event) # Send event to metrics system Metrics.increment("circuitbox.#{event}", tags: { circuit: circuit_name }) end def notify_warning(circuit_name, message) # Handle warnings Logger.warn("Circuit #{circuit_name}: #{message}") end def notify_run(circuit_name, &block) # Instrument block execution start = Time.now result = yield duration = Time.now - start Metrics.histogram("circuitbox.duration", duration, tags: { circuit: circuit_name }) result end end ``` ```ruby # Use the custom notifier Circuitbox.configure do |config| config.default_notifier = MetricsNotifier.new end # Or per-circuit circuit = Circuitbox.circuit(:api, { exceptions: [StandardError], notifier: MetricsNotifier.new }) ``` -------------------------------- ### Subscribe to Circuit Execution Timing Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/notifiers.md Example of subscribing specifically to 'run.circuitbox' events to monitor the duration of circuit-executed blocks. This is useful for performance analysis and identifying slow operations. ```ruby require 'active_support/notifications' # Subscribe to timing for performance monitoring ActiveSupport::Notifications.subscribe('run.circuitbox') do |*args| event = ActiveSupport::Notifications::Event.new(*args) circuit = event.payload[:circuit] duration = event.duration # milliseconds if duration > 1000 Rails.logger.warn("Slow circuit execution: #{circuit} took #{duration}ms") end end ``` -------------------------------- ### Subscribe to Specific Circuitbox Events Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/notifiers.md Example of subscribing to a specific circuit event like 'open.circuitbox' using ActiveSupport::Notifications. This allows custom handling of circuit events. ```ruby require 'active_support/notifications' # Subscribe to circuit open events ActiveSupport::Notifications.subscribe('open.circuitbox') do |*args| event = ActiveSupport::Notifications::Event.new(*args) circuit_name = event.payload[:circuit] Rails.logger.warn("Circuit open: #{circuit_name}") end ``` -------------------------------- ### Run a Service Client with Circuit Breaker Source: https://github.com/yammer/circuitbox/blob/main/README.md This example demonstrates how to use Circuitbox within a service client class. It defines a circuit for 'yammer' and specifies `Zephyr::FailedRequest` as an exception to monitor. The `http_get` method uses the circuit's `run` method with `exception: false`, meaning it will return nil on failure instead of raising an exception. ```ruby class ExampleServiceClient def circuit Circuitbox.circuit(:yammer, exceptions: [Zephyr::FailedRequest]) end def http_get circuit.run(exception: false) do Zephyr.new("http://example.com").get(200, 1000, "/api/messages") end end end ``` -------------------------------- ### Per-Request Circuit Configuration Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/faraday_middleware.md This example shows how to configure separate circuits for different endpoints or hosts within the same Faraday connection. Each unique identifier (host or custom string) will have its own independent circuit breaker state. ```APIDOC ## Per-Request Circuit Configuration This example shows how to configure separate circuits for different endpoints or hosts within the same Faraday connection. Each unique identifier (host or custom string) will have its own independent circuit breaker state. ### Code Example ```ruby conn = Faraday.new do |c| c.use Circuitbox::FaradayMiddleware, circuit_breaker_options: { exceptions: [Faraday::ConnectionFailed, Faraday::TimeoutError], sleep_window: 120, error_threshold: 60 } end # The middleware creates separate circuits per unique identifier # Each endpoint is protected independently users_response = conn.get("http://api.example.com/users") posts_response = conn.get("http://api.example.com/posts") # Two separate circuits: api.example.com/users and api.example.com/posts ``` ``` -------------------------------- ### Catch and Handle Circuitbox Errors Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/errors.md Provides an example of catching both `OpenCircuitError` and `ServiceFailureError`. It demonstrates logging the error and returning a degraded response for an open circuit, or re-raising for a service failure. ```ruby circuit = Circuitbox.circuit(:external_service, exceptions: [StandardError]) begin result = circuit.run { external_call } rescue Circuitbox::OpenCircuitError => e # Circuit is open, service is down # e.service => "external_service" logger.warn("Circuit open for service: #{e.service}") # Return degraded response { status: 'degraded', reason: 'service_unavailable' } rescue Circuitbox::ServiceFailureError => e # Service failed, but circuit still has attempts logger.error("Service error: #{e.original.class} - #{e.original.message}") raise # Re-raise or handle gracefully end ``` -------------------------------- ### Circuitbox Circuit Initialization with Options Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/types.md Demonstrates how to create a Circuitbox circuit using a configuration options hash. Ensure the `exceptions` field includes at least one exception class. ```ruby options = { exceptions: [Timeout::Error, StandardError], sleep_window: 120, error_threshold: 60 } circuit = Circuitbox.circuit(:api, options) ``` -------------------------------- ### Error Rate Example Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/types.md Example of an ErrorRate value returned by circuit.error_rate, representing the percentage of failures. ```ruby 42.5 ``` -------------------------------- ### ExconMiddleware.new Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/SUMMARY.txt Initializes a new Excon middleware instance for integrating Circuitbox with Excon HTTP clients. Accepts complete options. ```APIDOC ## ExconMiddleware.new ### Description Initializes a new Excon middleware instance for integrating Circuitbox with Excon HTTP clients. Accepts complete options. ### Method NEW ### Endpoint N/A (Ruby method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```ruby Excon.defaults[:middleware].push(ExconMiddleware) ``` ### Response #### Success Response (200) Returns a new ExconMiddleware instance. #### Response Example ```ruby # Returns an ExconMiddleware object ``` ``` -------------------------------- ### Manual Time Class Selection for Circuit Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/types.md Demonstrates how to manually select a time class (e.g., Circuitbox::TimeHelper::Real) when initializing a circuit. ```ruby # Automatically selected based on circuit_store type # MemoryStore uses Monotonic (not affected by system clock adjustments) # Moneta stores use Real (wall-clock time) # Manual selection circuit = Circuitbox.circuit(:api, { exceptions: [StandardError], time_class: Circuitbox::TimeHelper::Real }) ``` -------------------------------- ### Initialize Faraday Middleware Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/faraday_middleware.md Demonstrates basic usage with default settings. Requires 'faraday' and 'circuitbox/faraday_middleware' to be required. ```ruby require 'faraday' require 'circuitbox/faraday_middleware' # Basic usage with defaults conn = Faraday.new(url: "http://api.example.com") do |c| c.use Circuitbox::FaradayMiddleware end response = conn.get("/users") ``` -------------------------------- ### Watched Exception Example Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/types.md Example of WatchedException configuration, which is an array of StandardError subclasses to be monitored by the circuit breaker. ```ruby [Timeout::Error, StandardError] ``` -------------------------------- ### FaradayMiddleware.new Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/SUMMARY.txt Initializes a new Faraday middleware instance for integrating Circuitbox with Faraday HTTP clients. Accepts complete options. ```APIDOC ## FaradayMiddleware.new ### Description Initializes a new Faraday middleware instance for integrating Circuitbox with Faraday HTTP clients. Accepts complete options. ### Method NEW ### Endpoint N/A (Ruby method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```ruby faraday_app.use FaradayMiddleware, circuit_breaker: my_circuit_breaker ``` ### Response #### Success Response (200) Returns a new FaradayMiddleware instance. #### Response Example ```ruby # Returns a FaradayMiddleware object ``` ``` -------------------------------- ### Basic Usage with Timeouts Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/excon_middleware.md Shows basic integration of ExconMiddleware with Excon, including setting standard timeouts. The middleware automatically tracks timeout errors. ```ruby require 'excon' require 'circuitbox/excon_middleware' connection = Excon.new( "http://api.example.com", middlewares: [Circuitbox::ExconMiddleware], read_timeout: 30, write_timeout: 30, connect_timeout: 10 ) # Circuit automatically tracks timeout errors response = connection.request(method: :get, path: "/data") ``` -------------------------------- ### MemoryStore.new Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/SUMMARY.txt Initializes a new MemoryStore instance, which stores circuit breaker states in memory. Supports a `compaction_frequency` option. ```APIDOC ## MemoryStore.new ### Description Initializes a new MemoryStore instance, which stores circuit breaker states in memory. Supports a `compaction_frequency` option. ### Method NEW ### Endpoint N/A (Ruby method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```ruby store = MemoryStore.new(compaction_frequency: 1000) ``` ### Response #### Success Response (200) Returns a new MemoryStore instance. #### Response Example ```ruby # Returns a MemoryStore object ``` ``` -------------------------------- ### Notifier::ActiveSupport#notify_run Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/SUMMARY.txt The ActiveSupport notifier's `notify_run` method is used to signal the start or completion of a process or run. ```APIDOC ## Notifier::ActiveSupport#notify_run ### Description This method is part of the ActiveSupport notifier and is used to signal information about a run, such as its initiation or conclusion. It likely integrates with Active Support's eventing or logging. ### Method `notify_run` ### Parameters This method accepts parameters relevant to the run's context and status. Specific parameter details are documented in the full signature. ### Return Type Details on the return type are available in the full signature documentation. ### Source `Notifiers: ✓ Notifier::ActiveSupport#notify_run` ``` -------------------------------- ### Get Default Circuit Store Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/circuitbox.md Retrieves the currently configured default circuit store. This store is used by circuits that do not specify their own. ```ruby store = Circuitbox.default_circuit_store # => # ``` -------------------------------- ### Initialize Circuit Breaker with Basic Options Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/circuit_breaker.md Initialize a circuit breaker for a service, specifying the exceptions to monitor. This is the minimum required configuration. ```ruby circuit = Circuitbox::CircuitBreaker.new(:payment_service, { exceptions: [Timeout::Error, Net::SocketError] }) ``` -------------------------------- ### Get Default Notifier Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/circuitbox.md Retrieves the currently configured default notifier. This notifier is used by circuits that do not specify their own. It automatically detects ActiveSupport availability. ```ruby notifier = Circuitbox.default_notifier # => # ``` -------------------------------- ### Using Circuitbox Faraday Middleware Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/faraday_middleware.md Demonstrates how to integrate the Circuitbox Faraday middleware into a Faraday connection. Shows how to check for an open circuit (status 503) and access original response or exception details for fallback logic. ```ruby require 'faraday' require 'circuitbox/faraday_middleware' conn = Faraday.new(url: "http://api.example.com") do |c| c.use Circuitbox::FaradayMiddleware end # When circuit is open: response = conn.get("/data") if response.status == 503 # Circuit is open original = response.original_response error = response.original_exception if original && original.status >= 500 puts "Service error: #{original.status}" elsif error puts "Connection error: #{error.message}" end end ``` -------------------------------- ### Using Circuitbox Excon Middleware Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/excon_middleware.md Demonstrates how to integrate Circuitbox's Excon middleware into an Excon connection. This snippet shows how to make a request and check for a 503 fallback response when the circuit is open. ```ruby require 'excon' require 'circuitbox/excon_middleware' connection = Excon.new("http://api.example.com", middlewares: [Circuitbox::ExconMiddleware]) # When circuit is open: response = connection.request(method: :get, path: "/data") if response.status == 503 # Circuit is open puts "Fallback response used" end ``` -------------------------------- ### Get Circuit Breaker Success Count Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/circuit_breaker.md Retrieves the number of successes recorded within the current time window. This count resets when the time window expires. ```ruby circuit = Circuitbox.circuit(:service, exceptions: [StandardError]) puts "Successes: #{circuit.success_count}" ``` -------------------------------- ### Use Dynamic Configuration in Production Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/configuration.md Employ dynamic configuration using Procs for parameters like `sleep_window` and `error_threshold` in production. This allows runtime adjustments without restarting the application. ```ruby circuit = Circuitbox.circuit(:api, { exceptions: [StandardError], sleep_window: Proc.new { Config.get(:sleep_window, 90) }, error_threshold: Proc.new { Config.get(:error_threshold, 50) } }) ``` -------------------------------- ### Get Circuit Breaker Failure Count Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/circuit_breaker.md Retrieves the number of failures recorded within the current time window. This count resets when the time window expires. ```ruby circuit = Circuitbox.circuit(:service, exceptions: [StandardError]) puts "Failures: #{circuit.failure_count}" ``` -------------------------------- ### Monitoring Circuitbox Events Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/README.md Integrate Circuitbox with ActiveSupport::Notifications to monitor circuit breaker events. This example subscribes to the 'open.circuitbox' notification to increment a metric when a circuit opens. ```ruby Circuitbox.configure do |config| config.default_notifier = Circuitbox::Notifier::ActiveSupport.new end ActiveSupport::Notifications.subscribe('open.circuitbox') do |*args| event = ActiveSupport::Notifications::Event.new(*args) Metrics.increment("circuit.open", circuit: event.payload[:circuit]) end ``` -------------------------------- ### Initialize Circuit Breaker with All Options Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/circuit_breaker.md Configure a circuit breaker with all available options for fine-grained control over its behavior, including time windows, thresholds, and custom stores/notifiers. ```ruby circuit = Circuitbox::CircuitBreaker.new(:api, { exceptions: [StandardError], time_window: 120, sleep_window: 300, volume_threshold: 10, error_threshold: 60, circuit_store: custom_store, notifier: custom_notifier }) ``` -------------------------------- ### Per-Circuit Configuration with Proc Options Source: https://github.com/yammer/circuitbox/blob/main/README.md Configure a circuit using a Proc for option values, allowing dynamic configuration that evaluates each time the circuit breaker is used. This is useful for settings that may change without restarting the process. ```ruby Circuitbox.circuit(:yammer, { sleep_window: Proc.new { Configuration.get(:sleep_window) }, exceptions: [Net::ReadTimeout] }) ``` -------------------------------- ### Handling ServiceFailureError with Specific Exceptions Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/errors.md Example demonstrating how to catch `ServiceFailureError` and inspect the original exception to implement specific retry logic or error handling based on the exception type. ```ruby circuit = Circuitbox.circuit(:database, exceptions: [Connection::Timeout, ActiveRecord::ConnectionNotEstablished]) begin circuit.run do Database.query("SELECT * FROM users") end rescue Circuitbox::ServiceFailureError => e puts "Service: #{e.service}" puts "Original error: #{e.original.class} - #{e.original.message}" puts "Backtrace:" puts e.backtrace.first(5).join("\n") # React to specific original exception type case e.original when Connection::Timeout puts "Timeout occurred, trying again in 5 seconds" sleep 5 retry when ActiveRecord::ConnectionNotEstablished puts "Database connection lost" end end ``` -------------------------------- ### Circuitbox File Organization Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/README.md Overview of the Circuitbox gem's directory structure. ```bash circuitbox/ ├── lib/ │ └── circuitbox/ │ ├── circuitbox.rb # Main entry point │ ├── circuit_breaker.rb # Core breaker logic │ ├── configuration.rb # Global configuration │ ├── memory_store.rb # In-memory storage │ ├── memory_store/ │ │ └── container.rb # Expiring container │ ├── faraday_middleware.rb # Faraday integration │ ├── excon_middleware.rb # Excon integration │ ├── notifier/ │ │ ├── active_support.rb # ActiveSupport notifier │ │ └── null.rb # No-op notifier │ ├── time_helper/ │ │ ├── monotonic.rb # Monotonic time │ │ └── real.rb # Wall-clock time │ ├── errors/ │ │ ├── error.rb # Base error │ │ ├── open_circuit_error.rb # Open circuit │ │ └── service_failure_error.rb # Service failure │ └── version.rb # Version constant ├── README.md # Project overview ├── docs/ │ ├── circuit_notifications.md # Notifications guide │ └── 2.0-upgrade.md # Upgrade guide └── test/ # Test suite ``` -------------------------------- ### Configure Error Threshold for Circuitbox Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/configuration.md Specifies the percentage of failures that will cause the circuit to open once the volume threshold is met. This example shows a 50% failure rate triggering the open state. ```ruby circuit = Circuitbox.circuit(:api, { volume_threshold: 5, error_threshold: 50 # 50% failure rate opens }) ``` -------------------------------- ### Integrate Circuit Breaker with Excon Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/README.md Apply Circuitbox Excon middleware to protect Excon HTTP requests. Configure the identifier and circuit breaker options directly within the connection setup. ```ruby require 'excon' require 'circuitbox/excon_middleware' connection = Excon.new("http://api.example.com", middlewares: [Circuitbox::ExconMiddleware], circuitbox_identifier: "api", circuitbox_circuit_breaker_options: { exceptions: [Excon::Errors::Timeout] } ) response = connection.request(method: :get, path: "/data") ``` -------------------------------- ### Create a Circuit with Specific Options Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/configuration.md Configure individual circuits with specific behavior by passing options during creation. The `exceptions` option is required to track failure classes. ```ruby circuit = Circuitbox.circuit(:service_name, options) ``` ```ruby circuit = Circuitbox::CircuitBreaker.new(:service_name, options) ``` -------------------------------- ### Run a Service Client and Throw Exceptions Source: https://github.com/yammer/circuitbox/blob/main/README.md This snippet shows a variation of the `http_get` method where the `circuit.run` method is called without the `exception: false` option. This means that if the circuit is open or the underlying service fails, an exception will be thrown. ```ruby def http_get circuit.run do Zephyr.new("http://example.com").get(200, 1000, "/api/messages") end end ``` -------------------------------- ### Evaluate Proc Options in Circuitbox Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/configuration.md Demonstrates how Proc objects are stored and evaluated for circuit options. Procs are called during initialization, state transitions, and error rate calculations. ```ruby @circuit_options[name] = Proc.new { ... } ``` ```ruby def option_value(name) value = @circuit_options[name] value.is_a?(Proc) ? value.call : value end ``` -------------------------------- ### Automatic Failure Tracking Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/faraday_middleware.md This example demonstrates how to automatically track request failures using the Circuitbox Faraday middleware. After a specified number of requests with a high error rate, the circuit will open for a defined period. ```APIDOC ## Automatic Failure Tracking This example demonstrates how to automatically track request failures using the Circuitbox Faraday middleware. After a specified number of requests with a high error rate, the circuit will open for a defined period. ### Code Example ```ruby require 'faraday' require 'circuitbox/faraday_middleware' conn = Faraday.new(url: "http://slow-api.example.com") do |c| c.use Circuitbox::FaradayMiddleware, circuit_breaker_options: { exceptions: [Faraday::TimeoutError], sleep_window: 60, volume_threshold: 3, error_threshold: 50 } end # After 3+ requests with 50%+ timeouts, circuit opens for 60 seconds begin 5.times do response = conn.get("/api") puts "Status: #{response.status}" end rescue => e puts "Error: #{e.message}" end ``` ``` -------------------------------- ### Real Time Helper Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/types.md Provides wall-clock time in seconds, equivalent to Time.now.to_i. This is typically used with Moneta stores. ```ruby module Circuitbox::TimeHelper::Real def self.current_second -> Integer # Returns wall-clock time in seconds (Time.now.to_i) end end ``` -------------------------------- ### Configure Circuit with time_window and sleep_window Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/configuration.md Demonstrates a good practice where sleep_window is greater than or equal to time_window for proper statistics clearing before retries. ```ruby # GOOD: sleep_window >= time_window circuit = Circuitbox.circuit(:api, { time_window: 60, sleep_window: 90 # Sleep longer than measure window }) # Stats from bucket that opened circuit are cleared before retry ``` -------------------------------- ### Circuitbox Configuration for Error Handling Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/errors.md Configure circuit breaker parameters to control error thresholds, sleep windows, and time windows. This setup defines when an OpenCircuitError is triggered and how long the circuit remains open. ```ruby circuit = Circuitbox.circuit(:api, { exceptions: [Timeout::Error], sleep_window: 300, # Circuit stays open 5 minutes time_window: 60, # Measure errors over 1 minute volume_threshold: 10, # Need 10 requests before calculating rate error_threshold: 50 # Open at 50% error rate }) # In this configuration: # - After 10+ requests in 60 seconds with 50%+ failing: OpenCircuitError # - Circuit stays open 300 seconds before half-open attempt # - Individual failures: ServiceFailureError ``` -------------------------------- ### Circuitbox::ExconMiddleware.new Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/excon_middleware.md Initializes the Excon middleware with circuit breaker configuration. This constructor allows for customization of failure detection, circuit identification, and circuit breaker behavior. ```APIDOC ## Circuitbox::ExconMiddleware.new ### Description Initializes the Excon middleware with circuit breaker configuration. ### Signature ```ruby Circuitbox::ExconMiddleware.new(stack, opts = {}) ``` ### Parameters #### Path Parameters - **stack** (Excon::Connection::RequestStack) - Required - The Excon middleware stack. - **opts** (Hash) - Optional - Configuration options for the middleware. #### Options (Hash) - **open_circuit** (Proc) - Optional - Determines if a response counts as a failure. Default: `lambda { |response| response[:status] >= 400 }` - **identifier** (String or Proc) - Optional - Circuit identifier. Default: `lambda { |env| env[:host] }` - **circuit_breaker_options** (Hash) - Optional - Options passed to CircuitBreaker#new. Default: `{}` - **exceptions** (Array) - Optional - Exception classes to track as failures. Default: `DEFAULT_EXCEPTIONS` - **default_value** (Object or Proc) - Optional - Value returned when circuit is open. Default: `lambda { |response, error| NullResponse.new(response, error) }` - **circuitbox** (Class) - Optional - Instance or class to use for circuit creation. Default: `Circuitbox` ### Example ```ruby require 'excon' require 'circuitbox/excon_middleware' # Basic usage with defaults stack = Excon::Connection::RequestStack.new( [], Circuitbox::ExconMiddleware ) connection = Excon.new("http://api.example.com", middlewares: stack) response = connection.request(method: :get, path: "/users") # With custom failure detection (only 5xx) connection = Excon.new( "http://api.example.com", middlewares: [Circuitbox::ExconMiddleware], circuitbox_open_circuit: lambda { |response| response[:status] >= 500 } ) # With custom identifier connection = Excon.new( "http://api.example.com", middlewares: [Circuitbox::ExconMiddleware], circuitbox_identifier: "payment_service" ) # With custom circuit breaker options connection = Excon.new( "http://api.example.com", middlewares: [Circuitbox::ExconMiddleware], circuitbox_circuit_breaker_options: { sleep_window: 300, error_threshold: 60, volume_threshold: 10 } ) ``` ``` -------------------------------- ### Get Circuit Breaker Instance Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/circuitbox.md Retrieves a cached Circuitbox::CircuitBreaker instance for a given service name and options. Use this when you need to manage the circuit breaker's lifecycle or reuse it across multiple operations. ```ruby circuit = Circuitbox.circuit(:user_service, exceptions: [Timeout::Error]) # Use the circuit to execute protected code result = circuit.run { make_api_call } ``` -------------------------------- ### Rescue All Circuitbox Exceptions Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/errors.md Demonstrates how to rescue all exceptions that inherit from `Circuitbox::Error` to handle any circuit-related issues gracefully. ```ruby begin circuit = Circuitbox.circuit(:api, exceptions: [Timeout]) circuit.run { fetch_data } rescue Circuitbox::Error => e puts "Circuit error occurred: #{e.class}" end ``` -------------------------------- ### Per-Request Circuit Configuration in Faraday Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/faraday_middleware.md The middleware creates separate circuits per unique identifier, typically per host. This example shows how each endpoint is protected independently, with distinct circuits for different API paths on the same host. ```ruby conn = Faraday.new do |c| c.use Circuitbox::FaradayMiddleware, circuit_breaker_options: { exceptions: [Faraday::ConnectionFailed, Faraday::TimeoutError], sleep_window: 120, error_threshold: 60 } end # The middleware creates separate circuits per unique identifier # Each endpoint is protected independently users_response = conn.get("http://api.example.com/users") posts_response = conn.get("http://api.example.com/posts") # Two separate circuits: api.example.com/users and api.example.com/posts ``` -------------------------------- ### Circuitbox.circuit (Get Circuit Breaker) Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/circuitbox.md Creates or retrieves a circuit breaker for a given service name. Returns a cached Circuitbox::CircuitBreaker instance for the given service. Multiple calls with the same `service_name` and options return the same circuit instance. ```APIDOC ## Circuitbox.circuit (Get Circuit Breaker) ### Description Creates or retrieves a circuit breaker for a given service name. Returns a cached `Circuitbox::CircuitBreaker` instance for the given service. Multiple calls with the same `service_name` and options return the same circuit instance. ### Method GET (or equivalent for SDK) ### Endpoint Circuitbox.circuit(service_name, options = {}) ### Parameters #### Path Parameters - **service_name** (String, Symbol) - Required - Name of the service for which to create a circuit breaker. Symbols and strings with the same name create separate circuits. - **options** (Hash) - Optional - Default: `{}` - Configuration options for the circuit breaker (see CircuitBreaker#initialize). ### Return Value - **Type**: `Circuitbox::CircuitBreaker` - **Description**: A circuit breaker instance configured with the provided options. ### Example ```ruby # Get a circuit breaker for an HTTP service circuit = Circuitbox.circuit(:user_service, exceptions: [Timeout::Error]) # Use the circuit to execute protected code result = circuit.run { make_api_call } ``` ``` -------------------------------- ### Circuitbox Main Class Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/README.md Use the `Circuitbox` class to create or retrieve circuits and configure global defaults. Access default stores and notifiers through this class. ```ruby class Circuitbox extend Configuration # Create or retrieve a circuit def self.circuit(service_name, options = {}, &block) # Configure global defaults def self.configure { |config| ... } # Access global defaults def self.default_circuit_store def self.default_notifier end ``` -------------------------------- ### Initialize Circuitbox::MemoryStore Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/memory_store.md Initializes a new in-memory store instance. You can specify the compaction frequency in seconds. This store can also be configured as the default for Circuitbox. ```ruby # Default store store = Circuitbox::MemoryStore.new # Custom compaction frequency (compact every 30 seconds) store = Circuitbox::MemoryStore.new(compaction_frequency: 30) # Use as global default Circuitbox.configure do |config| config.default_circuit_store = Circuitbox::MemoryStore.new(compaction_frequency: 120) end ``` -------------------------------- ### Circuitbox::MemoryStore.new Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/memory_store.md Initializes a new in-memory store instance. You can optionally specify the compaction frequency to control how often expired entries are removed. ```APIDOC ## Circuitbox::MemoryStore.new ### Description Initializes a new in-memory store instance. Automatically compacts expired entries. ### Signature ```ruby Circuitbox::MemoryStore.new(compaction_frequency: 60) -> Circuitbox::MemoryStore ``` ### Parameters #### Path Parameters - **compaction_frequency** (Integer) - Optional - Interval in seconds between automatic compaction runs that remove expired entries. Defaults to 60 seconds. ``` -------------------------------- ### Dynamic Circuit Configuration Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/configuration.md Configure circuit thresholds dynamically using a configuration class that fetches values from the environment. This allows for adjustments without restarting the application. ```ruby # Configuration stored in environment or database class Config def self.get(key, default = nil) # Fetch from environment, database, or config file ENV["CIRCUIT_#{key.upcase}"]&.to_i || default end end circuit = Circuitbox.circuit(:api, { exceptions: [StandardError], sleep_window: Proc.new { Config.get(:sleep_window, 90) }, error_threshold: Proc.new { Config.get(:error_threshold, 50) }, volume_threshold: Proc.new { Config.get(:volume_threshold, 5) } }) ``` -------------------------------- ### Configure Circuit with Static Options Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/configuration.md Use static values for circuit parameters when they do not need to change dynamically. This is suitable for fixed configurations. ```ruby circuit = Circuitbox.circuit(:api, { sleep_window: 120, # Always 120 seconds error_threshold: 60 # Always 60% }) ``` -------------------------------- ### ExconMiddleware Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/README.md The `ExconMiddleware` integrates Circuitbox with the Excon HTTP client, automatically tracking failures. ```APIDOC ## Class: Circuitbox::ExconMiddleware ### Description Excon integration for automatic failure tracking. This middleware should be added to your Excon connection. ### Methods #### `initialize(stack, opts = {})` Initializes the middleware with the connection stack and optional configuration. #### `error_call(datum)` Handles errors that occur during the Excon request lifecycle. #### `request_call(datum)` Processes the request before it is sent. #### `response_call(datum)` Processes the response after it is received, updating circuit status if necessary. ``` -------------------------------- ### ExconMiddleware Request Handling Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/excon_middleware.md Handles the request phase before network transmission. It executes the request through the circuit, allowing the circuit to prevent execution if open. Returns nil if the circuit is open, otherwise proceeds to response_call. ```ruby middleware.request_call(datum) ``` -------------------------------- ### Dynamic Circuit Configuration with Procs Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/types.md Configure circuit options dynamically using Procs. The Proc will be called each time the value is needed, ensuring up-to-date configuration without restarting. Ensure the Proc returns the correct type for each option. ```ruby circuit = Circuitbox.circuit(:api, { exceptions: [StandardError], sleep_window: Proc.new { Configuration.get(:sleep_window) }, error_threshold: Proc.new { Configuration.get(:error_threshold) } }) ``` -------------------------------- ### Time Window and Bucket Alignment Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/README.md Explains how CircuitBox uses time-aligned buckets to track statistics within a defined time window. Buckets are aligned to specific time intervals and expire as new ones are created. ```text time_window = 60 seconds Bucket times: 0, 60, 120, 180, ... At t=120, bucket starting at t=60 is used for calculations At t=180, new bucket starts, t=60 bucket expires ``` -------------------------------- ### ExconMiddleware Integration Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/README.md Integrate `Circuitbox::ExconMiddleware` with Excon for automatic failure tracking. Handles request and response calls, and errors. ```ruby class ExconMiddleware < Excon::Middleware::Base def initialize(stack, opts = {}) def error_call(datum) def request_call(datum) def response_call(datum) class NullResponse < Excon::Response def initialize(response, exception) end end ``` -------------------------------- ### Initialize Excon Middleware Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/excon_middleware.md Initializes the Excon middleware with default circuit breaker settings. The stack parameter is required, and options can be provided to customize behavior. ```ruby require 'excon' require 'circuitbox/excon_middleware' # Basic usage with defaults stack = Excon::Connection::RequestStack.new( [], Circuitbox::ExconMiddleware ) connection = Excon.new("http://api.example.com", middlewares: stack) response = connection.request(method: :get, path: "/users") ``` -------------------------------- ### Circuitbox.configure Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/SUMMARY.txt Configures global settings for Circuitbox, such as default circuit store and notifier. ```APIDOC ## Circuitbox.configure ### Description Configures global settings for Circuitbox, such as default circuit store and notifier. ### Method SET ### Endpoint N/A (Ruby method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```ruby Circuitbox.configure do |config| config.default_circuit_store = MyCustomStore.new config.default_notifier = MyCustomNotifier.new end ``` ### Response #### Success Response (200) No explicit return value, modifies global configuration. #### Response Example None ``` -------------------------------- ### Configure MemoryStore with compaction frequency Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/memory_store.md Initializes a MemoryStore with a specific compaction frequency. This controls how often expired entries are automatically removed. Compaction is triggered transparently during normal operations. ```ruby # Store configured to compact every 60 seconds store = Circuitbox::MemoryStore.new(compaction_frequency: 60) # Over time, expired keys accumulate until the next compaction trigger store.store("old", "value", expires: 1) # ... after expiration ... # ... 60+ seconds later, during next access ... # Compaction runs, removing the expired "old" key store.load("other") ``` -------------------------------- ### Configure Default Null Notifier Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/notifiers.md Shows how to configure Circuitbox to use the Null notifier as the default, minimizing overhead by discarding all events. ```ruby Circuitbox.configure do |config| config.default_notifier = Circuitbox::Notifier::Null.new end # All events are discarded, minimal overhead circuit = Circuitbox.circuit(:api, exceptions: [StandardError]) ``` -------------------------------- ### Difference between run(exception: true) and run(exception: false) Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/errors.md Illustrates the behavior difference when `run` is called with `exception: true` (default, wraps exceptions) versus `exception: false` (returns nil on exception). ```ruby circuit = Circuitbox.circuit(:service, exceptions: [StandardError]) # With exception: true (default) begin circuit.run do raise StandardError.new("failed") end rescue Circuitbox::ServiceFailureError => e # Wraps the original error puts e.original.message # => "failed" end # With exception: false result = circuit.run(exception: false) do raise StandardError.new("failed") end # => nil (no exception, just returns nil) ``` -------------------------------- ### Per-Circuit Configuration with Specific Options Source: https://github.com/yammer/circuitbox/blob/main/README.md Configure a specific circuit with detailed options including exceptions, sleep window, time window, volume threshold, circuit store, error threshold, and notifier. This allows for fine-grained control over individual circuit breakers. ```ruby class ExampleServiceClient def circuit Circuitbox.circuit(:your_service, { # exceptions circuitbox tracks for counting failures (required) exceptions: [YourCustomException], # seconds the circuit stays open once it has passed the error threshold sleep_window: 300, # length of interval (in seconds) over which it calculates the error rate time_window: 60, # number of requests within `time_window` seconds before it calculates error rates (checked on failures) volume_threshold: 10, # the store you want to use to save the circuit state so it can be # tracked, this needs to be Moneta compatible, and support increment # this overrides what is set in the global configuration circuit_store: Circuitbox::MemoryStore.new, # exceeding this rate will open the circuit (checked on failures) error_threshold: 50, # Customized notifier # this overrides what is set in the global configuration notifier: Notifier.new }) end end ``` -------------------------------- ### Subscribe to All Circuitbox Events with Regex Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/notifiers.md Demonstrates subscribing to all circuitbox-related events using a regular expression. This enables a centralized handler for various circuit states and warnings. ```ruby require 'active_support/notifications' # Subscribe to all circuit events ActiveSupport::Notifications.subscribe(/circuitbox/) do |*args| event = ActiveSupport::Notifications::Event.new(*args) case event.name when 'open.circuitbox' # Handle open when 'close.circuitbox' # Handle close when 'success.circuitbox' # Handle success when 'failure.circuitbox' # Handle failure when 'skipped.circuitbox' # Handle skipped when 'warning.circuitbox' message = event.payload[:message] # Handle warning when 'run.circuitbox' duration_ms = event.duration # Log duration end end ``` -------------------------------- ### ExconMiddleware Response Handling Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/excon_middleware.md Handles the response phase after receiving data from the network. It evaluates the response against the open_circuit predicate, raises RequestFailed if the failure condition is met, increments the failure counter, returns a fallback response if the circuit is open, and otherwise proceeds down the middleware stack. ```ruby middleware.response_call(datum) ``` -------------------------------- ### Subscribe to Circuit Open/Close Events Source: https://github.com/yammer/circuitbox/blob/main/docs/circuit_notifications.md Subscribe to 'open.circuitbox' and 'close.circuitbox' events to log circuit state changes. Requires ActiveSupport::Notifications. ```ruby ActiveSupport::Notifications.subscribe('open.circuitbox') do |*args| event = ActiveSupport::Notifications::Event.new(*args) circuit_name = event.payload[:circuit] Rails.logger.warn("Open circuit for: #{circuit_name}") end ActiveSupport::Notifications.subscribe('close.circuitbox') do |*args| event = ActiveSupport::Notifications::Event.new(*args) circuit_name = event.payload[:circuit] Rails.logger.info("Close circuit for: #{circuit_name}") end ``` -------------------------------- ### MemoryStore#load Source: https://github.com/yammer/circuitbox/blob/main/_autodocs/api-reference/memory_store.md Retrieves the value associated with a key, returning nil if not found or expired. The `_opts` parameter is unused but present for API compatibility. ```APIDOC ## Method: `MemoryStore#load` Retrieves the value associated with a key, returning nil if not found or expired. ### Signature ```ruby store.load(key, _opts = {}) ``` ### Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | key | String | Yes | — | The key to retrieve. | | _opts | Hash | No | `{}` | Unused, present for API compatibility. | ### Return Value - **Type**: Object or nil - **Description**: The stored value, or nil if the key does not exist or has expired. ### Example ```ruby store = Circuitbox::MemoryStore.new store.store("present", "value") store.load("present") # => "value" store.load("missing") # => nil # With expiration store.store("temp", "value", expires: 1) sleep 2 store.load("temp") # => nil ``` ```