Class: Faulty::Status
- Inherits:
-
Struct
- Object
- Struct
- Faulty::Status
- Includes:
- ImmutableOptions
- Defined in:
- lib/faulty/status.rb,
lib/faulty/status.rb
Overview
The status of a circuit
Includes information like the state and locks. Also calculates whether a circuit can be run, or if it has failed a threshold.
Constant Summary collapse
- STATES =
The allowed state values
%i[ open closed ].freeze
- LOCKS =
The allowed lock values
%i[ open closed ].freeze
Instance Attribute Summary collapse
-
#failure_rate ⇒ Float
readonly
A number from 0 to 1 representing the percentage of failures for the circuit.
-
#lock ⇒ :open, ...
readonly
If the circuit is locked, the state that it is locked in.
-
#opened_at ⇒ Integer?
readonly
If the circuit is open, the timestamp that it was opened.
-
#options ⇒ Circuit::Options
readonly
The options for the circuit.
-
#sample_size ⇒ Integer
readonly
The number of samples used to calculate the failure rate.
-
#state ⇒ :open, :closed
readonly
The stored circuit state.
-
#stub ⇒ Boolean
readonly
True if this status is a stub and not calculated from the storage backend.
Class Method Summary collapse
-
.from_entries(entries, **hash) ⇒ Status
Create a new
Status
from a list of circuit runs.
Instance Method Summary collapse
-
#can_run? ⇒ Boolean
Whether the circuit can be run.
-
#closed? ⇒ Boolean
Whether the circuit is closed.
- #defaults ⇒ Object
-
#fails_threshold? ⇒ Boolean
Whether the circuit fails the sample size and rate thresholds.
- #finalize ⇒ Object
-
#half_open? ⇒ Boolean
Whether the circuit is half-open.
-
#locked_closed? ⇒ Boolean
Whether the circuit is locked closed.
-
#locked_open? ⇒ Boolean
Whether the circuit is locked open.
-
#open? ⇒ Boolean
Whether the circuit is open.
- #required ⇒ Object
Methods included from ImmutableOptions
#dup_with, #initialize, #setup
Instance Attribute Details
#failure_rate ⇒ Float (readonly)
Returns A number from 0 to 1 representing the percentage of failures for the circuit. For exmaple 0.5 represents a 50% failure rate.
33 34 35 36 37 38 39 40 41 |
# File 'lib/faulty/status.rb', line 33 Status = Struct.new( :state, :lock, :opened_at, :failure_rate, :sample_size, :options, :stub ) |
#lock ⇒ :open, ... (readonly)
Returns If the circuit is locked, the state that
it is locked in. Default nil
.
33 34 35 36 37 38 39 40 41 |
# File 'lib/faulty/status.rb', line 33 Status = Struct.new( :state, :lock, :opened_at, :failure_rate, :sample_size, :options, :stub ) |
#opened_at ⇒ Integer? (readonly)
Returns If the circuit is open, the timestamp that it was
opened. This is not necessarily reset when the circuit is closed.
Default nil
.
33 34 35 36 37 38 39 40 41 |
# File 'lib/faulty/status.rb', line 33 Status = Struct.new( :state, :lock, :opened_at, :failure_rate, :sample_size, :options, :stub ) |
#options ⇒ Circuit::Options (readonly)
Returns The options for the circuit.
33 34 35 36 37 38 39 40 41 |
# File 'lib/faulty/status.rb', line 33 Status = Struct.new( :state, :lock, :opened_at, :failure_rate, :sample_size, :options, :stub ) |
#sample_size ⇒ Integer (readonly)
Returns The number of samples used to calculate the failure rate.
33 34 35 36 37 38 39 40 41 |
# File 'lib/faulty/status.rb', line 33 Status = Struct.new( :state, :lock, :opened_at, :failure_rate, :sample_size, :options, :stub ) |
#state ⇒ :open, :closed (readonly)
Returns The stored circuit state. This is always open
or closed. Half-open is calculated from the current time. For that
reason, calling state directly should be avoided. Instead use the
status methods #open?, #closed?, and #half_open?.
Default :closed
.
33 34 35 36 37 38 39 40 41 |
# File 'lib/faulty/status.rb', line 33 Status = Struct.new( :state, :lock, :opened_at, :failure_rate, :sample_size, :options, :stub ) |
#stub ⇒ Boolean (readonly)
True if this status is a stub and not calculated from
the storage backend. Used by Faulty::Storage::FaultTolerantProxy when
returning the status for an offline storage backend. Default false
.
33 34 35 36 37 38 39 40 41 |
# File 'lib/faulty/status.rb', line 33 Status = Struct.new( :state, :lock, :opened_at, :failure_rate, :sample_size, :options, :stub ) |
Class Method Details
.from_entries(entries, **hash) ⇒ Status
Create a new Status
from a list of circuit runs
For storage backends that store entries, this automatically calculates failure_rate and sample size.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/faulty/status.rb', line 68 def self.from_entries(entries, **hash) window_start = Faulty.current_time - hash[:options].evaluation_window size = entries.size i = 0 failures = 0 sample_size = 0 # This is a hot loop, and while is slightly faster than each while i < size time, success = entries[i] i += 1 next unless time > window_start sample_size += 1 failures += 1 unless success end new(hash.merge( sample_size: sample_size, failure_rate: sample_size.zero? ? 0.0 : failures.to_f / sample_size )) end |
Instance Method Details
#can_run? ⇒ Boolean
Whether the circuit can be run
Takes the circuit state, locks and cooldown into account
137 138 139 140 141 |
# File 'lib/faulty/status.rb', line 137 def can_run? return false if locked_open? closed? || locked_closed? || half_open? end |
#closed? ⇒ Boolean
Whether the circuit is closed
This is mutually exclusive with #open? and #half_open?
105 106 107 |
# File 'lib/faulty/status.rb', line 105 def closed? state == :closed end |
#defaults ⇒ Object
164 165 166 167 168 169 170 171 |
# File 'lib/faulty/status.rb', line 164 def defaults { state: :closed, failure_rate: 0.0, sample_size: 0, stub: false } end |
#fails_threshold? ⇒ Boolean
Whether the circuit fails the sample size and rate thresholds
146 147 148 149 150 |
# File 'lib/faulty/status.rb', line 146 def fails_threshold? return false if sample_size < .sample_threshold failure_rate >= .rate_threshold end |
#finalize ⇒ Object
152 153 154 155 156 157 158 |
# File 'lib/faulty/status.rb', line 152 def finalize raise ArgumentError, "state must be a symbol in #{self.class}::STATES" unless STATES.include?(state) unless lock.nil? || LOCKS.include?(lock) raise ArgumentError, "lock must be a symbol in #{self.class}::LOCKS or nil" end raise ArgumentError, 'opened_at is required if state is open' if state == :open && opened_at.nil? end |
#half_open? ⇒ Boolean
114 115 116 |
# File 'lib/faulty/status.rb', line 114 def half_open? state == :open && opened_at + .cool_down <= Faulty.current_time end |
#locked_closed? ⇒ Boolean
Whether the circuit is locked closed
128 129 130 |
# File 'lib/faulty/status.rb', line 128 def locked_closed? lock == :closed end |
#locked_open? ⇒ Boolean
Whether the circuit is locked open
121 122 123 |
# File 'lib/faulty/status.rb', line 121 def locked_open? lock == :open end |
#open? ⇒ Boolean
Whether the circuit is open
This is mutually exclusive with #closed? and #half_open?
96 97 98 |
# File 'lib/faulty/status.rb', line 96 def open? state == :open && opened_at + .cool_down > Faulty.current_time end |
#required ⇒ Object
160 161 162 |
# File 'lib/faulty/status.rb', line 160 def required %i[state failure_rate sample_size options stub] end |