class Resilient::CircuitBreaker

Attributes

key[R]
open[R]
opened_or_last_checked_at_epoch[R]
properties[R]

Public Class Methods

get(key, properties = nil, registry = nil) click to toggle source

Public: Returns an instance of circuit breaker based on key and registry. Default registry is used if none is provided. If key does not exist, it is registered. If key does exist, it returns registered instance instead of allocating a new instance in order to ensure that state/metrics are the same per key.

See #initialize for docs on key and properties.
# File lib/resilient/circuit_breaker.rb, line 18
def self.get(key, properties = nil, registry = nil)
  key = Key.wrap(key)
  (registry || Registry.default).fetch(key) {
    new(key, properties)
  }
end
new(key, properties = nil) click to toggle source

Private: Builds new instance of a CircuitBreaker.

key - The String or Resilient::Key that determines uniqueness of the
      circuit breaker in the registry and for instrumentation.

properties - The Hash or Resilient::CircuitBreaker::Properties that determine how the
             circuit breaker should behave. Optional. Defaults to new
             Resilient::CircuitBreaker::Properties instance.

Returns CircuitBreaker instance.

# File lib/resilient/circuit_breaker.rb, line 49
def initialize(key, properties = nil)
  raise ArgumentError, "key argument is required" if key.nil?

  @key = Key.wrap(key)
  @properties = Properties.wrap(properties)
  @open = false
  @opened_or_last_checked_at_epoch = 0
end

Public Instance Methods

allow_request?() click to toggle source
# File lib/resilient/circuit_breaker.rb, line 58
def allow_request?
  instrument("resilient.circuit_breaker.allow_request", key: @key) { |payload|
    payload[:result] = if payload[:force_open] = @properties.force_open
      false
    else
      # we still want to simulate normal behavior/metrics like open, allow
      # single request, etc. so it is possible to test properties in
      # production without impact using force_closed so we run these here
      # instead of in the else below
      allow_request = !open? || allow_single_request?

      if payload[:force_closed] = @properties.force_closed
        true
      else
        allow_request
      end
    end
  }
end
failure() click to toggle source
# File lib/resilient/circuit_breaker.rb, line 90
def failure
  instrument("resilient.circuit_breaker.failure", key: @key) { |payload|
    metrics.failure
    nil
  }
end
reset() click to toggle source
# File lib/resilient/circuit_breaker.rb, line 97
def reset
  instrument("resilient.circuit_breaker.reset", key: @key) { |payload|
    @open = false
    @opened_or_last_checked_at_epoch = 0
    metrics.reset
    nil
  }
end
success() click to toggle source
# File lib/resilient/circuit_breaker.rb, line 78
def success
  instrument("resilient.circuit_breaker.success", key: @key) { |payload|
    if @open
      payload[:closed_the_circuit] = true
      close_circuit
    else
      metrics.success
    end
    nil
  }
end

Private Instance Methods

allow_single_request?() click to toggle source
# File lib/resilient/circuit_breaker.rb, line 153
def allow_single_request?
  instrument("resilient.circuit_breaker.allow_single_request", key: @key) { |payload|
    now = Time.now.to_i

    payload[:result] = if @open && now > (@opened_or_last_checked_at_epoch + @properties.sleep_window_seconds)
      @opened_or_last_checked_at_epoch = now
      true
    else
      false
    end
  }
end
close_circuit() click to toggle source
# File lib/resilient/circuit_breaker.rb, line 116
def close_circuit
  instrument("resilient.circuit_breaker.close_circuit", key: @key) { |payload|
    @open = false
    payload[:open] = @open
    payload[:interval] = Time.now.to_i - @opened_or_last_checked_at_epoch
    @opened_or_last_checked_at_epoch = 0
  }
  metrics.reset
end
instrument(name, payload = {}, &block) click to toggle source
# File lib/resilient/circuit_breaker.rb, line 166
def instrument(name, payload = {}, &block)
  properties.instrumenter.instrument(name, payload, &block)
end
open?() click to toggle source
# File lib/resilient/circuit_breaker.rb, line 134
def open?
  instrument("resilient.circuit_breaker.open", key: @key) { |payload|
    payload[:result] = if @open
      true
    else
      if under_request_volume_threshold?
        false
      else
        if under_error_threshold_percentage?
          false
        else
          open_circuit
          true
        end
      end
    end
  }
end
open_circuit() click to toggle source
# File lib/resilient/circuit_breaker.rb, line 108
def open_circuit
  instrument("resilient.circuit_breaker.open_circuit", key: @key) { |payload|
    @opened_or_last_checked_at_epoch = Time.now.to_i
    @open = true
    payload[:open] = @open
  }
end
under_error_threshold_percentage?() click to toggle source
# File lib/resilient/circuit_breaker.rb, line 130
def under_error_threshold_percentage?
  metrics.under_error_threshold_percentage?(@properties.error_threshold_percentage)
end
under_request_volume_threshold?() click to toggle source
# File lib/resilient/circuit_breaker.rb, line 126
def under_request_volume_threshold?
  metrics.under_request_volume_threshold?(@properties.request_volume_threshold)
end