Class: ResilientCall::Circuit
- Inherits:
-
Object
- Object
- ResilientCall::Circuit
- Defined in:
- lib/resilient_call/circuit.rb
Overview
Holds the state of a single named circuit and manages its transitions.
The mutable state lives in the injected storage (memory by default, Redis
for multi-process setups); this class owns only the transition rules.
Thread-safe within a process: every read-modify-write runs under an internal Mutex, so concurrent calls on the same instance stay consistent. Across processes, consistency is bounded by the storage backend (see Storage::Redis).
closed --(threshold failures)--> open --(reset_timeout elapsed)--> half_open
^ |
+------------------------- record_success! -------------------------+
half_open --(record_failure!)--> open (restarts the timer)
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#reset_timeout ⇒ Object
readonly
Returns the value of attribute reset_timeout.
-
#threshold ⇒ Object
readonly
Returns the value of attribute threshold.
Instance Method Summary collapse
-
#allow_request? ⇒ Boolean
Whether a request may pass right now.
- #failure_count ⇒ Object
-
#initialize(name, threshold: 5, reset_timeout: 60, storage: nil) ⇒ Circuit
constructor
A new instance of Circuit.
-
#last_failure ⇒ Object
The full Exception object captured most recently in this process.
- #opened_at ⇒ Object
- #record_failure!(error) ⇒ Object
- #record_success! ⇒ Object
- #reset! ⇒ Object
- #state ⇒ Object
-
#update_config(threshold: nil, reset_timeout: nil) ⇒ Object
Lets the entry point inject configured thresholds onto a circuit that was created lazily by the registry with default values.
Constructor Details
#initialize(name, threshold: 5, reset_timeout: 60, storage: nil) ⇒ Circuit
Returns a new instance of Circuit.
21 22 23 24 25 26 27 28 |
# File 'lib/resilient_call/circuit.rb', line 21 def initialize(name, threshold: 5, reset_timeout: 60, storage: nil) @name = name @threshold = threshold @reset_timeout = reset_timeout @storage = storage || Storage::Memory.new @last_failure = nil @mutex = Mutex.new end |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
19 20 21 |
# File 'lib/resilient_call/circuit.rb', line 19 def name @name end |
#reset_timeout ⇒ Object (readonly)
Returns the value of attribute reset_timeout.
19 20 21 |
# File 'lib/resilient_call/circuit.rb', line 19 def reset_timeout @reset_timeout end |
#threshold ⇒ Object (readonly)
Returns the value of attribute threshold.
19 20 21 |
# File 'lib/resilient_call/circuit.rb', line 19 def threshold @threshold end |
Instance Method Details
#allow_request? ⇒ Boolean
Whether a request may pass right now. An :open circuit whose reset_timeout has elapsed transitions to :half_open and lets a single probe through.
51 52 53 54 55 56 57 58 59 60 |
# File 'lib/resilient_call/circuit.rb', line 51 def allow_request? @mutex.synchronize do state = read_state case state[:status] when :closed, :half_open then true when :open then attempt_half_open(state) end end end |
#failure_count ⇒ Object
34 35 36 |
# File 'lib/resilient_call/circuit.rb', line 34 def failure_count read_field(:failure_count) end |
#last_failure ⇒ Object
The full Exception object captured most recently in this process. State shared through Redis only carries its message/class, so a fresh process reading an open circuit sees nil here — use CircuitOpenError for details.
41 42 43 |
# File 'lib/resilient_call/circuit.rb', line 41 def last_failure @mutex.synchronize { @last_failure } end |
#opened_at ⇒ Object
45 46 47 |
# File 'lib/resilient_call/circuit.rb', line 45 def opened_at read_field(:opened_at) end |
#record_failure!(error) ⇒ Object
71 72 73 74 75 76 77 78 79 80 |
# File 'lib/resilient_call/circuit.rb', line 71 def record_failure!(error) transition do |state| @last_failure = error state[:failure_count] += 1 state[:last_failure_message] = error. state[:last_failure_class] = error.class.name trip_open!(state) if should_open?(state) end end |
#record_success! ⇒ Object
62 63 64 65 66 67 68 69 |
# File 'lib/resilient_call/circuit.rb', line 62 def record_success! transition do |state| case state[:status] when :half_open then state.merge!(status: :closed, failure_count: 0) when :closed then state[:failure_count] = 0 end end end |
#reset! ⇒ Object
82 83 84 85 86 87 |
# File 'lib/resilient_call/circuit.rb', line 82 def reset! @mutex.synchronize do @last_failure = nil @storage.reset(@name) end end |
#state ⇒ Object
30 31 32 |
# File 'lib/resilient_call/circuit.rb', line 30 def state read_field(:status) end |
#update_config(threshold: nil, reset_timeout: nil) ⇒ Object
Lets the entry point inject configured thresholds onto a circuit that was created lazily by the registry with default values.
91 92 93 94 95 96 |
# File 'lib/resilient_call/circuit.rb', line 91 def update_config(threshold: nil, reset_timeout: nil) @mutex.synchronize do @threshold = threshold unless threshold.nil? @reset_timeout = reset_timeout unless reset_timeout.nil? end end |