Class: Faulty::Storage::Interface

Inherits:
Object
  • Object
show all
Defined in:
lib/faulty/storage/interface.rb

Overview

The interface required for a storage backend implementation

This is for documentation only and is not loaded

Instance Method Summary collapse

Instance Method Details

#clearvoid

This method returns an undefined value.

Reset all circuits

Some implementions may clear circuits on a best-effort basis since all circuits may not be known.

Raises:

  • NotImplementedError If the storage backend does not support clearing.



242
243
244
# File 'lib/faulty/storage/interface.rb', line 242

def clear
  raise NotImplementedError
end

#close(circuit) ⇒ Boolean

Set the circuit state to closed

If multiple parallel processes close the circuit simultaneously, close may be called more than once. If so, this method should return true only once, when the circuit transitions from open to closed.

The backend should reset the reserved_at value to empty when closing the circuit.

If the backend does not support locking or atomic operations, then it may always return true, but that could result in duplicate close notifications.

Returns:

  • (Boolean)

    True if the circuit transitioned from open to closed

Raises:

  • (NotImplementedError)


128
129
130
# File 'lib/faulty/storage/interface.rb', line 128

def close(circuit)
  raise NotImplementedError
end

#entry(circuit, time, success, status) ⇒ Status?

Add a circuit run entry to storage

The backend may choose to store this in whatever manner it chooses as long as it can implement the other read methods.

Parameters:

  • circuit (Circuit)

    The circuit that ran

  • time (Float)

    The unix timestamp for the run, from Faulty.current_time

  • success (Boolean)

    True if the run succeeded

  • status (Status, nil)

    The previous status. If given, this method must return an updated status object from the new entry data.

Returns:

  • (Status, nil)

    If status is not nil, the updated status object.

Raises:

  • (NotImplementedError)


44
45
46
# File 'lib/faulty/storage/interface.rb', line 44

def entry(circuit, time, success, status)
  raise NotImplementedError
end

#fault_tolerant?Boolean

Can this storage backend raise an error?

If the storage backend returns false from this method, it will be wrapped in a FaultTolerantProxy, otherwise it will be used as-is.

Returns:

  • (Boolean)

    True if this cache backend is fault tolerant

Raises:

  • (NotImplementedError)


252
253
254
# File 'lib/faulty/storage/interface.rb', line 252

def fault_tolerant?
  raise NotImplementedError
end

#get_options(circuit) ⇒ Hash

Get the options stored for circuit

They should be returned exactly as given by #set_options

Returns:

  • (Hash)

    A hash of the options stored by #set_options. The keys must be symbols.

Raises:

  • (NotImplementedError)


15
16
17
# File 'lib/faulty/storage/interface.rb', line 15

def get_options(circuit)
  raise NotImplementedError
end

#history(circuit) ⇒ Array<Array>

Get the entry history of a circuit

No concurrency gurantees are provided for getting status. It's possible that status may represent a circuit in the middle of modification.

A storage backend may choose not to implement this method and instead return an empty array.

Each item in the history array is an array of two items (a tuple) of [run_time, succeeded], where run_time is a unix timestamp, and succeeded is a boolean, true if the run succeeded.

Parameters:

  • circuit (Circuit)

    The circuit to get history for

Returns:

  • (Array<Array>)

    An array of history tuples

Raises:

  • (NotImplementedError)


221
222
223
# File 'lib/faulty/storage/interface.rb', line 221

def history(circuit)
  raise NotImplementedError
end

#listArray<String>

Get a list of all circuit names

If the storage backend does not support listing circuits, this may return an empty array.

Returns:

  • (Array<String>)

Raises:

  • (NotImplementedError)


231
232
233
# File 'lib/faulty/storage/interface.rb', line 231

def list
  raise NotImplementedError
end

#lock(circuit, state) ⇒ void

This method returns an undefined value.

Lock the circuit in a given state

No concurrency gurantees are provided for locking

Parameters:

  • circuit (Circuit)

    The circuit to lock

  • state (:open, :closed)

    The state to lock the circuit in

Raises:

  • (NotImplementedError)


169
170
171
# File 'lib/faulty/storage/interface.rb', line 169

def lock(circuit, state)
  raise NotImplementedError
end

#open(circuit, opened_at) ⇒ Boolean

Set the circuit state to open

If multiple parallel processes open the circuit simultaneously, open may be called more than once. If so, this method should return true only once, when the circuit transitions from closed to open.

If the backend does not support locking or atomic operations, then it may always return true, but that could result in duplicate open notifications.

If returning true, this method also updates opened_at to the current time.

Parameters:

  • circuit (Circuit)

    The circuit to open

  • opened_at (Float)

    The timestamp the circuit was opened at, from Faulty.current_time

Returns:

  • (Boolean)

    True if the circuit transitioned from closed to open

Raises:

  • (NotImplementedError)


65
66
67
# File 'lib/faulty/storage/interface.rb', line 65

def open(circuit, opened_at)
  raise NotImplementedError
end

#reopen(circuit, opened_at, previous_opened_at) ⇒ Boolean

Reset the opened_at time for a half_open circuit

