Class: Semian::AdaptiveCircuitBreaker

Inherits:
Object
  • Object
show all
Includes:
CircuitBreakerBehaviour
Defined in:
lib/semian/adaptive_circuit_breaker.rb

Overview

Adaptive Circuit Breaker that uses PID controller for dynamic rejection

Instance Attribute Summary collapse

Attributes included from CircuitBreakerBehaviour

#exceptions, #last_error, #name

Instance Method Summary collapse

Methods included from CircuitBreakerBehaviour

#initialize_behaviour

Constructor Details

#initialize(name:, exceptions:, kp:, ki:, kd:, window_size:, initial_error_rate:, implementation:, sliding_interval:, dead_zone_ratio:, ideal_error_rate_estimator_cap_value:, integral_upper_cap:, integral_lower_cap:) ⇒ AdaptiveCircuitBreaker



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/semian/adaptive_circuit_breaker.rb', line 15

def initialize(name:, exceptions:, kp:, ki:, kd:, window_size:, initial_error_rate:, implementation:,
  sliding_interval:, dead_zone_ratio:, ideal_error_rate_estimator_cap_value:, integral_upper_cap:,
  integral_lower_cap:)
  initialize_behaviour(name: name)

  @exceptions = exceptions
  @stopped = false

  @pid_controller = implementation::PIDController.new(
    kp: kp,
    ki: ki,
    kd: kd,
    window_size: window_size,
    implementation: implementation,
    sliding_interval: sliding_interval,
    initial_error_rate: initial_error_rate,
    dead_zone_ratio: dead_zone_ratio,
    ideal_error_rate_estimator_cap_value: ideal_error_rate_estimator_cap_value,
    integral_upper_cap: integral_upper_cap,
    integral_lower_cap: integral_lower_cap,
  )

  @pid_controller_thread = PIDControllerThread.instance.register_resource(self)
end

Instance Attribute Details

#pid_controllerObject (readonly)

Returns the value of attribute pid_controller.



11
12
13
# File 'lib/semian/adaptive_circuit_breaker.rb', line 11

def pid_controller
  @pid_controller
end

#pid_controller_threadObject (readonly)

Returns the value of attribute pid_controller_thread.



11
12
13
# File 'lib/semian/adaptive_circuit_breaker.rb', line 11

def pid_controller_thread
  @pid_controller_thread
end

#sliding_intervalObject (readonly)

Returns the value of attribute sliding_interval.



11
12
13
# File 'lib/semian/adaptive_circuit_breaker.rb', line 11

def sliding_interval
  @sliding_interval
end

#stoppedObject (readonly)

Returns the value of attribute stopped.



11
12
13
# File 'lib/semian/adaptive_circuit_breaker.rb', line 11

def stopped
  @stopped
end

#update_threadObject (readonly)

Returns the value of attribute update_thread.



11
12
13
# File 'lib/semian/adaptive_circuit_breaker.rb', line 11

def update_thread
  @update_thread
end

Instance Method Details

#acquire(resource = nil, scope: nil, adapter: nil, &block) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/semian/adaptive_circuit_breaker.rb', line 40

def acquire(resource = nil, scope: nil, adapter: nil, &block)
  unless request_allowed?
    mark_rejected(scope:, adapter:)
    raise OpenCircuitError, "Rejected by adaptive circuit breaker"
  end

  result = nil
  begin
    result = block.call
  rescue *@exceptions => error
    if !error.respond_to?(:marks_semian_circuits?) || error.marks_semian_circuits?
      mark_failed(error, scope:, adapter:)
    end
    raise error
  else
    mark_success(scope:, adapter:)
  end
  result
end

#closed?Boolean



83
84
85
# File 'lib/semian/adaptive_circuit_breaker.rb', line 83

def closed?
  @pid_controller.rejection_rate == 0
end

#destroyObject



69
70
71
72
73
# File 'lib/semian/adaptive_circuit_breaker.rb', line 69

def destroy
  @stopped = true
  PIDControllerThread.instance.unregister_resource(self)
  @pid_controller.reset
end

#half_open?Boolean

Compatibility with ProtectedResource - Adaptive circuit breaker does not have a half open state



88
89
90
# File 'lib/semian/adaptive_circuit_breaker.rb', line 88

def half_open?
  !open? && !closed?
end

#in_use?Boolean



109
110
111
# File 'lib/semian/adaptive_circuit_breaker.rb', line 109

def in_use?
  true
end

#mark_failed(error, scope: nil, adapter: nil) ⇒ Object



92
93
94
95
# File 'lib/semian/adaptive_circuit_breaker.rb', line 92

def mark_failed(error, scope: nil, adapter: nil)
  @last_error = error
  @pid_controller.record_request(:error)
end

#mark_rejected(scope: nil, adapter: nil) ⇒ Object



101
102
103
# File 'lib/semian/adaptive_circuit_breaker.rb', line 101

def mark_rejected(scope: nil, adapter: nil)
  @pid_controller.record_request(:rejected)
end

#mark_success(scope: nil, adapter: nil) ⇒ Object



97
98
99
# File 'lib/semian/adaptive_circuit_breaker.rb', line 97

def mark_success(scope: nil, adapter: nil)
  @pid_controller.record_request(:success)
end

#metricsObject



75
76
77
# File 'lib/semian/adaptive_circuit_breaker.rb', line 75

def metrics
  @pid_controller.metrics
end

#open?Boolean



79
80
81
# File 'lib/semian/adaptive_circuit_breaker.rb', line 79

def open?
  @pid_controller.rejection_rate == 1
end

#pid_controller_updateObject



113
114
115
116
# File 'lib/semian/adaptive_circuit_breaker.rb', line 113

def pid_controller_update
  @pid_controller.update
  notify_metrics_update(@pid_controller.metrics(full: false))
end

#request_allowed?Boolean



105
106
107
# File 'lib/semian/adaptive_circuit_breaker.rb', line 105

def request_allowed?
  !@pid_controller.should_reject?
end

#reset(scope: nil, adapter: nil) ⇒ Object



60
61
62
63
# File 'lib/semian/adaptive_circuit_breaker.rb', line 60

def reset(scope: nil, adapter: nil)
  @last_error = nil
  @pid_controller.reset
end

#stopObject



65
66
67
# File 'lib/semian/adaptive_circuit_breaker.rb', line 65

def stop
  destroy
end