Class: Sc4ry::Circuits

Inherits:
Object
  • Object
show all
Includes:
Constants, Exceptions
Defined in:
lib/sc4ry/circuits.rb

Overview

Circuits and default configuration management class

Constant Summary collapse

@@circuits_store =
Sc4ry::Store.instance
@@circuits_notifiers =
Sc4ry::Notifiers
@@circuits_loggers =
Sc4ry::Loggers
@@config =
DEFAULT_CONFIG

Constants included from Constants

Sc4ry::Constants::CURRENT_NOTIFIERS, Sc4ry::Constants::DEFAULT_CONFIG, Sc4ry::Constants::DEFAULT_CONFIG_FORMATS

forwarders collapse

Default Sc4ry configuration management collapse

Circuits management collapse

Class Method Summary collapse

Class Method Details

.configure {|Sc4ry::Config::ConfigMapper| ... } ⇒ Object

class method for specifiying config by block

Examples:

usage

include Sc4ry
Circuits.configure do |spec|
   spec.max_failure_count = 3
end

Yields:



64
65
66
67
68
69
70
# File 'lib/sc4ry/circuits.rb', line 64

def self.configure
  mapper = Sc4ry::Config::ConfigMapper.new(definition: @@config.dup)
  yield(mapper)
  validator = Sc4ry::Config::Validator.new(definition: mapper.config, from: @@config)
  validator.validate!
  @@config = validator.result
end

.control(circuit:, values:) ⇒ Boolean

the private class method to control circuits running status

Parameters:

  • circuit (Symbol)

    the name the circuit to control

  • values (Hash)

    the resut value of a run

Returns:

  • (Boolean)


238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/sc4ry/circuits.rb', line 238

def self.control(circuit:, values:)
  data = @@circuits_store.get key: circuit
  unless data.include? :status
    data[:status] =
      { general: :closed, failure_count: 0, overtime_count: 0, timeout_count: 0 }
  end
  data[:values] = [] unless data.include? :values
  level = [data[:max_failure_count].to_i, data[:max_timeout_count].to_i, data[:max_overtime_count].to_i].max
  data[:values].shift if data[:values].size > level
  data[:values].push values
  worst_status = []
  %w[failure overtime timeout].each do |control|
    if values[control.to_sym] == true
      data[:status]["#{control}_count".to_sym] += 1
    else
      data[:status]["#{control}_count".to_sym] = 0
    end

    case data[:status]["#{control}_count".to_sym]
    when 0
      worst_status.push :closed
    when 1..data["max_#{control}_count".to_sym]
      worst_status.push :half_open
    else
      worst_status.push :open
    end
  end
  save = data[:status][:general]
  %i[closed half_open open].each do |status|
    data[:status][:general] = status if worst_status.include? status
  end
  if save != data[:status][:general]
    raise CircuitBreaked if data[:status][:general] == :open && data[:raise_on_opening]

    if data[:status][:general] == :open
      Sc4ry::Helpers.log level: :error,
                         message: "Circuit #{circuit} : breacking ! "
    end
    if data[:status][:general] == :closed
      Sc4ry::Helpers.log level: :info,
                         message: "Circuit #{circuit} : is now closed"
    end
    Sc4ry::Helpers.notify circuit: circuit, config: data
  end
  @@circuits_store.put key: circuit, value: data
end

.default_configHash

Class method how return de default Sc4ry config

Returns:

  • (Hash)


42
43
44
# File 'lib/sc4ry/circuits.rb', line 42

def self.default_config
  @@config
end

.default_config=(config) ⇒ Object

Deprecated.

use merge_default_config instead

old default config setter

Parameters:

  • config (Hash)


75
76
77
78
79
80
# File 'lib/sc4ry/circuits.rb', line 75

def self.default_config=(config)
  warning_mess = 'DEPRECATED: Circuits.default_config= use Circuits.merge_default_config add: {<config_hash>}'
  Sc4ry::Helpers.log level: :warn,
                     message: warning_mess
  Circuits.merge_default_config(diff: config)
