Class: Faulty::Storage::FaultTolerantProxy
- Inherits:
-
Object
- Object
- Faulty::Storage::FaultTolerantProxy
- Extended by:
- Forwardable
- Defined in:
- lib/faulty/storage/fault_tolerant_proxy.rb
Overview
A wrapper for storage backends that may raise errors
Faulty#initialize automatically wraps all non-fault-tolerant storage backends with this class.
If the storage backend raises a StandardError, it will be captured and
sent to the notifier.
The overall design preference is to keep protected code paths running
when the storage backend is degraded, even when that means losing
circuit-breaker protections that the storage normally provides:
#status returns a stub closed status (so Circuit#run proceeds),
#reserve returns true (so half-open test runs proceed), and the
write paths (#open, #reopen, #close, #entry) return false
to safe-deny the recorded transition without failing the in-flight
call. The trade-off is that a correlated outage of the storage
backend and the upstream protected by the circuit will let the fleet
converge on the upstream — but that fleet would converge anyway via
the stub-closed status path, so individual write methods don't make
it worse.
Defined Under Namespace
Classes: Options
Instance Attribute Summary collapse
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Class Method Summary collapse
-
.wrap(storage) ⇒ Storage::Interface
Wrap a storage backend in a FaultTolerantProxy unless it's already fault tolerant.
Instance Method Summary collapse
-
#clear ⇒ Object
Clear is not called in normal operation, so it doesn't capture errors.
-
#close(circuit) ⇒ Boolean
Safely mark a circuit as closed.
-
#entry(circuit, time, success, status) ⇒ Status?
Add a history entry safely.
-
#fault_tolerant? ⇒ true
This cache makes any storage fault tolerant, so this is always
true. -
#get_options(circuit) ⇒ Hash
Get circuit options safely.
-
#history(circuit) ⇒ Object
History is not called in normal operation, so it doesn't capture errors.
-
#initialize(storage, **options) {|Options| ... } ⇒ FaultTolerantProxy
constructor
A new instance of FaultTolerantProxy.
-
#list ⇒ Object
List is not called in normal operation, so it doesn't capture errors.
-
#lock(circuit, state) ⇒ Object
Lock is not called in normal operation, so it doesn't capture errors.
-
#open(circuit, opened_at) ⇒ Boolean
Safely mark a circuit as open.
-
#reopen(circuit, opened_at, previous_opened_at) ⇒ Boolean
Safely mark a circuit as reopened.
-
#reserve(circuit, reserved_at, previous_reserved_at) ⇒ Boolean
Safely reserve execution of a circuit.
-
#reset(circuit) ⇒ Object
Reset is not called in normal operation, so it doesn't capture errors.
-
#set_options(circuit, stored_options) ⇒ void
Set circuit options safely.
-
#status(circuit) ⇒ Status
Safely get the status of a circuit.
-
#unlock(circuit) ⇒ Object
Unlock is not called in normal operation, so it doesn't capture errors.
Constructor Details
#initialize(storage, **options) {|Options| ... } ⇒ FaultTolerantProxy
Returns a new instance of FaultTolerantProxy.
47 48 49 50 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 47 def initialize(storage, **, &) @storage = storage @options = Options.new(, &) end |
Instance Attribute Details
#options ⇒ Object (readonly)
Returns the value of attribute options.
28 29 30 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 28 def @options end |
Class Method Details
.wrap(storage) ⇒ Storage::Interface
Wrap a storage backend in a FaultTolerantProxy unless it's already fault tolerant
57 58 59 60 61 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 57 def self.wrap(storage, ...) return storage if storage.fault_tolerant? new(storage, ...) end |
Instance Method Details
#clear ⇒ Object
Clear is not called in normal operation, so it doesn't capture errors
104 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 104 def_delegators :@storage, :lock, :unlock, :reset, :history, :list, :clear |
#close(circuit) ⇒ Boolean
Safely mark a circuit as closed
171 172 173 174 175 176 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 171 def close(circuit) @storage.close(circuit) rescue StandardError => e .notifier.notify(:storage_failure, circuit: circuit, action: :close, error: e) false end |
#entry(circuit, time, success, status) ⇒ Status?
Add a history entry safely
135 136 137 138 139 140 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 135 def entry(circuit, time, success, status) @storage.entry(circuit, time, success, status) rescue StandardError => e .notifier.notify(:storage_failure, circuit: circuit, action: :entry, error: e) stub_status(circuit) if status end |
#fault_tolerant? ⇒ true
This cache makes any storage fault tolerant, so this is always true
212 213 214 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 212 def fault_tolerant? true end |
#get_options(circuit) ⇒ Hash
Get circuit options safely
111 112 113 114 115 116 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 111 def (circuit) @storage.(circuit) rescue StandardError => e .notifier.notify(:storage_failure, circuit: circuit, action: :get_options, error: e) nil end |
#history(circuit) ⇒ Object
History is not called in normal operation, so it doesn't capture errors
104 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 104 def_delegators :@storage, :lock, :unlock, :reset, :history, :list, :clear |
#list ⇒ Object
List is not called in normal operation, so it doesn't capture errors
104 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 104 def_delegators :@storage, :lock, :unlock, :reset, :history, :list, :clear |
#lock(circuit, state) ⇒ Object
Lock is not called in normal operation, so it doesn't capture errors
104 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 104 def_delegators :@storage, :lock, :unlock, :reset, :history, :list, :clear |
#open(circuit, opened_at) ⇒ Boolean
Safely mark a circuit as open
147 148 149 150 151 152 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 147 def open(circuit, opened_at) @storage.open(circuit, opened_at) rescue StandardError => e .notifier.notify(:storage_failure, circuit: circuit, action: :open, error: e) false end |
#reopen(circuit, opened_at, previous_opened_at) ⇒ Boolean
Safely mark a circuit as reopened
159 160 161 162 163 164 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 159 def reopen(circuit, opened_at, previous_opened_at) @storage.reopen(circuit, opened_at, previous_opened_at) rescue StandardError => e .notifier.notify(:storage_failure, circuit: circuit, action: :reopen, error: e) false end |
#reserve(circuit, reserved_at, previous_reserved_at) ⇒ Boolean
Safely reserve execution of a circuit
Returns true on storage error so half-open test runs proceed when
the backend is degraded. See the class-level docs for the gem's
fail-open trade-off.
202 203 204 205 206 207 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 202 def reserve(circuit, reserved_at, previous_reserved_at) @storage.reserve(circuit, reserved_at, previous_reserved_at) rescue StandardError => e .notifier.notify(:storage_failure, circuit: circuit, action: :reserve, error: e) true end |
#reset(circuit) ⇒ Object
Reset is not called in normal operation, so it doesn't capture errors
104 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 104 def_delegators :@storage, :lock, :unlock, :reset, :history, :list, :clear |
#set_options(circuit, stored_options) ⇒ void
This method returns an undefined value.
Set circuit options safely
123 124 125 126 127 128 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 123 def (circuit, ) @storage.(circuit, ) rescue StandardError => e .notifier.notify(:storage_failure, circuit: circuit, action: :set_options, error: e) nil end |
#status(circuit) ⇒ Status
Safely get the status of a circuit
If the backend is unavailable, this returns a stub status that indicates that the circuit is closed.
186 187 188 189 190 191 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 186 def status(circuit) @storage.status(circuit) rescue StandardError => e .notifier.notify(:storage_failure, circuit: circuit, action: :status, error: e) stub_status(circuit) end |
#unlock(circuit) ⇒ Object
Unlock is not called in normal operation, so it doesn't capture errors
104 |
# File 'lib/faulty/storage/fault_tolerant_proxy.rb', line 104 def_delegators :@storage, :lock, :unlock, :reset, :history, :list, :clear |