Module: BreakerMachines::DSL

Extended by:
ActiveSupport::Concern
Defined in:
lib/breaker_machines/dsl.rb

Overview

DSL module for adding circuit breakers to classes

This module uses WeakRef to track instances that include the DSL. Why? In long-running applications (web servers, background workers), objects that include this DSL may be created and destroyed frequently. Without WeakRef, the registry would hold strong references to these objects, preventing garbage collection and causing memory leaks.

Example scenario: A Rails controller that includes BreakerMachines::DSL is instantiated for each request. Without WeakRef, every controller instance would be kept in memory forever.

Defined Under Namespace

Classes: CircuitBuilder, HedgedBuilder, ParallelFallbackWrapper

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object

Use included callback to add instance tracking



143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/breaker_machines/dsl.rb', line 143

def self.included(base)
  super

  # Hook into new to register instances
  base.singleton_class.prepend(Module.new do
    def new(...)
      instance = super
      instance_registry << WeakRef.new(instance)
      instance
    end
  end)
end

Instance Method Details

#apply_template(circuit_name, template_name) ⇒ Object

Apply a template to an existing or new circuit

Raises:

  • (ArgumentError)


193
194
195
196
197
198
199
# File 'lib/breaker_machines/dsl.rb', line 193

def apply_template(circuit_name, template_name)
  template_config = self.class.circuit_templates[template_name]
  raise ArgumentError, "Template '#{template_name}' not found" unless template_config

  @circuit_instances ||= {}
  @circuit_instances[circuit_name] = Circuit.new(circuit_name, template_config.merge(owner: self))
end

#circuit(name) ⇒ Object



156
157
158
159
160
# File 'lib/breaker_machines/dsl.rb', line 156

def circuit(name)
  self.class.circuits[name] ||= {}
  @circuit_instances ||= {}
  @circuit_instances[name] ||= Circuit.new(name, self.class.circuits[name].merge(owner: self))
end

#circuit_instancesObject

Get all circuit instances for this object



227
228
229
# File 'lib/breaker_machines/dsl.rb', line 227

def circuit_instances
  @circuit_instances || {}
end

#circuits_reportObject

Get detailed information for all circuits



237
238
239
# File 'lib/breaker_machines/dsl.rb', line 237

def circuits_report
  circuit_instances.transform_values(&:to_h)
end

#circuits_summaryObject

Get summary of all circuits for this instance



232
233
234
# File 'lib/breaker_machines/dsl.rb', line 232

def circuits_summary
  circuit_instances.transform_values(&:summary)
end

#cleanup_stale_dynamic_circuits(max_age_seconds = 3600) ⇒ Object

Cleanup stale dynamic circuits (global)



257
258
259
# File 'lib/breaker_machines/dsl.rb', line 257

def cleanup_stale_dynamic_circuits(max_age_seconds = 3600)
  BreakerMachines.registry.cleanup_stale_dynamic_circuits(max_age_seconds)
end

#dynamic_circuit(name, template: nil, global: false, &config_block) ⇒ Object

Create a dynamic circuit breaker with inline configuration Options:

global: true - Store circuit globally, preventing memory leaks in long-lived objects
global: false - Store circuit locally in this instance (default, backward compatible)


166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/breaker_machines/dsl.rb', line 166

def dynamic_circuit(name, template: nil, global: false, &config_block)
  # Start with template config if provided
  base_config = if template && self.class.circuit_templates[template]
                  self.class.circuit_templates[template].deep_dup
                else
                  default_circuit_config
                end

  # Apply additional configuration if block provided
  if config_block
    builder = CircuitBuilder.new
    builder.instance_variable_set(:@config, base_config.deep_dup)
    builder.instance_eval(&config_block)
    base_config = builder.config
  end

  if global
    # Use global registry to prevent memory leaks
    BreakerMachines.registry.get_or_create_dynamic_circuit(name, self, base_config)
  else
    # Local storage (backward compatible)
    @circuit_instances ||= {}
    @circuit_instances[name] ||= Circuit.new(name, base_config.merge(owner: self))
  end
end

#dynamic_circuit_namesObject

Get all dynamic circuit names from global registry



252
253
254
# File 'lib/breaker_machines/dsl.rb', line 252

def dynamic_circuit_names
  BreakerMachines.registry.dynamic_circuit_names
end

#remove_dynamic_circuit(name) ⇒ Object

Remove a global dynamic circuit by name



247
248
249
# File 'lib/breaker_machines/dsl.rb', line 247

def remove_dynamic_circuit(name)
  BreakerMachines.registry.remove_dynamic_circuit(name)
end

#reset_all_circuitsObject

Reset all circuits for this instance



242
243
244
# File 'lib/breaker_machines/dsl.rb', line 242

def reset_all_circuits
  circuit_instances.each_value(&:reset)
end