Class: RubyLLM::Agents::CircuitBreaker

Inherits:
Object
  • Object
show all
Includes:
CacheHelper
Defined in:
lib/ruby_llm/agents/infrastructure/circuit_breaker.rb

Overview

Cache-based circuit breaker for protecting against cascading failures

Implements a simple circuit breaker pattern using Rails.cache:

  • Tracks failure counts in a rolling window

  • Opens the breaker when failure threshold is reached

  • Stays open for a cooldown period

  • Automatically closes after cooldown expires

In multi-tenant mode, circuit breakers are isolated per tenant, so one tenant’s failures don’t affect other tenants.

Examples:

Basic usage

breaker = CircuitBreaker.new("MyAgent", "gpt-4o", errors: 10, within: 60, cooldown: 300)
breaker.open?         # => false
breaker.record_failure!
# ... after 10 failures within 60 seconds ...
breaker.open?         # => true

Multi-tenant usage

breaker = CircuitBreaker.new("MyAgent", "gpt-4o", tenant_id: "acme", errors: 10)
breaker.open?  # Isolated to "acme" tenant

See Also:

Constant Summary

Constants included from CacheHelper

RubyLLM::Agents::CacheHelper::NAMESPACE

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CacheHelper

#cache_delete, #cache_exist?, #cache_increment, #cache_key, #cache_read, #cache_store, #cache_write

Constructor Details

#initialize(agent_type, model_id, tenant_id: nil, errors: 10, within: 60, cooldown: 300) ⇒ CircuitBreaker

Returns a new instance of CircuitBreaker.

Parameters:

  • agent_type (String)

    The agent class name

  • model_id (String)

    The model identifier

  • tenant_id (String, nil) (defaults to: nil)

    Optional tenant identifier for multi-tenant isolation

  • errors (Integer) (defaults to: 10)

    Number of errors to trigger open state (default: 10)

  • within (Integer) (defaults to: 60)

    Rolling window in seconds (default: 60)

  • cooldown (Integer) (defaults to: 300)

    Cooldown period in seconds when open (default: 300)



42
43
44
45
46
47
48
49
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 42

def initialize(agent_type, model_id, tenant_id: nil, errors: 10, within: 60, cooldown: 300)
  @agent_type = agent_type
  @model_id = model_id
  @tenant_id = resolve_tenant_id(tenant_id)
  @errors_threshold = errors
  @window_seconds = within
  @cooldown_seconds = cooldown
end

Instance Attribute Details

#agent_typeObject (readonly)



34
35
36
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 34

def agent_type
  @agent_type
end

#cooldown_secondsObject (readonly)



34
35
36
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 34

def cooldown_seconds
  @cooldown_seconds
end

#errors_thresholdObject (readonly)



34
35
36
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 34

def errors_threshold
  @errors_threshold
end

#model_idObject (readonly)



34
35
36
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 34

def model_id
  @model_id
end

#tenant_idObject (readonly)



34
35
36
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 34

def tenant_id
  @tenant_id
end

#window_secondsObject (readonly)



34
35
36
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 34

def window_seconds
  @window_seconds
end

Class Method Details

.from_config(agent_type, model_id, config, tenant_id: nil) ⇒ CircuitBreaker

Creates a CircuitBreaker from a configuration hash

Parameters:

  • agent_type (String)

    The agent class name

  • model_id (String)

    The model identifier

  • config (Hash)

    Configuration with :errors, :within, :cooldown keys

  • tenant_id (String, nil) (defaults to: nil)

    Optional tenant identifier

Returns:



58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 58

def self.from_config(agent_type, model_id, config, tenant_id: nil)
  return nil unless config.is_a?(Hash)

  new(
    agent_type,
    model_id,
    tenant_id: tenant_id,
    errors: config[:errors] || 10,
    within: config[:within] || 60,
    cooldown: config[:cooldown] || 300
  )
end

Instance Method Details

#failure_countInteger

Returns the current failure count

Returns:

  • (Integer)

    The current failure count in the rolling window



120
121
122
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 120

def failure_count
  cache_read(count_key).to_i
end

#open?Boolean

Checks if the circuit breaker is currently open

Returns:

  • (Boolean)

    true if the breaker is open and requests should be blocked



74
75
76
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 74

def open?
  cache_exist?(open_key)
end

#record_failure!Boolean

Records a failed attempt and potentially opens the breaker

Increments the failure counter and checks if the threshold has been reached. If the threshold is exceeded, opens the breaker for the cooldown period.

Returns:

  • (Boolean)

    true if the breaker is now open



84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 84

def record_failure!
  # Increment the failure counter (atomic operation)
  count = increment_failure_count

  # Check if we should open the breaker
  if count >= errors_threshold && !open?
    open_breaker!
    true
  else
    open?
  end
end

#record_success!(reset_counter: true) ⇒ void

This method returns an undefined value.

Records a successful attempt

Optionally resets the failure counter to reduce false positives.

Parameters:

  • reset_counter (Boolean) (defaults to: true)

    Whether to reset the failure counter (default: true)



103
104
105
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 103

def record_success!(reset_counter: true)
  cache_delete(count_key) if reset_counter
end

#reset!void

This method returns an undefined value.

Manually resets the circuit breaker

Clears both the open flag and the failure counter.



112
113
114
115
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 112

def reset!
  cache_delete(open_key)
  cache_delete(count_key)
end

#statusHash

Returns status information for the circuit breaker

Useful for debugging circuit breaker state in rails console.

Returns:

  • (Hash)

    Status information including open state and failure count



140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 140

def status
  {
    agent_type: agent_type,
    model_id: model_id,
    tenant_id: tenant_id,
    open: open?,
    failure_count: failure_count,
    errors_threshold: errors_threshold,
    window_seconds: window_seconds,
    cooldown_seconds: cooldown_seconds
  }
end

#time_until_closeInteger?

Returns the time remaining until the breaker closes

Useful for debugging circuit breaker state in rails console.

Returns:

  • (Integer, nil)

    Seconds until cooldown expires, or nil if not open



129
130
131
132
133
# File 'lib/ruby_llm/agents/infrastructure/circuit_breaker.rb', line 129

def time_until_close
  return nil unless open?

  cooldown_seconds
end