Class: Fizzy::CircuitBreaker

Inherits:
Object
  • Object
show all
Defined in:
lib/fizzy/circuit_breaker.rb

Overview

Circuit breaker pattern for fault tolerance.

Tracks consecutive failures and opens the circuit when the threshold is reached, preventing further requests until the recovery timeout expires.

States:

  • :closed – normal operation, requests flow through

  • :open – circuit tripped, requests fail immediately

  • :half_open – recovery probe, single request allowed through

Examples:

breaker = Fizzy::CircuitBreaker.new(threshold: 5, timeout: 30)
breaker.call { http.get("/boards") }

Instance Method Summary collapse

Constructor Details

#initialize(threshold: 5, timeout: 30) ⇒ CircuitBreaker

Returns a new instance of CircuitBreaker.

Parameters:

  • threshold (Integer) (defaults to: 5)

    consecutive failures before opening

  • timeout (Numeric) (defaults to: 30)

    seconds to wait before half-open probe



20
21
22
23
24
25
26
27
# File 'lib/fizzy/circuit_breaker.rb', line 20

def initialize(threshold: 5, timeout: 30)
  @threshold = threshold
  @timeout = timeout
  @failure_count = 0
  @last_failure_at = nil
  @state = :closed
  @mutex = Mutex.new
end

Instance Method Details

#call { ... } ⇒ Object

Executes the block through the circuit breaker.

Yields:

  • the operation to protect

Returns:

  • the result of the block

Raises:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/fizzy/circuit_breaker.rb', line 39

def call
  half_open_probe = false

  @mutex.synchronize do
    case effective_state
    when :open
      raise Fizzy::APIError.new(
        "Circuit breaker is open",
        retryable: true,
        hint: "Service appears unavailable, will retry after #{@timeout}s"
      )
    when :half_open
      half_open_probe = true
      @state = :half_open
    end
  end

  if half_open_probe
    # Single-probe: hold the probe flag so concurrent callers see :half_open
    # and block (they'll see :open until this probe completes).
    @mutex.synchronize { @state = :open }
    begin
      result = yield
      record_success
      return result
    rescue Fizzy::NetworkError, Fizzy::APIError => e
      record_failure if e.retryable?
      raise
    end
  end

  begin
    result = yield
    record_success
    result
  rescue Fizzy::NetworkError, Fizzy::APIError => e
    record_failure if e.retryable?
    raise
  end
end

#resetObject

Resets the circuit breaker to closed state.



81
82
83
84
85
86
87
# File 'lib/fizzy/circuit_breaker.rb', line 81

def reset
  @mutex.synchronize do
    @failure_count = 0
    @last_failure_at = nil
    @state = :closed
  end
end

#stateSymbol

Returns current circuit state (:closed, :open, :half_open).

Returns:

  • (Symbol)

    current circuit state (:closed, :open, :half_open)



30
31
32
# File 'lib/fizzy/circuit_breaker.rb', line 30

def state
  @mutex.synchronize { effective_state }
end