circuit_breaker Ruby Gem

repository·master·Indexed 19 days ago

https://github.com/wsargent/circuit_breaker

A Ruby mixin that implements the Circuit Breaker pattern to wrap service calls and prevent cascading failures. It manages three states—Closed, Open, and Half Open—tripping the circuit when error thresholds or invocation timeouts are met. The library provides a `CircuitBreaker` module with a `circuit_method` macro, a configurable `CircuitHandler` for tuning failure thresholds and timeouts, and a `CircuitBrokenException` for handling tripped circuits.

Tokens
2.8K
Snippets
8
Records
12
Agent score
62%

What's inside circuit_breaker

  1. How the Circuit Breaker pattern works in circuit_breaker

    master

    The CircuitBreaker mixin implements Michael Nygard's pattern to protect your application from failing remote services. It manages three states:

    1. Closed: The default state. All calls to the protected method are allowed to proceed. Consecutive failures are tracked.
    2. Open: Triggered when the failure_threshold is reached or an invocation_timeout occurs. In this state, all calls immediately fail by raising a CircuitBrokenException without attempting to call the service.
    3. Half Open: Occurs after the failure_timeout duration has elapsed while in the Open state. The next call is allowed through. If it fails, the circuit immediately returns to the Open state. If it succeeds, the circuit returns to the Closed state and the failure count is reset.
  2. Implement CircuitBreaker in a Ruby class

    master

    To use the circuit breaker, include the CircuitBreaker module in your class and use the circuit_method macro to specify which method should be wrapped by the circuit breaker logic. You can optionally configure the behavior using a circuit_handler block or provide a custom handler class.

    require 'circuit_breaker'
    
    class TestService
      include CircuitBreaker
    
      # The method you want to protect
      def call_remote_service()
        # ... implementation ...
      end
    
      # Designate the method to be wrapped
      circuit_method :call_remote_service
    
      # Optional: Configure the handler
      circuit_handler do |handler|
        handler.logger = Logger.new(STDOUT)
        handler.failure_threshold = 5      # Number of failures before tripping
        handler.failure_timeout = 5        # Seconds to stay in 'open' state
        handler.invocation_timeout = 10     # Seconds before a slow call trips the circuit
        handler.excluded_exceptions = [NotConsideredFailureException]
      end
    
      # Optional: Use a custom handler class
      # circuit_handler_class MyCustomCircuitHandler
    end
  3. Configure the CircuitBreaker handler

    master

    The circuit_handler block allows you to tune the sensitivity and behavior of the circuit breaker. Available configuration options include:

    • failure_threshold: The number of consecutive failures required to trip the circuit from closed to open.
    • failure_timeout: The duration (in seconds) the circuit remains in the open state before transitioning to half_open.
    • invocation_timeout: The maximum duration (in seconds) allowed for a service call. If the call exceeds this, the circuit trips to open.
    • logger: A logger instance for recording circuit state changes and events.
    • excluded_exceptions: An array of exception classes that should not count towards the failure_threshold.
  4. Add Circuit Breaker functionality to a class

    master

    To use CircuitBreaker, include the module in your class and use circuit_method to specify which methods should be wrapped by the circuit breaker logic.

    By default, the circuit is "closed" (all calls pass). If consecutive failures exceed a threshold, the circuit "trips" to an "open" state, where subsequent calls raise a CircuitBrokenException. After a failure_timeout, the circuit enters a "half open" state to test the service. A success in "half open" closes the circuit, while a failure re-opens it immediately.

    require 'circuit_breaker'
    
    class TestService
      include CircuitBreaker
    
      def call_remote_service
        # Your service logic here
      end
    
      # Wraps the specified method with circuit breaker logic
      circuit_method :call_remote_service
    end
  5. Configure CircuitBreaker::CircuitHandler

    master

    The CircuitBreaker::CircuitHandler is a stateless configuration object used to define how a circuit breaker behaves. It manages thresholds for tripping the breaker, timeouts, and exception handling. Because it is stateless, the actual state of the circuit is maintained in a separate CircuitState object which is passed into the handler's methods.

    Configuration Options

    AttributeTypeDescription
    failure_threshold=IntegerSets the number of failures needed to trip the breaker. This automatically configures the trip_checker to use CircuitBreaker::TripChecker::Count.
    failure_percentage_threshold=Float/IntegerSets the percentage of failures needed to trip the breaker. This automatically configures the trip_checker to use CircuitBreaker::TripChecker::Percentage.
    failure_percentage_minimumIntegerThe minimum number of calls required before the failure_percentage_threshold is evaluated.
    failure_timeoutNumericThe period of time (in seconds) to wait after a failure before attempting to reset the breaker.
    invocation_timeoutNumericThe maximum time (in seconds) the protected method is allowed to run before a CircuitBreaker::CircuitBrokenException is thrown.
    excluded_exceptionsArray<Class>A list of exception classes that should be ignored and not counted as failures.
    loggerObjectAn optional logger for debugging circuit state transitions.
    trip_checkerObjectThe object that determines whether or not the circuit has been tripped.

    Default Values

    • failure_threshold: 5
    • failure_timeout: 5
    • invocation_timeout: 30
    • failure_percentage_minimum: 3
    • excluded_exceptions: []
    # Example: Configuring a handler with a percentage threshold
    handler = CircuitBreaker::CircuitHandler.new(my_logger)
    handler.failure_percentage_threshold = 50
    handler.failure_percentage_minimum = 10
    handler.invocation_timeout = 5
  6. Handle CircuitBreaker::CircuitBrokenException

    master

    When a circuit breaker is in an 'open' state (meaning it has tripped and is preventing further calls to a failing service), the library raises a CircuitBreaker::CircuitBrokenException.

    You can rescue this exception to implement fallback logic or to notify the user that the service is temporarily unavailable. The exception object includes a circuit_state attribute which indicates the state of the circuit at the time the exception was raised.

    begin
      # code that calls a protected service
    rescue CircuitBreaker::CircuitBrokenException => e
      puts "Circuit is open! State: #{e.circuit_state}"
      # Implement fallback logic here
    end
  7. Execute protected methods with CircuitHandler#handle

    master

    Use the handle method to wrap calls to methods you want to protect with a circuit breaker. The handler will monitor the success/failure of the method and manage the circuit state accordingly.

    Signature: handle(circuit_state, method, *args)

    • circuit_state: An instance of CircuitBreaker::CircuitState that tracks the current status (closed, open, half-open).
    • method: The method object to be executed.
    • *args: The arguments to pass to the method.

    Behavior:

    1. If the circuit is currently tripped (open), it will raise a CircuitBreaker::CircuitBrokenException immediately without executing the method.
    2. If the circuit is in a timeout period, it may attempt to reset to a half-open state.
    3. If the method executes within the invocation_timeout, it returns the result and records a success.
    4. If the method fails or exceeds the invocation_timeout, it records a failure (unless the exception is in excluded_exceptions).
    # Assuming 'service' is the object with the method to protect
    # and 'state' is a CircuitBreaker::CircuitState instance
    
    result = handler.handle(state, service.method(:some_unreliable_call), arg1, arg2)
  8. Use a custom CircuitHandler class

    master

    If you need to implement custom logic for how circuits trip or manage state, you can provide your own class using circuit_handler_class. The provided class should be compatible with the expected handler interface.

    class MyCustomCircuitHandler
      # Implementation of handler logic
    end
    
    class TestService
      include CircuitBreaker
      
      circuit_handler_class MyCustomCircuitHandler
      
      def call_remote_service; end
      circuit_method :call_remote_service
    end
  9. Check the current circuit state

    master

    You can query the current state of the circuit on an instance of the class using the circuit_state method. Note that state is tracked per instance, meaning different instances of the same class can have different circuit states.

    service = TestService.new
    puts service.circuit_state
  10. CircuitBreaker ClassMethods API Reference

    master

    The following methods are available to classes that include CircuitBreaker:

    • circuit_method(*methods): Takes a splat of method names and wraps them with the circuit handler. It undefines the original method and defines a new one that executes through the handler.
    • circuit_handler(&block): Returns the current circuit handler. If a block is provided, it yields the handler instance to the block for configuration.
    • circuit_handler_class(klass = nil): Sets or returns the class used to instantiate the circuit handler. If an argument is provided, it sets the class; otherwise, it returns the current class.