Class: Faulty::Storage::Redis
- Inherits:
-
Object
- Object
- Faulty::Storage::Redis
- Defined in:
- lib/faulty/storage/redis.rb
Overview
A storage backend for storing circuit state in Redis.
When using this or any networked backend, be sure to evaluate the risk, and set conservative timeouts so that the circuit storage does not cause cascading failures in your application when evaluating circuits. Always wrap this backend with a FaultTolerantProxy to limit the effect of these types of events.
Defined Under Namespace
Classes: Options
Constant Summary collapse
- ENTRY_SEPARATOR =
Separates the time/status for history entry strings
':'
Instance Attribute Summary collapse
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Instance Method Summary collapse
-
#close(circuit) ⇒ Boolean
Mark a circuit as closed.
-
#entry(circuit, time, success) ⇒ Array<Array>
Add an entry to storage.
-
#fault_tolerant? ⇒ true
Redis storage is not fault-tolerant.
-
#get_options(circuit) ⇒ Hash
Get the options stored for circuit.
-
#history(circuit) ⇒ Array<Array>
Get the circuit history up to
max_sample_size. -
#initialize(**options) {|Options| ... } ⇒ Redis
constructor
A new instance of Redis.
-
#list ⇒ Array<String>
List all unexpired circuits.
-
#lock(circuit, state) ⇒ void
Lock a circuit open or closed.
-
#open(circuit, opened_at) ⇒ Boolean
Mark a circuit as open.
-
#reopen(circuit, opened_at, previous_opened_at) ⇒ Boolean
Mark a circuit as reopened.
-
#reset(circuit) ⇒ void
Reset a circuit.
-
#set_options(circuit, stored_options) ⇒ void
Store the options for a circuit.
-
#status(circuit) ⇒ Status
Get the status of a circuit.
-
#unlock(circuit) ⇒ void
Unlock a circuit.
Constructor Details
#initialize(**options) {|Options| ... } ⇒ Redis
Returns a new instance of Redis.
83 84 85 86 87 88 89 90 |
# File 'lib/faulty/storage/redis.rb', line 83 def initialize(**, &block) @options = Options.new(, &block) # Ensure JSON is available since we don't explicitly require it JSON # rubocop:disable Lint/Void end |
Instance Attribute Details
#options ⇒ Object (readonly)
Returns the value of attribute options.
16 17 18 |
# File 'lib/faulty/storage/redis.rb', line 16 def @options end |
Instance Method Details
#close(circuit) ⇒ Boolean
Mark a circuit as closed
164 165 166 167 168 169 170 |
# File 'lib/faulty/storage/redis.rb', line 164 def close(circuit) redis do |r| closed = compare_and_set(r, state_key(circuit), ['open'], 'closed', ex: .circuit_ttl) r.del(entries_key(circuit)) if closed closed end end |
#entry(circuit, time, success) ⇒ Array<Array>
Add an entry to storage
122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/faulty/storage/redis.rb', line 122 def entry(circuit, time, success) key = entries_key(circuit) result = pipe do |r| r.sadd(list_key, circuit.name) r.expire(list_key, .circuit_ttl + .list_granularity) if .circuit_ttl r.lpush(key, "#{time}#{ENTRY_SEPARATOR}#{success ? 1 : 0}") r.ltrim(key, 0, .max_sample_size - 1) r.expire(key, .sample_ttl) if .sample_ttl r.lrange(key, 0, -1) end map_entries(result.last) end |
#fault_tolerant? ⇒ true
Redis storage is not fault-tolerant
252 253 254 |
# File 'lib/faulty/storage/redis.rb', line 252 def fault_tolerant? false end |
#get_options(circuit) ⇒ Hash
Get the options stored for circuit
97 98 99 100 101 102 |
# File 'lib/faulty/storage/redis.rb', line 97 def (circuit) json = redis { |r| r.get((circuit)) } return if json.nil? JSON.parse(json, symbolize_names: true) end |
#history(circuit) ⇒ Array<Array>
Get the circuit history up to max_sample_size
237 238 239 240 |
# File 'lib/faulty/storage/redis.rb', line 237 def history(circuit) entries = redis { |r| r.lrange(entries_key(circuit), 0, -1) } map_entries(entries).reverse end |
#list ⇒ Array<String>
List all unexpired circuits
245 246 247 |
# File 'lib/faulty/storage/redis.rb', line 245 def list redis { |r| r.sunion(*all_list_keys) } end |
#lock(circuit, state) ⇒ void
This method returns an undefined value.
Lock a circuit open or closed
The circuit_ttl does not apply to locks
179 180 181 |
# File 'lib/faulty/storage/redis.rb', line 179 def lock(circuit, state) redis { |r| r.set(lock_key(circuit), state) } end |
#open(circuit, opened_at) ⇒ Boolean
Mark a circuit as open
140 141 142 143 144 145 146 |
# File 'lib/faulty/storage/redis.rb', line 140 def open(circuit, opened_at) redis do |r| opened = compare_and_set(r, state_key(circuit), ['closed', nil], 'open', ex: .circuit_ttl) r.set(opened_at_key(circuit), opened_at, ex: .circuit_ttl) if opened opened end end |
#reopen(circuit, opened_at, previous_opened_at) ⇒ Boolean
Mark a circuit as reopened
153 154 155 156 157 |
# File 'lib/faulty/storage/redis.rb', line 153 def reopen(circuit, opened_at, previous_opened_at) redis do |r| compare_and_set(r, opened_at_key(circuit), [previous_opened_at.to_s], opened_at, ex: .circuit_ttl) end end |
#reset(circuit) ⇒ void
This method returns an undefined value.
Reset a circuit
197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/faulty/storage/redis.rb', line 197 def reset(circuit) pipe do |r| r.del( entries_key(circuit), opened_at_key(circuit), lock_key(circuit), (circuit) ) r.set(state_key(circuit), 'closed', ex: .circuit_ttl) end end |
#set_options(circuit, stored_options) ⇒ void
This method returns an undefined value.
Store the options for a circuit
These will be serialized as JSON
111 112 113 114 115 |
# File 'lib/faulty/storage/redis.rb', line 111 def (circuit, ) redis do |r| r.set((circuit), JSON.dump(), ex: .circuit_ttl) end end |
#status(circuit) ⇒ Status
Get the status of a circuit
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/faulty/storage/redis.rb', line 214 def status(circuit) futures = {} pipe do |r| futures[:state] = r.get(state_key(circuit)) futures[:lock] = r.get(lock_key(circuit)) futures[:opened_at] = r.get(opened_at_key(circuit)) futures[:entries] = r.lrange(entries_key(circuit), 0, -1) end Faulty::Status.from_entries( map_entries(futures[:entries].value), state: futures[:state].value&.to_sym || :closed, lock: futures[:lock].value&.to_sym, opened_at: futures[:opened_at].value ? futures[:opened_at].value.to_i : nil, options: circuit. ) end |
#unlock(circuit) ⇒ void
This method returns an undefined value.
Unlock a circuit
188 189 190 |
# File 'lib/faulty/storage/redis.rb', line 188 def unlock(circuit) redis { |r| r.del(lock_key(circuit)) } end |