Class: Parse::LiveQuery::CircuitBreaker
- Inherits:
-
Object
- Object
- Parse::LiveQuery::CircuitBreaker
- Defined in:
- lib/parse/live_query/circuit_breaker.rb
Overview
Circuit breaker pattern for connection failure handling.
Prevents repeated connection attempts when the server is unavailable, allowing time for recovery before retrying.
States:
-
:closed - Normal operation, requests allowed
-
:open - Too many failures, requests blocked
-
:half_open - Testing if service recovered
Constant Summary collapse
- STATES =
Valid circuit states
[:closed, :open, :half_open].freeze
- DEFAULT_FAILURE_THRESHOLD =
Default number of failures before opening circuit
5- DEFAULT_RESET_TIMEOUT =
Default seconds before transitioning from open to half_open
60.0- DEFAULT_HALF_OPEN_REQUESTS =
Default number of successful requests in half_open before closing
1
Instance Attribute Summary collapse
-
#failure_count ⇒ Integer
readonly
Number of consecutive failures.
-
#failure_threshold ⇒ Integer
readonly
Failure threshold before opening.
-
#last_failure_at ⇒ Time?
readonly
When the last failure occurred.
-
#reset_timeout ⇒ Float
readonly
Seconds before half_open transition.
-
#state ⇒ Symbol
readonly
Current state (:closed, :open, :half_open).
-
#success_count ⇒ Integer
readonly
Number of successful requests in half_open.
Instance Method Summary collapse
-
#allow_request? ⇒ Boolean
Check if a request is allowed.
-
#closed? ⇒ Boolean
Check if circuit is closed (allowing requests).
-
#half_open? ⇒ Boolean
Check if circuit is half_open (testing recovery).
-
#info ⇒ Hash
Get circuit breaker info as hash.
-
#initialize(failure_threshold: DEFAULT_FAILURE_THRESHOLD, reset_timeout: DEFAULT_RESET_TIMEOUT, half_open_requests: DEFAULT_HALF_OPEN_REQUESTS, on_state_change: nil) ⇒ CircuitBreaker
constructor
Create a new circuit breaker.
-
#open? ⇒ Boolean
Check if circuit is open (blocking requests).
-
#record_failure ⇒ void
Record a failed request.
-
#record_success ⇒ void
Record a successful request.
-
#reset! ⇒ void
Reset the circuit breaker to closed state.
-
#time_until_half_open ⇒ Float?
Seconds until circuit transitions to half_open.
Constructor Details
#initialize(failure_threshold: DEFAULT_FAILURE_THRESHOLD, reset_timeout: DEFAULT_RESET_TIMEOUT, half_open_requests: DEFAULT_HALF_OPEN_REQUESTS, on_state_change: nil) ⇒ CircuitBreaker
Create a new circuit breaker
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 68 def initialize(failure_threshold: DEFAULT_FAILURE_THRESHOLD, reset_timeout: DEFAULT_RESET_TIMEOUT, half_open_requests: DEFAULT_HALF_OPEN_REQUESTS, on_state_change: nil) @failure_threshold = failure_threshold @reset_timeout = reset_timeout @half_open_requests = half_open_requests @on_state_change = on_state_change @monitor = Monitor.new @state = :closed @failure_count = 0 @success_count = 0 @last_failure_at = nil end |
Instance Attribute Details
#failure_count ⇒ Integer (readonly)
Returns number of consecutive failures.
49 50 51 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 49 def failure_count @failure_count end |
#failure_threshold ⇒ Integer (readonly)
Returns failure threshold before opening.
58 59 60 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 58 def failure_threshold @failure_threshold end |
#last_failure_at ⇒ Time? (readonly)
Returns when the last failure occurred.
55 56 57 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 55 def last_failure_at @last_failure_at end |
#reset_timeout ⇒ Float (readonly)
Returns seconds before half_open transition.
61 62 63 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 61 def reset_timeout @reset_timeout end |
#state ⇒ Symbol (readonly)
Returns current state (:closed, :open, :half_open).
46 47 48 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 46 def state @state end |
#success_count ⇒ Integer (readonly)
Returns number of successful requests in half_open.
52 53 54 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 52 def success_count @success_count end |
Instance Method Details
#allow_request? ⇒ Boolean
Thread-safe. Callbacks are invoked outside the synchronized block.
Check if a request is allowed
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 87 def allow_request? state_change = nil result = @monitor.synchronize do case @state when :closed true when :open if Time.now - @last_failure_at >= @reset_timeout state_change = transition_to_internal(:half_open) true else false end when :half_open @success_count < @half_open_requests end end # Invoke callback outside synchronized block to prevent deadlocks notify_state_change(state_change) if state_change result end |
#closed? ⇒ Boolean
Check if circuit is closed (allowing requests)
179 180 181 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 179 def closed? @monitor.synchronize { @state == :closed } end |
#half_open? ⇒ Boolean
Check if circuit is half_open (testing recovery)
185 186 187 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 185 def half_open? @monitor.synchronize { @state == :half_open } end |
#info ⇒ Hash
Get circuit breaker info as hash
201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 201 def info @monitor.synchronize do { state: @state, failure_count: @failure_count, success_count: @success_count, failure_threshold: @failure_threshold, reset_timeout: @reset_timeout, last_failure_at: @last_failure_at, time_until_half_open: time_until_half_open, } end end |
#open? ⇒ Boolean
Check if circuit is open (blocking requests)
173 174 175 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 173 def open? @monitor.synchronize { @state == :open } end |
#record_failure ⇒ void
Thread-safe. Callbacks are invoked outside the synchronized block.
This method returns an undefined value.
Record a failed request
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 138 def record_failure state_change = nil @monitor.synchronize do @failure_count += 1 @last_failure_at = Time.now case @state when :closed if @failure_count >= @failure_threshold Logging.warn("Circuit breaker opening", failures: @failure_count) state_change = transition_to_internal(:open) end when :half_open Logging.warn("Circuit breaker re-opening from half_open") state_change = transition_to_internal(:open) end end # Invoke callback outside synchronized block to prevent deadlocks notify_state_change(state_change) if state_change end |
#record_success ⇒ void
Thread-safe. Callbacks are invoked outside the synchronized block.
This method returns an undefined value.
Record a successful request
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 115 def record_success state_change = nil @monitor.synchronize do case @state when :half_open @success_count += 1 if @success_count >= @half_open_requests Logging.info("Circuit breaker closing after successful recovery") state_change = reset_internal! end when :closed @failure_count = 0 end end # Invoke callback outside synchronized block to prevent deadlocks notify_state_change(state_change) if state_change end |
#reset! ⇒ void
Thread-safe. Callbacks are invoked outside the synchronized block.
This method returns an undefined value.
Reset the circuit breaker to closed state
164 165 166 167 168 169 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 164 def reset! state_change = @monitor.synchronize { reset_internal! } # Invoke callback outside synchronized block to prevent deadlocks notify_state_change(state_change) if state_change end |
#time_until_half_open ⇒ Float?
Seconds until circuit transitions to half_open
191 192 193 194 195 196 197 |
# File 'lib/parse/live_query/circuit_breaker.rb', line 191 def time_until_half_open @monitor.synchronize do return nil unless @state == :open && @last_failure_at remaining = @reset_timeout - (Time.now - @last_failure_at) [remaining, 0].max end end |