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
Statusfrom 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 |