Class: HTTPigeon::CircuitBreaker::Fuse

Inherits:
Object
  • Object
show all
Defined in:
lib/httpigeon/circuit_breaker/fuse.rb

Constant Summary collapse

STATE_OPEN =
'open'.freeze
STATE_HALF_OPEN =
'half_open'.freeze
STATE_CLOSED =
'closed'.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Fuse

Returns a new instance of Fuse.



19
20
21
22
23
24
25
26
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 19

def initialize(config)
  @config = config
  @service_id = config.service_id.to_s
  @store = CircuitBreaker::MemoryStore.new(config.sample_window)
  @open_storage_key = "circuit:#{service_id}:#{STATE_OPEN}"
  @half_open_storage_key = "circuit:#{service_id}:#{STATE_HALF_OPEN}"
  @state_change_syncer = Mutex.new
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



17
18
19
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 17

def config
  @config
end

#service_idObject (readonly)

Returns the value of attribute service_id.



17
18
19
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 17

def service_id
  @service_id
end

#storeObject (readonly)

Returns the value of attribute store.



17
18
19
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 17

def store
  @store
end

Class Method Details

.from_options(options) ⇒ Object



13
14
15
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 13

def self.from_options(options)
  new(FuseConfig.new(options))
end

Instance Method Details

#execute(request_id: nil) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 28

def execute(request_id: nil)
  @request_id = request_id

  if open?
    record_tripped!

    config.open_circuit_handler.call(config.null_response, config.circuit_open_error)
  else
    begin
      response = yield
      server_maintenance_timeout = response.headers[config.maintenance_mode_header].to_i

      if server_maintenance_timeout.positive?
        record_failure!
        open!(
          {
            expires_in: server_maintenance_timeout,
            # for logging purposes. can't log expires_in because it might be overridden if greater than max
            server_maintenance_timeout: server_maintenance_timeout
          }
        )

        return config.open_circuit_handler.call(response, config.circuit_open_error)
      end

      record_success!
      response
    rescue Faraday::Error => e
      record_failure! if e.response_status >= 500 || config.error_codes_watchlist.include?(e.response_status)

      raise e
    end
  end
end

#failure_countObject



71
72
73
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 71

def failure_count
  store.get(stat_storage_key(:failure)).to_i
end

#failure_rateObject



83
84
85
86
87
88
89
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 83

def failure_rate
  total_stats = success_count + failure_count + tripped_count

  return 0.0 unless total_stats.positive?

  (total_stats - success_count).to_f / total_stats
end

#half_open?Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 67

def half_open?
  store.key?(half_open_storage_key)
end

#open?Boolean

Returns:

  • (Boolean)


63
64
65
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 63

def open?
  store.key?(open_storage_key)
end

#reset!Object



91
92
93
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 91

def reset!
  state_change_syncer.synchronize { store.reset! }
end

#success_countObject



75
76
77
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 75

def success_count
  store.get(stat_storage_key(:success)).to_i
end

#tripped_countObject



79
80
81
# File 'lib/httpigeon/circuit_breaker/fuse.rb', line 79

def tripped_count
  store.get(stat_storage_key(:tripped)).to_i
end