class Supervision::CircuitControl

A class responsible for controling state of the circuit

Constants

MAX_THREAD_LIFETIME

Attributes

config[R]

The circuit configuration

@api private

monitor[R]

The circuit performance monitor

@api private

scheduler[R]

The reset timeout scheduler

@api private

Public Class Methods

new(options = {}) click to toggle source

Create a circuit control

@param [Hash] options

@api public

# File lib/supervision/circuit_control.rb, line 33
def initialize(options = {})
  @config            = Configuration.new(options)
  @failure_count     = Atomic.new(0)
  @last_failure_time = Atomic.new
  @lock              = Mutex.new
  @monitor           = CircuitMonitor.new
  fsm
end

Public Instance Methods

fail_fast!() click to toggle source

Fail fast on any call

@raise [CircuitBreakerOpenError]

@api private

# File lib/supervision/circuit_control.rb, line 116
def fail_fast!
  monitor.measure(:open_circuit)
  raise CircuitBreakerOpenError
end
failure_count() click to toggle source

Total failure count for current circuit

@return [Integer]

@api public

# File lib/supervision/circuit_control.rb, line 87
def failure_count
  @failure_count.value
end
failure_count_exceeded?() click to toggle source

@return [Boolean]

@api private

# File lib/supervision/circuit_control.rb, line 124
def failure_count_exceeded?
  failure_count > @config.max_failures
end
fsm() click to toggle source

Creates internal finite state machine to transitions through three states :closed, :open and :half_open.

@return [FiniteMachine]

@api private

# File lib/supervision/circuit_control.rb, line 49
def fsm
  context = self
  @fsm ||= FiniteMachine.define do
    initial :closed

    target context

    events do
      event :trip,         [:closed, :half_open] => :open
      event :attempt_reset, :open                => :half_open
      event :reset,         :half_open           => :closed
    end

    callbacks do
      on_enter :closed do |event|
        reset_failure
      end

      on_enter :open do |event|
        measure_timeout
        fail_fast!
      end

      on_enter :half_open do |event|
        monitor.measure(:half_open_circuit)
      end
    end
  end
end
handle_failure(error = nil) click to toggle source

Handler exception

@api public

# File lib/supervision/circuit_control.rb, line 151
def handle_failure(error = nil)
  fail_fast! if fsm.open?
  record_failure
  monitor.record_failure
  trip if failure_count_exceeded? || fsm.half_open?
end
last_failure_time() click to toggle source

Last time failure occured

@return [Time]

@api public

# File lib/supervision/circuit_control.rb, line 96
def last_failure_time
  @last_failure_time.value
end
max_thread_lifetime() click to toggle source

Estimate maximum duration the scheduling thread should live

@return [Time]

@api private

# File lib/supervision/circuit_control.rb, line 220
def max_thread_lifetime
  @config.reset_timeout + 100.milli
end
measure_timeout() click to toggle source

Measure remaining timeout

@api private

# File lib/supervision/circuit_control.rb, line 188
def measure_timeout
  @scheduler = Thread.new do
    Thread.current.abort_on_exception = true
    thread = Thread.current
    thread[:created_at] = Time.now
    @lock.synchronize do
      run_loop(thread)
    end
  end
end
record_failure() click to toggle source

Record failure count

@api public

# File lib/supervision/circuit_control.rb, line 170
def record_failure
  if fsm.closed? || fsm.half_open?
    @failure_count.update { |v| v + 1 }
    @last_failure_time.value = Time.now
  end
end
record_success() click to toggle source

Record successful call

@api public

# File lib/supervision/circuit_control.rb, line 161
def record_success
  reset if fsm.half_open?
  reset_failure
  monitor.record_success
end
reset!() click to toggle source

Force closed state and reset failure statistics

@return [nil]

@api public

# File lib/supervision/circuit_control.rb, line 105
def reset!
  fsm.reset!
  reset_failure
  throw(:terminate) if @scheduler && @scheduler.alive?
end
reset_failure() click to toggle source

Resets failure count

@api public

# File lib/supervision/circuit_control.rb, line 180
def reset_failure
  @failure_count.value = 0
  @last_failure_time.value = nil
end
run_loop(thread) click to toggle source

Run scheduler loop

@api private

# File lib/supervision/circuit_control.rb, line 202
def run_loop(thread)
  catch(:terminate) do
    loop do
      if tripped?
        attempt_reset && break
      elsif Time.now - thread[:created_at] > max_thread_lifetime
        thread.kill if thread.alive?
        break
      end
    end
  end
end
timeout_exceeded?() click to toggle source

Check if remaining duration until reset has been exceeded

@return [Boolean]

whether or not the breaker will attempt a reset by transitioning
to :half_open state

@api private

# File lib/supervision/circuit_control.rb, line 142
def timeout_exceeded?
  return false unless last_failure_time
  timeout = Time.now - last_failure_time
  timeout > @config.reset_timeout
end
tripped?() click to toggle source

@return [Boolean]

@api private

# File lib/supervision/circuit_control.rb, line 131
def tripped?
  fsm.open? && timeout_exceeded?
end