Class: Supervision::CircuitControl
- Inherits:
-
Object
- Object
- Supervision::CircuitControl
- Extended by:
- Forwardable
- Defined in:
- lib/supervision/circuit_control.rb
Overview
A class responsible for controling state of the circuit
Constant Summary collapse
- MAX_THREAD_LIFETIME =
5
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
private
The circuit configuration.
-
#monitor ⇒ Object
readonly
private
The circuit performance monitor.
-
#scheduler ⇒ Object
readonly
private
The reset timeout scheduler.
Instance Method Summary collapse
-
#fail_fast! ⇒ Object
private
Fail fast on any call.
-
#failure_count ⇒ Integer
Total failure count for current circuit.
- #failure_count_exceeded? ⇒ Boolean private
-
#fsm ⇒ FiniteMachine
private
Creates internal finite state machine to transitions through three states :closed, :open and :half_open.
-
#handle_failure(error = nil) ⇒ Object
Handler exception.
-
#initialize(options = {}) ⇒ CircuitControl
constructor
Create a circuit control.
-
#last_failure_time ⇒ Time
Last time failure occured.
-
#max_thread_lifetime ⇒ Time
private
Estimate maximum duration the scheduling thread should live.
-
#measure_timeout ⇒ Object
private
Measure remaining timeout.
-
#record_failure ⇒ Object
Record failure count.
-
#record_success ⇒ Object
Record successful call.
-
#reset! ⇒ nil
Force closed state and reset failure statistics.
-
#reset_failure ⇒ Object
Resets failure count.
-
#run_loop(thread) ⇒ Object
private
Run scheduler loop.
-
#timeout_exceeded? ⇒ Boolean
private
Check if remaining duration until reset has been exceeded.
- #tripped? ⇒ Boolean private
Constructor Details
#initialize(options = {}) ⇒ CircuitControl
Create a circuit control
33 34 35 36 37 38 39 40 |
# File 'lib/supervision/circuit_control.rb', line 33 def initialize( = {}) @config = Configuration.new() @failure_count = Atomic.new(0) @last_failure_time = Atomic.new @lock = Mutex.new @monitor = CircuitMonitor.new fsm end |
Instance Attribute Details
#config ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The circuit configuration
14 15 16 |
# File 'lib/supervision/circuit_control.rb', line 14 def config @config end |
#monitor ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The circuit performance monitor
24 25 26 |
# File 'lib/supervision/circuit_control.rb', line 24 def monitor @monitor end |
#scheduler ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The reset timeout scheduler
19 20 21 |
# File 'lib/supervision/circuit_control.rb', line 19 def scheduler @scheduler end |
Instance Method Details
#fail_fast! ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Fail fast on any call
116 117 118 119 |
# File 'lib/supervision/circuit_control.rb', line 116 def fail_fast! monitor.measure(:open_circuit) raise CircuitBreakerOpenError end |
#failure_count ⇒ Integer
Total failure count for current circuit
87 88 89 |
# File 'lib/supervision/circuit_control.rb', line 87 def failure_count @failure_count.value end |
#failure_count_exceeded? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
124 125 126 |
# File 'lib/supervision/circuit_control.rb', line 124 def failure_count_exceeded? failure_count > @config.max_failures end |
#fsm ⇒ FiniteMachine
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Creates internal finite state machine to transitions through three states :closed, :open and :half_open.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/supervision/circuit_control.rb', line 49 def fsm context = self @fsm ||= FiniteMachine.define do initial :closed target context events do event :trip, [:closed, :half_open] => :open event :attempt_reset, :open => :half_open event :reset, :half_open => :closed end callbacks do on_enter :closed do |event| reset_failure end on_enter :open do |event| measure_timeout fail_fast! end on_enter :half_open do |event| monitor.measure(:half_open_circuit) end end end end |
#handle_failure(error = nil) ⇒ Object
Handler exception
151 152 153 154 155 156 |
# File 'lib/supervision/circuit_control.rb', line 151 def handle_failure(error = nil) fail_fast! if fsm.open? record_failure monitor.record_failure trip if failure_count_exceeded? || fsm.half_open? end |
#last_failure_time ⇒ Time
Last time failure occured
96 97 98 |
# File 'lib/supervision/circuit_control.rb', line 96 def last_failure_time @last_failure_time.value end |
#max_thread_lifetime ⇒ Time
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Estimate maximum duration the scheduling thread should live
220 221 222 |
# File 'lib/supervision/circuit_control.rb', line 220 def max_thread_lifetime @config.reset_timeout + 100.milli end |
#measure_timeout ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Measure remaining timeout
188 189 190 191 192 193 194 195 196 197 |
# File 'lib/supervision/circuit_control.rb', line 188 def measure_timeout @scheduler = Thread.new do Thread.current.abort_on_exception = true thread = Thread.current thread[:created_at] = Time.now @lock.synchronize do run_loop(thread) end end end |
#record_failure ⇒ Object
Record failure count
170 171 172 173 174 175 |
# File 'lib/supervision/circuit_control.rb', line 170 def record_failure if fsm.closed? || fsm.half_open? @failure_count.update { |v| v + 1 } @last_failure_time.value = Time.now end end |
#record_success ⇒ Object
Record successful call
161 162 163 164 165 |
# File 'lib/supervision/circuit_control.rb', line 161 def record_success reset if fsm.half_open? reset_failure monitor.record_success end |
#reset! ⇒ nil
Force closed state and reset failure statistics
105 106 107 108 109 |
# File 'lib/supervision/circuit_control.rb', line 105 def reset! fsm.reset! reset_failure throw(:terminate) if @scheduler && @scheduler.alive? end |
#reset_failure ⇒ Object
Resets failure count
180 181 182 183 |
# File 'lib/supervision/circuit_control.rb', line 180 def reset_failure @failure_count.value = 0 @last_failure_time.value = nil end |
#run_loop(thread) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Run scheduler loop
202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/supervision/circuit_control.rb', line 202 def run_loop(thread) catch(:terminate) do loop do if tripped? attempt_reset && break elsif Time.now - thread[:created_at] > max_thread_lifetime thread.kill if thread.alive? break end end end end |
#timeout_exceeded? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Check if remaining duration until reset has been exceeded
142 143 144 145 146 |
# File 'lib/supervision/circuit_control.rb', line 142 def timeout_exceeded? return false unless last_failure_time timeout = Time.now - last_failure_time timeout > @config.reset_timeout end |
#tripped? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
131 132 133 |
# File 'lib/supervision/circuit_control.rb', line 131 def tripped? fsm.open? && timeout_exceeded? end |