Class: BrainzLab::Utilities::CircuitBreaker
- Inherits:
-
Object
- Object
- BrainzLab::Utilities::CircuitBreaker
- Defined in:
- lib/brainzlab/utilities/circuit_breaker.rb
Overview
Circuit breaker pattern implementation for resilient external calls Integrates with Flux for metrics and Reflex for error tracking
States:
-
:closed - Normal operation, requests pass through
-
:open - Failing, requests are rejected immediately
-
:half_open - Testing, limited requests allowed to check recovery
Defined Under Namespace
Classes: CircuitOpenError
Constant Summary collapse
- STATES =
%i[closed open half_open].freeze
Instance Attribute Summary collapse
-
#failure_count ⇒ Object
readonly
Returns the value of attribute failure_count.
-
#last_failure_at ⇒ Object
readonly
Returns the value of attribute last_failure_at.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#state ⇒ Object
readonly
Returns the value of attribute state.
-
#success_count ⇒ Object
readonly
Returns the value of attribute success_count.
Class Method Summary collapse
- .call(name, **options) ⇒ Object
- .get(name) ⇒ Object
- .register(name) ⇒ Object
- .registry ⇒ Object
- .reset_all! ⇒ Object
- .status_all ⇒ Object
Instance Method Summary collapse
-
#available? ⇒ Boolean
Check if circuit is allowing requests.
-
#call(fallback: nil) ⇒ Object
Execute a block with circuit breaker protection.
-
#force_state!(new_state) ⇒ Object
Force the circuit to a specific state.
-
#initialize(name:, failure_threshold: 5, success_threshold: 2, recovery_timeout: 30, timeout: nil, exclude_exceptions: []) ⇒ CircuitBreaker
constructor
A new instance of CircuitBreaker.
-
#reset! ⇒ Object
Reset the circuit breaker.
-
#status ⇒ Object
Get circuit status.
Constructor Details
#initialize(name:, failure_threshold: 5, success_threshold: 2, recovery_timeout: 30, timeout: nil, exclude_exceptions: []) ⇒ CircuitBreaker
Returns a new instance of CircuitBreaker.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 34 def initialize(name:, failure_threshold: 5, success_threshold: 2, recovery_timeout: 30, timeout: nil, exclude_exceptions: []) @name = name @failure_threshold = failure_threshold @success_threshold = success_threshold @recovery_timeout = recovery_timeout @timeout = timeout @exclude_exceptions = exclude_exceptions @state = :closed @failure_count = 0 @success_count = 0 @last_failure_at = nil @mutex = Mutex.new end |
Instance Attribute Details
#failure_count ⇒ Object (readonly)
Returns the value of attribute failure_count.
32 33 34 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 32 def failure_count @failure_count end |
#last_failure_at ⇒ Object (readonly)
Returns the value of attribute last_failure_at.
32 33 34 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 32 def last_failure_at @last_failure_at end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
32 33 34 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 32 def name @name end |
#state ⇒ Object (readonly)
Returns the value of attribute state.
32 33 34 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 32 def state @state end |
#success_count ⇒ Object (readonly)
Returns the value of attribute success_count.
32 33 34 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 32 def success_count @success_count end |
Class Method Details
.call(name, **options) ⇒ Object
127 128 129 130 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 127 def call(name, **, &) breaker = get(name) || register(name, **) breaker.call(**.slice(:fallback), &) end |
.get(name) ⇒ Object
119 120 121 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 119 def get(name) registry[name.to_s] end |
.register(name) ⇒ Object
123 124 125 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 123 def register(name, **) registry[name.to_s] = new(name: name, **) end |
.registry ⇒ Object
115 116 117 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 115 def registry @registry ||= {} end |
.reset_all! ⇒ Object
132 133 134 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 132 def reset_all! registry.each_value(&:reset!) end |
.status_all ⇒ Object
136 137 138 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 136 def status_all registry.transform_values(&:status) end |
Instance Method Details
#available? ⇒ Boolean
Check if circuit is allowing requests
108 109 110 111 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 108 def available? check_state_transition! @state != :open end |
#call(fallback: nil) ⇒ Object
Execute a block with circuit breaker protection
51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 51 def call(fallback: nil, &) check_state_transition! case @state when :open track_rejected raise CircuitOpenError.new(@name, failure_count: @failure_count, last_failure_at: @last_failure_at) unless fallback fallback.respond_to?(:call) ? fallback.call : fallback when :closed, :half_open execute_with_protection(fallback, &) end end |
#force_state!(new_state) ⇒ Object
Force the circuit to a specific state
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 67 def force_state!(new_state) unless STATES.include?(new_state) raise BrainzLab::ValidationError.new( "Invalid circuit breaker state: #{new_state}", hint: "Valid states are: #{STATES.join(', ')}", code: 'invalid_circuit_state', field: 'state', context: { provided: new_state, valid_values: STATES } ) end @mutex.synchronize do @state = new_state @failure_count = 0 if new_state == :closed @success_count = 0 if new_state == :half_open end track_state_change(new_state) end |
#reset! ⇒ Object
Reset the circuit breaker
88 89 90 91 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 88 def reset! force_state!(:closed) @last_failure_at = nil end |
#status ⇒ Object
Get circuit status
94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/brainzlab/utilities/circuit_breaker.rb', line 94 def status { name: @name, state: @state, failure_count: @failure_count, success_count: @success_count, failure_threshold: @failure_threshold, success_threshold: @success_threshold, last_failure_at: @last_failure_at, recovery_timeout: @recovery_timeout } end |