If multiple parallel processes open the circuit simultaneously, reopen may be called more than once. If so, this method should return true only once, when the circuit updates the opened_at value. It can use the value from previous_opened_at to do a compare-and-set operation.

If the backend does not support locking or atomic operations, then it may always return true, but that could result in duplicate reopen notifications.

The backend MUST NOT clear reserved_at here.

Preserving the prior cycle's reserved_at is load-bearing for half-open exclusivity. If a late-arriving caller read status while reserved_at was still nil (before the winning process reserved), its subsequent reserve(circuit, T, nil) CAS must fail. Clearing reserved_at in reopen would let that stale CAS incorrectly succeed and produce a duplicate half-open run.

Beyond exclusivity, the prior reservation expires naturally at reserved_at + cool_down, aligned with the start of the next half-open window (since reserved_at <= new_opened_at). An explicit reset would be redundant with the cool-down-aligned expiry that already handles crash recovery.

This invariant assumes the reservation TTL equals cool_down. If a separate reservation_ttl is ever introduced, this method must be revisited and may need to clear reserved_at explicitly.

Parameters:

  • circuit (Circuit)

    The circuit to reopen

  • opened_at (Float)

    The timestamp the circuit was opened at, from Faulty.current_time

  • previous_opened_at (Float)

    The last known value of opened_at. Can be used to compare-and-set. Always non-nil — Circuit#failure! only enters the reopen branch when status.half_open? is true, which requires non-nil opened_at. Unlike previous_reserved_at on #reserve, there is no legitimate "no prior value" call path to reopen, so backends may treat this parameter as required and are not expected to handle nil.

Returns:

  • (Boolean)

    True if the opened_at time was updated

Raises:

  • (NotImplementedError)


110
111
112
# File 'lib/faulty/storage/interface.rb', line 110

def reopen(circuit, opened_at, previous_opened_at)
  raise NotImplementedError
end

#reserve(circuit, reserved_at, previous_reserved_at) ⇒ Boolean

Reserve an exclusive run for this circuit

This is used when the circuit is half-open and the test run is being attempted. We need to make sure only a single run is allowed.

The backend should store reserved_at and use it to serve future status requests. When setting reserved_at, the backend should atomically compare any existing value using previous_reserved_at. This ensures that mutltiple parallel processes can't reserve the circuit.

The return value is the caller's signal to proceed with the half-open test run, not a strict report of whether atomic acquisition succeeded. Atomic backends should return true only when the CAS against previous_reserved_at succeeds. Non-atomic backends, no-op backends (Null), or wrappers that fail open (FaultTolerantProxy) may always return true; the caller will proceed at the cost of allowing duplicate half-open test runs.

Parameters:

  • circuit (Circuit)

    The circuit to reserve

  • reserved_at (Float)

    The timestamp of this reservation, from Faulty.current_time

  • previous_reserved_at (Float, nil)

    The last known value of reserved_at, or nil for the first reservation in a new open cycle. Can be used to compare-and-set.

Returns:

  • (Boolean)

    True if the caller may proceed with the half-open test run; false if another caller already holds the reservation.

Raises:

  • (NotImplementedError)


158
159
160
# File 'lib/faulty/storage/interface.rb', line 158

def reserve(circuit, reserved_at, previous_reserved_at)
  raise NotImplementedError
end

#reset(circuit) ⇒ void

This method returns an undefined value.

Reset the circuit to a fresh state

Clears all circuit status including entries, state, locks, opened_at, options, and any other values that would affect Status.

No concurrency gurantees are provided for resetting

Parameters:

  • circuit (Circuit)

    The circuit to unlock

Raises:

  • (NotImplementedError)


192
193
194
# File 'lib/faulty/storage/interface.rb', line 192

def reset(circuit)
  raise NotImplementedError
end

#set_options(circuit, stored_options) ⇒ void

This method returns an undefined value.

Store the options for a circuit

They should be returned exactly as given by #set_options

Parameters:

  • circuit (Circuit)

    The circuit to set options for

  • stored_options (Hash<Symbol, Object>)

    A hash of symbol option names to circuit options. These option values are guranteed to be primive values.

Raises:

  • (NotImplementedError)


28
29
30
# File 'lib/faulty/storage/interface.rb', line 28

def set_options(circuit, stored_options)
  raise NotImplementedError
end

#status(circuit) ⇒ Status

Get the status object for a circuit

No concurrency gurantees are provided for getting status. It's possible that status may represent a circuit in the middle of modification.

Parameters:

  • circuit (Circuit)

    The circuit to get status for

Returns:

  • (Status)

    The current status

Raises:

  • (NotImplementedError)


203
204
205
# File 'lib/faulty/storage/interface.rb', line 203

def status(circuit)
  raise NotImplementedError
end

#unlock(circuit) ⇒ void

This method returns an undefined value.

Unlock the circuit from any state

No concurrency gurantees are provided for locking

Parameters:

  • circuit (Circuit)

    The circuit to unlock

Raises:

  • (NotImplementedError)


179
180
181
# File 'lib/faulty/storage/interface.rb', line 179

def unlock(circuit)
  raise NotImplementedError
end