Class: Faulty::Storage::Memory

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

Overview

TODO:

Add a more sophsticated implmentation that can limit the number of circuits stored.

The default in-memory storage for circuits

This implementation is thread-safe and circuit state is shared across threads. Since state is stored in-memory, this state is not shared across processes, or persisted across application restarts.

Circuit state and runs are stored in memory. Although runs have a maximum size within a circuit, there is no limit on the number of circuits that can be stored. This means the user should be careful about the number of circuits that are created. To that end, it's a good idea to avoid dynamically-named circuits with this backend.

For a more robust distributed implementation, use the Redis storage backend.

This can be used as a reference implementation for storage backends that store a list of circuit run entries.

Defined Under Namespace

Classes: MemoryCircuit, Options

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**options) {|Options| ... } ⇒ Memory

Returns a new instance of Memory.

Parameters:

  • options (Hash)

    Attributes for Options

Yields:

  • (Options)

    For setting options in a block



76
77
78
79
# File 'lib/faulty/storage/memory.rb', line 76

def initialize(**options, &)
  @circuits = Concurrent::Map.new
  @options = Options.new(options, &)
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



26
27
28
# File 'lib/faulty/storage/memory.rb', line 26

def options
  @options
end

Instance Method Details

#clearvoid

This method returns an undefined value.

Clears all circuits



216
217
218
# File 'lib/faulty/storage/memory.rb', line 216

def clear
  @circuits.clear
end

#close(circuit) ⇒ Boolean

Mark a circuit as closed

Returns:

  • (Boolean)

    True if the circuit transitioned from open to closed

See Also:



141
142
143
144
145
146
147
# File 'lib/faulty/storage/memory.rb', line 141

def close(circuit)
  memory = fetch(circuit)
  memory.runs.modify { |_old| [] }
  closed = memory.state.compare_and_set(:open, :closed)
  memory.reserved_at.reset(nil) if closed
  closed
end

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

Add an entry to storage

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.

See Also:



104
105
106
107
108
109
110
111
112
# File 'lib/faulty/storage/memory.rb', line 104

def entry(circuit, time, success, status)
  memory = fetch(circuit)
  memory.runs.borrow do |runs|
    runs.push([time, success])
    runs.shift if runs.size > options.max_sample_size
  end

  Status.from_entries(memory.runs.value, **status.to_h) if status
end

#fault_tolerant?true

Memory storage is fault-tolerant by default

Returns:

  • (true)


223
224
225
# File 'lib/faulty/storage/memory.rb', line 223

def fault_tolerant?
  true
end

#get_options(circuit) ⇒ Hash

Get the options stored for circuit

Returns:

  • (Hash)

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

See Also:



86
87
88
# File 'lib/faulty/storage/memory.rb', line 86

def get_options(circuit)
  fetch(circuit).options
end

#history(circuit) ⇒ Array<Array>

Get the circuit history up to max_sample_size

Parameters:

  • circuit (Circuit)

    The circuit to get history for

Returns:

  • (Array<Array>)

    An array of history tuples

See Also:



202
203
204
# File 'lib/faulty/storage/memory.rb', line 202

def history(circuit)
  fetch(circuit).runs.value
end

#listArray<String>

Get a list of circuit names

Returns:

  • (Array<String>)

    The circuit names



209
210
211
# File 'lib/faulty/storage/memory.rb', line 209

def list
  @circuits.keys
end

#lock(circuit, state) ⇒ void

This method returns an undefined value.

Lock a circuit open or closed

Parameters:

  • circuit (Circuit)

    The circuit to lock

  • state (:open, :closed)

    The state to lock the circuit in

See Also:



164
165
166
167
# File 'lib/faulty/storage/memory.rb', line 164

def lock(circuit, state)
  memory = fetch(circuit)
  memory.lock = state
end

#open(circuit, opened_at) ⇒ Boolean

Mark a circuit as open

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

See Also:



119
120
121
122
123
124
# File 'lib/faulty/storage/memory.rb', line 119

def open(circuit, opened_at)
  memory = fetch(circuit)
  opened = memory.state.compare_and_set(:closed, :open)
  memory.opened_at.reset(opened_at) if opened
  opened
end

#reopen(circuit, opened_at, previous_opened_at) ⇒ Boolean

Mark a circuit as reopened

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

See Also:



131
132
133
134
# File 'lib/faulty/storage/memory.rb', line 131

def reopen(circuit, opened_at, previous_opened_at)
  memory = fetch(circuit)
  memory.opened_at.compare_and_set(previous_opened_at, opened_at)
end

#reserve(circuit, reserved_at, previous_reserved_at) ⇒ Boolean

Reserve an exclusive run for this circuit

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.

See Also:



154
155
156
157
# File 'lib/faulty/storage/memory.rb', line 154

def reserve(circuit, reserved_at, previous_reserved_at)
  memory = fetch(circuit)
  memory.reserved_at.compare_and_set(previous_reserved_at, reserved_at)
end

#reset(circuit) ⇒ void

This method returns an undefined value.

Reset a circuit

Parameters:

  • circuit (Circuit)

    The circuit to unlock

See Also:



184
185
186
# File 'lib/faulty/storage/memory.rb', line 184

def reset(circuit)
  @circuits.delete(circuit.name)
end

#set_options(circuit, stored_options) ⇒ void

This method returns an undefined value.

Store the options for a circuit

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.

See Also:



95
96
97
# File 'lib/faulty/storage/memory.rb', line 95

def set_options(circuit, stored_options)
  fetch(circuit).options = stored_options
end

#status(circuit) ⇒ Status

Get the status of a circuit

Parameters:

  • circuit (Circuit)

    The circuit to get status for

Returns:

  • (Status)

    The current status

See Also:



193
194
195
# File 'lib/faulty/storage/memory.rb', line 193

def status(circuit)
  fetch(circuit).status(circuit.options)
end

#unlock(circuit) ⇒ void

This method returns an undefined value.

Unlock a circuit

Parameters:

  • circuit (Circuit)

    The circuit to unlock

See Also:



174
175
176
177
# File 'lib/faulty/storage/memory.rb', line 174

def unlock(circuit)
  memory = fetch(circuit)
  memory.lock = nil
end