end

.flushtrue, false

class method how flush all circuits in current store

Examples:

usage

include Sc4ry
Circuits.flush

Returns:

  • (true, false)


136
137
138
# File 'lib/sc4ry/circuits.rb', line 136

def self.flush
  @@circuits_store.flush
end

.get(circuit:) ⇒ Hash

class method for get a specific circuit by circuit name

Examples:

usage

include Sc4ry
Circuits.get circuit: :mycircuit

Parameters:

  • circuit (Symbol)

    a circuit name

Returns:

  • (Hash)

    the circuit record in current store included values and status if the circuit have already run.



161
162
163
# File 'lib/sc4ry/circuits.rb', line 161

def self.get(circuit:)
  @@circuits_store.get key: circuit
end

.listArray

class method how list all circuits in current store

Examples:

usage

include Sc4ry
circuits = Circuits.list

Returns:

  • (Array)

    the list of [Symbol] circuits name



127
128
129
# File 'lib/sc4ry/circuits.rb', line 127

def self.list
  @@circuits_store.list
end

.loggersSc4ry::Store

Class method how forward the Logger manager class factory/manager

Returns:



32
33
34
# File 'lib/sc4ry/circuits.rb', line 32

def self.loggers
  @@circuits_loggers
end

.merge_default_config(diff:) ⇒ Object

class method how merge a differential hash to default config

Examples:

usage

include Sc4ry
Circuits.merge_default_config diff: {max_time: 20, notifiers: [:mattermost]}

Parameters:

  • diff (Hash)

    the differential hash config



51
52
53
54
55
# File 'lib/sc4ry/circuits.rb', line 51

def self.merge_default_config(diff:)
  validator = Sc4ry::Config::Validator.new(definition: diff, from: @@config)
  validator.validate!
  @@config = validator.result
end

.notifiersSc4ry::Notifiers

Class method how forward the Notifiers class factory/manager

Returns:



20
21
22
# File 'lib/sc4ry/circuits.rb', line 20

def self.notifiers
  @@circuits_notifiers
end

.register(circuit:, config: {}) {|Sc4ry::Config::ConfigMapper| ... } ⇒ Hash

class method for registering a new circuit, cloud work with a block

Examples:

usage

include Sc4ry
Circuits.register circuit: :mycircuit, config: {raise_on_opening: true, timeout: true}
# or
Circuits.register circuit: :mycircuit do |spec|
  spec.raise_on_opening = true
  spec.timeout = true
end

Parameters:

  • circuit (Symbol)

    a circuit name

  • config (Hash) (defaults to: {})

    a config override on default config for the circuit

Yields:

Returns:

  • (Hash)

    the full config of the circuit after merge on default

Raises:



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/sc4ry/circuits.rb', line 101

def self.register(circuit:, config: {})
  if !config.empty? && block_given?
    raise Sc4ryGenericError,
          'config: keyword must not be defined when block is given'
  end

  if block_given?
    mapper = Sc4ry::Config::ConfigMapper.new(definition: @@config.dup)
    yield(mapper)
    validator = Sc4ry::Config::Validator.new(definition: mapper.config, from: @@config.dup)
  else
    validator = Sc4ry::Config::Validator.new(definition: config, from: @@config.dup)
  end
  validator.validate!
  Sc4ry::Helpers.log level: :debug, message: "Circuit #{circuit} : registered"
  raise Sc4ryGenericError, "Circuit: #{circuit} already exist in store" if @@circuits_store.exist? key: circuit

  @@circuits_store.put key: circuit, value: validator.result
  validator.result
end

.run(circuit: nil) {|Proc| ... } ⇒ Hash

class method for running circuit, need a block

Examples:

usage

include Sc4ry
Circuits.run circuit: :mycircuit do
    #  [...]  your code like a Restclient.get("URL")
end
# or
Circuits.run do
     # [...] your code like a Restclient.get("URL")
     #  running with the first define circuit (use only on a one circuit usage)
