class Expeditor::Service

Attributes

executor[R]
fallback_enabled[RW]

Public Class Methods

new(opts = {}) click to toggle source
# File lib/expeditor/service.rb, line 8
def initialize(opts = {})
  @mutex = Mutex.new
  @executor = opts.fetch(:executor) { Concurrent::ThreadPoolExecutor.new }
  @threshold = opts.fetch(:threshold, 0.5)
  @non_break_count = opts.fetch(:non_break_count, 20)
  @sleep = opts.fetch(:sleep, 1)
  granularity = 10
  @rolling_number_opts = {
    size: granularity,
    per_time: opts.fetch(:period, 10).to_f / granularity
  }
  reset_status!
  @fallback_enabled = true
end

Public Instance Methods

break() click to toggle source
# File lib/expeditor/service.rb, line 39
def break
  @rolling_number.increment :break
end
breaking?() click to toggle source
# File lib/expeditor/service.rb, line 51
def breaking?
  @breaking
end
current_status() click to toggle source

@deprecated Use `#status` instead.

# File lib/expeditor/service.rb, line 104
def current_status
  warn 'Expeditor::Service#current_status is deprecated. Please use #status instead.'
  @rolling_number.current
end
dependency() click to toggle source
# File lib/expeditor/service.rb, line 43
def dependency
  @rolling_number.increment :dependency
end
failure() click to toggle source
# File lib/expeditor/service.rb, line 27
def failure
  @rolling_number.increment :failure
end
fallback_enabled?() click to toggle source
# File lib/expeditor/service.rb, line 47
def fallback_enabled?
  !!fallback_enabled
end
rejection() click to toggle source
# File lib/expeditor/service.rb, line 31
def rejection
  @rolling_number.increment :rejection
end
reset_status!() click to toggle source
# File lib/expeditor/service.rb, line 109
def reset_status!
  @mutex.synchronize do
    @rolling_number = Expeditor::RollingNumber.new(@rolling_number_opts)
    @breaking = false
    @break_start = nil
  end
end
run_if_allowed() { || ... } click to toggle source

Run given block when the request is allowed, otherwise raise Expeditor::CircuitBreakError. When breaking and sleep time was passed, the circuit breaker tries to close the circuit. So subsequent single command execution is allowed (will not be breaked) to check the service is healthy or not. The circuit breaker only allows one request so other subsequent requests will be aborted with CircuitBreakError. When the test request succeeds, the circuit breaker resets the service status and closes the circuit.

# File lib/expeditor/service.rb, line 63
def run_if_allowed
  if @breaking
    now = Time.now

    # Only one thread can be allowed to execute single request when half-opened.
    allow_single_request = false
    @mutex.synchronize do
      allow_single_request = now - @break_start > @sleep
      @break_start = now if allow_single_request
    end

    if allow_single_request
      result = yield # This can be raise exception.
      # The execution succeed, then
      reset_status!
      result
    else
      raise CircuitBreakError
    end
  else
    open = calc_open
    if open
      change_state(true, Time.now)
      raise CircuitBreakError
    else
      yield
    end
  end
end
shutdown() click to toggle source

shutdown thread pool after shutdown, if you create thread, RejectedExecutionError is raised.

# File lib/expeditor/service.rb, line 95
def shutdown
  @executor.shutdown
end
status() click to toggle source
# File lib/expeditor/service.rb, line 99
def status
  @rolling_number.total
end
success() click to toggle source
# File lib/expeditor/service.rb, line 23
def success
  @rolling_number.increment :success
end
timeout() click to toggle source
# File lib/expeditor/service.rb, line 35
def timeout
  @rolling_number.increment :timeout
end

Private Instance Methods

calc_open() click to toggle source
# File lib/expeditor/service.rb, line 119
def calc_open
  s = @rolling_number.total
  total_count = s.success + s.failure + s.timeout
  if total_count >= [@non_break_count, 1].max
    failure_count = s.failure + s.timeout
    failure_count.to_f / total_count.to_f >= @threshold
  else
    false
  end
end
change_state(breaking, break_start) click to toggle source
# File lib/expeditor/service.rb, line 130
def change_state(breaking, break_start)
  @mutex.synchronize do
    @breaking = breaking
    @break_start = break_start
  end
end