Class: Semian::DualCircuitBreaker

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

Overview

DualCircuitBreaker wraps both classic and adaptive circuit breakers, allowing runtime switching between them via a callable that determines which to use.

Defined Under Namespace

Modules: SiblingSync Classes: ChildAdaptiveCircuitBreaker, ChildClassicCircuitBreaker

Instance Attribute Summary collapse

Attributes included from CircuitBreakerBehaviour

#exceptions, #name

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CircuitBreakerBehaviour

#initialize_behaviour

Constructor Details

#initialize(name:, classic_circuit_breaker:, adaptive_circuit_breaker:) ⇒ DualCircuitBreaker

use_adaptive should be a callable (Proc/lambda) that returns true/false to determine which circuit breaker to use. If it returns true, use adaptive.



37
38
39
40
41
42
43
44
45
46
47
# File 'lib/semian/dual_circuit_breaker.rb', line 37

def initialize(name:, classic_circuit_breaker:, adaptive_circuit_breaker:)
  initialize_behaviour(name: name)

  @classic_circuit_breaker = classic_circuit_breaker
  @adaptive_circuit_breaker = adaptive_circuit_breaker

  @classic_circuit_breaker.sibling = @adaptive_circuit_breaker
  @adaptive_circuit_breaker.sibling = @classic_circuit_breaker

  @active_circuit_breaker = @classic_circuit_breaker
end

Instance Attribute Details

#active_circuit_breakerObject (readonly)

Returns the value of attribute active_circuit_breaker.



33
34
35
# File 'lib/semian/dual_circuit_breaker.rb', line 33

def active_circuit_breaker
  @active_circuit_breaker
end

#adaptive_circuit_breakerObject (readonly)

Returns the value of attribute adaptive_circuit_breaker.



33
34
35
# File 'lib/semian/dual_circuit_breaker.rb', line 33

def adaptive_circuit_breaker
  @adaptive_circuit_breaker
end

#classic_circuit_breakerObject (readonly)

Returns the value of attribute classic_circuit_breaker.



33
34
35
# File 'lib/semian/dual_circuit_breaker.rb', line 33

def classic_circuit_breaker
  @classic_circuit_breaker
end

Class Method Details

.adaptive_circuit_breaker_selector(selector) ⇒ Object

rubocop:disable Style/ClassMethodsDefinitions



49
50
51
# File 'lib/semian/dual_circuit_breaker.rb', line 49

def self.adaptive_circuit_breaker_selector(selector) # rubocop:disable Style/ClassMethodsDefinitions
  @@adaptive_circuit_breaker_selector = selector # rubocop:disable Style/ClassVars
end

Instance Method Details

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



57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/semian/dual_circuit_breaker.rb', line 57

def acquire(resource = nil, scope: nil, adapter: nil, &block)
  # NOTE: This assignment is not thread-safe, but this is acceptable for now:
  # - Each request gets its own decision based on the selector at that moment
  # - The worst case is a brief inconsistency where a thread reads a stale value,
  #    which just means it uses the previous circuit breaker type for that one request
  old_type = active_breaker_type
  @active_circuit_breaker = get_active_circuit_breaker(resource)
  if old_type != active_breaker_type
    Semian.notify(:circuit_breaker_mode_change, self, nil, nil, old_mode: old_type, new_mode: active_breaker_type)
  end

  @active_circuit_breaker.acquire(resource, scope:, adapter:, &block)
end

#active_breaker_typeObject



53
54
55
# File 'lib/semian/dual_circuit_breaker.rb', line 53

def active_breaker_type
  @active_circuit_breaker.is_a?(Semian::AdaptiveCircuitBreaker) ? :adaptive : :classic
end

#closed?Boolean

Returns:

  • (Boolean)


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

def closed?
  @active_circuit_breaker.closed?
end

#destroyObject



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

def destroy
  @classic_circuit_breaker&.destroy
  @adaptive_circuit_breaker&.destroy
end

#half_open?Boolean

Returns:

  • (Boolean)


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

def half_open?
  @active_circuit_breaker.half_open?
end

#in_use?Boolean

Returns:

  • (Boolean)


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

def in_use?
  @classic_circuit_breaker&.in_use? || @adaptive_circuit_breaker&.in_use?
end

#last_errorObject



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

def last_error
  @active_circuit_breaker.last_error
end

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



87
88
89
# File 'lib/semian/dual_circuit_breaker.rb', line 87

def mark_failed(error, scope: nil, adapter: nil)
  @active_circuit_breaker&.mark_failed(error, scope: nil, adapter: nil)
end

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



91
92
93
# File 'lib/semian/dual_circuit_breaker.rb', line 91

def mark_success(scope: nil, adapter: nil)
  @active_circuit_breaker&.mark_success(scope: nil, adapter: nil)
end

#metricsObject



117
118
119
120
121
122
123
# File 'lib/semian/dual_circuit_breaker.rb', line 117

def metrics
  {
    active: active_breaker_type,
    classic: classic_metrics,
    adaptive: adaptive_metrics,
  }
end

#open?Boolean

Returns:

  • (Boolean)


71
72
73
# File 'lib/semian/dual_circuit_breaker.rb', line 71

def open?
  @active_circuit_breaker.open?
end

#request_allowed?Boolean

Returns:

  • (Boolean)


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

def request_allowed?
  @active_circuit_breaker.request_allowed?
end

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



99
100
101
102
# File 'lib/semian/dual_circuit_breaker.rb', line 99

def reset(scope: nil, adapter: nil)
  @classic_circuit_breaker&.reset(scope:, adapter:)
  @adaptive_circuit_breaker&.reset(scope:, adapter:)
end

#stopObject



95
96
97
# File 'lib/semian/dual_circuit_breaker.rb', line 95

def stop
  @adaptive_circuit_breaker&.stop
end