end

Parameters:

  • circuit (Symbol) (defaults to: nil)

    a circuit name

Yields:

  • (Proc)

Returns:

  • (Hash)

    a result like “:general=>:open, :failure_count=>X, :overtime_count=>X, :timeout_count=>X”

Raises:

  • (Sc4ryGenericError)

    if circuit already not exit, block is missing or store empty



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/sc4ry/circuits.rb', line 209

def self.run(circuit: nil, &block)
  circuits_list = Circuits.list
  raise Sc4ryGenericError, 'No circuit block given' unless block_given?
  raise Sc4ryGenericError, 'No circuits defined' if circuits_list.empty?

  circuit_name = circuit || circuits_list.first
  raise Sc4ryGenericError, "Circuit #{circuit_name} not found" unless circuits_list.include? circuit_name

  circuit_to_run = Circuits.get circuit: circuit_name
  skip = false
  if circuit_to_run.include?(:status) && (circuit_to_run[:status][:general] == :open)
    @now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    skip = true if (@now - circuit_to_run[:values].last[:end_time]) < circuit_to_run[:check_delay]
  end
  unless skip
    controller = Sc4ry::RunController.new(circuit_to_run)
    Circuits.control circuit: circuit_name, values: controller.run(block: block)
  end
  result = @@circuits_store.get key: circuit_name
  Sc4ry::Helpers.log level: :debug, message: "Circuit #{circuit_name} : status #{result[:status]}"
  result
end

.status(circuit:) ⇒ Symbol

class method for get the status of a specific circuit by circuit name

Examples:

usage

include Sc4ry
Circuits.status circuit: :mycircuit

Parameters:

  • circuit (Symbol)

    a circuit name

Returns:

  • (Symbol)

    status must in [:open,:half_open,:closed,:never_run]



189
190
191
192
# File 'lib/sc4ry/circuits.rb', line 189

def self.status(circuit:)
  data = @@circuits_store.get key: circuit
  data.include?(:status) ? data[:status][:general] : :never_run
end

.storeSc4ry::Store

Class method how forward a Store manager class singleton

Returns:



26
27
28
# File 'lib/sc4ry/circuits.rb', line 26

def self.store
  @@circuits_store
end

.unregister(circuit:) ⇒ true, false

class method for unregistering a circuit

Examples:

usage

include Sc4ry
Circuits.unregister circuit: :mycircuit

Parameters:

  • circuit (Symbol)

    a circuit name

Returns:

  • (true, false)

Raises:



147
148
149
150
151
152
153
# File 'lib/sc4ry/circuits.rb', line 147

def self.unregister(circuit:)
  raise Sc4ryGenericError, "Circuit #{circuit} not found" unless Circuits.list.include? circuit

  @@circuits_store.del key: circuit
  Sc4ry::Helpers.log level: :debug, message: "Circuit #{circuit} : unregistered"
  true
end

.update_config(circuit:, config: { forward_unknown_exceptions: false }) ⇒ Hash

Note:

: important updating config will reset status and values !

class method for update the config of a specific circuit by circuit name

Examples:

usage

include Sc4ry
Circuits.update_config circuit: :mycircuit, config: {}

Parameters:

  • circuit (Symbol)

    a circuit name

  • config (Hash) (defaults to: { forward_unknown_exceptions: false })

    a config hash to merge on current config

Returns:

  • (Hash)

    new config for this circuit

Raises:



173
174
175
176
177
178
179
180
181
# File 'lib/sc4ry/circuits.rb', line 173

def self.update_config(circuit:, config: { forward_unknown_exceptions: false })
  raise Sc4ryGenericError, "Circuit #{circuit} not found" unless Circuits.list.include? circuit

  save = @@circuits_store.get key: circuit
  save.delete_if { |key, _val| %i[status values].include? key }
  Circuits.unregister(circuit: circuit)
  save.merge! config
  Circuits.register circuit: circuit, config: save
end