### Getting Started with BreakerMachines Development Source: https://github.com/seuros/breaker_machines/blob/master/CONTRIBUTING.md Provides the essential bash commands to set up the BreakerMachines development environment. This includes cloning the repository, navigating into the project directory, installing Ruby dependencies using Bundler, and running the test suite to verify the setup. ```bash git clone https://github.com/yourusername/breaker_machines.git cd breaker_machines bundle install bundle exec rake test ``` -------------------------------- ### Configure BreakerMachines and Use Async Storage with Falcon Server Source: https://github.com/seuros/breaker_machines/blob/master/docs/ASYNC_STORAGE_EXAMPLES.md This example demonstrates how to integrate `AsyncRedisCircuitStorage` with a Falcon server. It configures BreakerMachines for `fiber_safe` mode, defines a circuit breaker using the DSL, and shows how to wrap an asynchronous HTTP call within the circuit. The Falcon app then uses this API to fetch data, ensuring non-blocking operations. ```Ruby # config.ru require 'falcon' require 'async' require 'breaker_machines' require_relative 'lib/async_redis_circuit_storage' # Configure BreakerMachines for fiber-safe mode BreakerMachines.configure do |config| config.fiber_safe = true config.default_storage = AsyncRedisCircuitStorage.new end class MyAPI include BreakerMachines::DSL circuit :external_api, fiber_safe: true do threshold failures: 5, within: 60 reset_after 30 timeout 3 # Safe cooperative timeout in fiber mode! fallback do |error| { error: "Service temporarily unavailable", cached: true } end on_open do # This can also be async Async do # Send alert to monitoring service AsyncHTTP::Internet.new.post( 'https://monitoring.example.com/alerts', { circuit: 'external_api', status: 'open' } ) end end end def fetch_data circuit(:external_api).wrap do # This returns an Async::Task when called within Falcon Async::HTTP::Internet.new.get('https://api.example.com/data') end end end # Falcon app run lambda { |env| Async do api = MyAPI.new result = api.fetch_data [200, {'Content-Type' => 'application/json'}, [result.to_json]] end.wait } ``` -------------------------------- ### Install BreakerMachines Gem Dependencies Source: https://github.com/seuros/breaker_machines/blob/master/README.md Provides the command-line instruction to install the BreakerMachines gem and its dependencies after adding it to the Gemfile. ```bash $ bundle install ``` -------------------------------- ### Async PostgreSQL Storage Gem Dependency Source: https://github.com/seuros/breaker_machines/blob/master/docs/ASYNC_STORAGE_EXAMPLES.md This snippet indicates the gem dependency for implementing an asynchronous PostgreSQL storage backend for BreakerMachines, using the `async-postgres` gem. ```Ruby # Gemfile gem 'async-postgres' ``` -------------------------------- ### APIDOC: AsyncPostgresCircuitStorage Class Reference Source: https://github.com/seuros/breaker_machines/blob/master/docs/ASYNC_STORAGE_EXAMPLES.md Detailed API reference for the `AsyncPostgresCircuitStorage` class, outlining its constructor, public methods for managing circuit breaker states, and internal helper methods. This class integrates with `BreakerMachines` to provide persistent, asynchronous storage. ```APIDOC class AsyncPostgresCircuitStorage < BreakerMachines::Storage::Base # Constructor initialize(connection_string: ENV['DATABASE_URL']) connection_string: String - PostgreSQL connection string. Defaults to ENV['DATABASE_URL']. # Public Methods get_status(circuit_name) circuit_name: String - The name of the circuit breaker. Returns: Hash or nil - A hash containing circuit status (:status, :opened_at, :failure_count, :success_count, :last_failure_at) or nil if not found. set_status(circuit_name, status, opened_at = nil) circuit_name: String - The name of the circuit breaker. status: Symbol - The new status of the circuit (:closed, :open, :half_open). opened_at: Float, optional - Unix timestamp when the circuit was opened. record_failure(circuit_name, duration = nil) circuit_name: String - The name of the circuit breaker. duration: Numeric, optional - Duration of the operation (not used in this implementation for storage logic). record_success(circuit_name, duration = nil) circuit_name: String - The name of the circuit breaker. duration: Numeric, optional - Duration of the operation (not used in this implementation for storage logic). # Private Methods ensure_table_exists() Ensures the 'circuit_breaker_states' table exists in the database. ``` -------------------------------- ### SQL: Circuit Breaker States Table Schema Source: https://github.com/seuros/breaker_machines/blob/master/docs/ASYNC_STORAGE_EXAMPLES.md This SQL DDL defines the `circuit_breaker_states` table, which stores the state of circuit breakers including their name, current status, failure/success counts, and timestamps. It also includes an index for efficient updates. ```SQL CREATE TABLE IF NOT EXISTS circuit_breaker_states ( circuit_name VARCHAR(255) PRIMARY KEY, status VARCHAR(50) NOT NULL DEFAULT 'closed', opened_at TIMESTAMP, failure_count INTEGER DEFAULT 0, success_count INTEGER DEFAULT 0, last_failure_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_circuit_breaker_updated_at ON circuit_breaker_states(updated_at); ``` -------------------------------- ### Install BreakerMachines Gem Source: https://github.com/seuros/breaker_machines/blob/master/README.md Instructions to add the BreakerMachines gem to a Ruby project's Gemfile for dependency management, enabling its use within the application. ```bash gem 'breaker_machines' ``` -------------------------------- ### Initialize and Call a BreakerMachines Circuit Source: https://github.com/seuros/breaker_machines/blob/master/sig/README.md This example illustrates the basic usage of `BreakerMachines::Circuit`. It shows how to create a new circuit instance with specific failure threshold and reset timeout, and then how to execute a block of code through the circuit using the `call` method. ```ruby circuit = BreakerMachines::Circuit.new("api", failure_threshold: 5, reset_timeout: 30 ) result = circuit.call { api.fetch_data } ``` -------------------------------- ### Ruby: AsyncPostgresCircuitStorage Class Implementation Source: https://github.com/seuros/breaker_machines/blob/master/docs/ASYNC_STORAGE_EXAMPLES.md This Ruby class implements `BreakerMachines::Storage::Base` to provide an asynchronous PostgreSQL storage solution for circuit breaker states. It handles database connection, retrieving circuit status, and updating states for failures, successes, and general status changes using `async/postgres`. ```Ruby require 'async/postgres' class AsyncPostgresCircuitStorage < BreakerMachines::Storage::Base def initialize(connection_string: ENV['DATABASE_URL']) @connection = Async::Postgres.connect(connection_string) ensure_table_exists end def get_status(circuit_name) result = @connection.exec_params( 'SELECT * FROM circuit_breaker_states WHERE circuit_name = $1', [circuit_name] ).wait return nil if result.ntuples == 0 row = result.first { status: row['status'].to_sym, opened_at: row['opened_at']&.to_f, failure_count: row['failure_count'].to_i, success_count: row['success_count'].to_i, last_failure_at: row['last_failure_at']&.to_f } end def set_status(circuit_name, status, opened_at = nil) @connection.exec_params( "INSERT INTO circuit_breaker_states\n (circuit_name, status, opened_at, updated_at)\n VALUES ($1, $2, $3, NOW())\n ON CONFLICT (circuit_name)\n DO UPDATE SET\n status = EXCLUDED.status,\n opened_at = EXCLUDED.opened_at,\n updated_at = EXCLUDED.updated_at", [circuit_name, status.to_s, opened_at ? Time.at(opened_at) : nil] ).wait end def record_failure(circuit_name, duration = nil) @connection.exec_params( "INSERT INTO circuit_breaker_states\n (circuit_name, failure_count, last_failure_at, updated_at)\n VALUES ($1, 1, NOW(), NOW())\n ON CONFLICT (circuit_name)\n DO UPDATE SET\n failure_count = circuit_breaker_states.failure_count + 1,\n last_failure_at = NOW(),\n updated_at = NOW()", [circuit_name] ).wait end def record_success(circuit_name, duration = nil) @connection.exec_params( "INSERT INTO circuit_breaker_states\n (circuit_name, success_count, updated_at)\n VALUES ($1, 1, NOW())\n ON CONFLICT (circuit_name)\n DO UPDATE SET\n success_count = circuit_breaker_states.success_count + 1,\n updated_at = NOW()", [circuit_name] ).wait end private def ensure_table_exists @connection.exec("CREATE TABLE IF NOT EXISTS circuit_breaker_states (\n circuit_name VARCHAR(255) PRIMARY KEY,\n status VARCHAR(50) NOT NULL DEFAULT 'closed',\n opened_at TIMESTAMP,\n failure_count INTEGER DEFAULT 0,\n success_count INTEGER DEFAULT 0,\n last_failure_at TIMESTAMP,\n created_at TIMESTAMP DEFAULT NOW(),\n updated_at TIMESTAMP DEFAULT NOW()\n );\n\n CREATE INDEX IF NOT EXISTS idx_circuit_breaker_updated_at\n ON circuit_breaker_states(updated_at);\n ").wait end end ``` -------------------------------- ### Install BreakerMachines Gem Source: https://github.com/seuros/breaker_machines/blob/master/README.md This snippet provides the necessary line to add the BreakerMachines gem to a Ruby project's Gemfile, making the circuit breaker functionality available for use. ```bash gem 'breaker_machines' ``` -------------------------------- ### Configure Half-Open State for Circuit Breakers Source: https://github.com/seuros/breaker_machines/blob/master/README.md This example focuses on the 'Half-Open' state of a circuit breaker, demonstrating how to configure `half_open_requests` to test service recovery. It also shows the use of `on_half_open` and `on_close` callbacks for logging and reacting to state transitions. ```Ruby circuit :quantum_stabilizer do threshold failures: 3, within: 60 reset_after 30 half_open_requests 3 # Test with caution on_half_open do whisper_to_logs("Testing quantum stabilizer... nobody breathe...") end on_close do celebrate("Quantum stabilizer online! Reality is stable!") end end ``` -------------------------------- ### Ruby: RSpec Tests for Async Circuit Storage Source: https://github.com/seuros/breaker_machines/blob/master/docs/ASYNC_STORAGE_EXAMPLES.md These RSpec tests demonstrate how to verify the functionality of an asynchronous circuit breaker storage implementation. They use `async/rspec` to test failure recording and concurrent operations, ensuring non-blocking behavior and correct state management under load. ```Ruby require 'async/rspec' RSpec.describe AsyncRedisCircuitStorage do include Async::RSpec let(:storage) { described_class.new } it "records failures without blocking" do # This test runs in an async context storage.record_failure('test_circuit', 0.5) status = storage.get_status('test_circuit') expect(status[:failure_count]).to eq(1) end it "handles concurrent operations" do # Run 100 concurrent operations tasks = 100.times.map do Async do storage.record_failure('concurrent_test') end end # Wait for all to complete tasks.each(&:wait) status = storage.get_status('concurrent_test') expect(status[:failure_count]).to eq(100) end end ``` -------------------------------- ### Install Breaker Machines Gem Source: https://github.com/seuros/breaker_machines/blob/master/README.md This snippet shows how to add the 'breaker_machines' gem to a Ruby project using Bundler. It's a standard command-line instruction for managing project dependencies. ```bash $ bundle add breaker_machines ``` -------------------------------- ### Implement Async Redis Circuit Storage for BreakerMachines Source: https://github.com/seuros/breaker_machines/blob/master/docs/ASYNC_STORAGE_EXAMPLES.md This Ruby class, `AsyncRedisCircuitStorage`, extends `BreakerMachines::Storage::Base` to provide a non-blocking Redis backend for circuit breaker status. It uses the `async-redis` gem to ensure all Redis operations are asynchronous, suitable for `fiber_safe` mode. It manages circuit status, failure/success counts, and provides methods for recording and resetting circuit states. ```Ruby # Gemfile gem 'async-redis' ``` ```Ruby # lib/async_redis_circuit_storage.rb require 'async/redis' class AsyncRedisCircuitStorage < BreakerMachines::Storage::Base def initialize(redis_url: ENV['REDIS_URL'], prefix: 'circuit_breaker:') @client = Async::Redis::Client.new(Async::Redis.parse_url(redis_url)) @prefix = prefix end def get_status(circuit_name) key = "#{@prefix}#{circuit_name}" # All Redis operations are non-blocking in async context data = @client.hgetall(key).wait return nil if data.empty? { status: data['status']&.to_sym, opened_at: data['opened_at']&.to_f, failure_count: data['failure_count']&.to_i || 0, success_count: data['success_count']&.to_i || 0, last_failure_at: data['last_failure_at']&.to_f } end def set_status(circuit_name, status, opened_at = nil) key = "#{@prefix}#{circuit_name}" @client.multi do |transaction| transaction.hset(key, 'status', status.to_s) transaction.hset(key, 'opened_at', opened_at) if opened_at transaction.expire(key, 3600) # Auto-cleanup after 1 hour end.wait end def record_failure(circuit_name, duration = nil) key = "#{@prefix}#{circuit_name}" @client.multi do |transaction| transaction.hincrby(key, 'failure_count', 1) transaction.hset(key, 'last_failure_at', Time.now.to_f) transaction.hset(key, 'last_failure_duration', duration) if duration end.wait end def record_success(circuit_name, duration = nil) key = "#{@prefix}#{circuit_name}" @client.multi do |transaction| transaction.hincrby(key, 'success_count', 1) transaction.hset(key, 'last_success_duration', duration) if duration end.wait end def failure_count(circuit_name, window = nil) if window # For windowed counts, use Redis sorted sets score_key = "#{@prefix}#{circuit_name}:failures" min_score = Time.now.to_f - window @client.zcount(score_key, min_score, '+inf').wait else get_status(circuit_name)[:failure_count] || 0 end end def success_count(circuit_name, window = nil) if window score_key = "#{@prefix}#{circuit_name}:successes" min_score = Time.now.to_f - window @client.zcount(score_key, min_score, '+inf').wait else get_status(circuit_name)[:success_count] || 0 end end def reset(circuit_name) @client.del("#{@prefix}#{circuit_name}").wait end def close @client.close end end ``` -------------------------------- ### Configuring Async Storage Backend for BreakerMachines Source: https://github.com/seuros/breaker_machines/blob/master/README.md This snippet illustrates how to create a custom async-compatible storage backend for BreakerMachines, extending `BreakerMachines::Storage::Base`. It provides an example using `Async::Redis::Client` for non-blocking Redis operations and shows how to configure this custom storage as the default for BreakerMachines, crucial for maintaining full async behavior. ```Ruby class AsyncRedisStorage < BreakerMachines::Storage::Base def initialize @client = Async::Redis::Client.new end def record_failure(circuit_name, duration = nil) # Non-blocking Redis operation @client.hincrby("circuit:#{circuit_name}", 'failures', 1).wait end end BreakerMachines.configure do |config| config.fiber_safe = true config.default_storage = AsyncRedisStorage.new end ``` -------------------------------- ### Ruby: Configuring Null Storage for Circuit Breakers Source: https://github.com/seuros/breaker_machines/blob/master/README.md This example demonstrates how to configure 'null' storage for circuit breakers, either globally or per-circuit. Null storage provides maximum performance by disabling metrics and event logging, making it suitable for performance-critical paths or when external monitoring is already in place. ```ruby # Global configuration BreakerMachines.configure do |config| config.default_storage = :null end # Or per-circuit circuit :external_api do storage :null # No overhead, just protection threshold failures: 5, within: 60 end ``` -------------------------------- ### Implementing an AI Service with Fiber-Safe Circuit Breaker Source: https://github.com/seuros/breaker_machines/blob/master/README.md This example showcases a Ruby class `AIService` that integrates BreakerMachines with `fiber_safe` mode. It demonstrates defining a circuit with cooperative timeouts and an async fallback block, wrapping an `Async::HTTP` call to an OpenAI API, ensuring non-blocking behavior and graceful degradation for external service interactions. ```Ruby class AIService include BreakerMachines::DSL circuit :gpt4, fiber_safe: true do threshold failures: 2, within: 30 timeout 10 # Cooperative timeout - won't corrupt state! fallback do |error| # Fallback can also be async Async do # Try a cheaper model openai.completions(model: 'gpt-3.5-turbo', prompt: @prompt) end end end def generate_response(prompt) @prompt = prompt circuit(:gpt4).wrap do # Returns an Async::Task in Falcon Async::HTTP::Internet.new.post( 'https://api.openai.com/v1/completions', headers: { 'Authorization' => "Bearer #{api_key}" }, body: { model: 'o42-av', prompt: prompt }.to_json ) end end end ``` -------------------------------- ### Ruby: Configuring Breaker Machines Circuit Breaker Storage Options Source: https://github.com/seuros/breaker_machines/blob/master/README.md This snippet shows how to configure the default storage mechanism for Breaker Machines circuit breakers. It provides examples for in-memory, null, and Redis-backed storage, allowing users to choose the appropriate persistence strategy based on their performance and distributed system requirements. ```ruby BreakerMachines.configure do |config| # Default: Efficient sliding window with event tracking config.default_storage = :bucket_memory # Alternative: Simple in-memory storage config.default_storage = :memory # Minimal overhead: No metrics or logging config.default_storage = :null # Or use Redis for distributed state config.default_storage = RedisCircuitStorage.new end ``` -------------------------------- ### Ruby: Redis Client with Command Timeout Source: https://github.com/seuros/breaker_machines/blob/master/README.md Shows how to apply a circuit breaker to Redis operations while ensuring safe timeout management. This example uses the Redis client's built-in timeout parameter, adhering to the principle of cooperative timeouts. ```Ruby circuit :redis_cache do threshold failures: 5 end def get_from_cache(key) circuit(:redis_cache).wrap do Redis.new(timeout: 3).get(key) # 3 second timeout end end ``` -------------------------------- ### Preventing Ractor Meltdown with Circuit Breakers in Ruby Source: https://github.com/seuros/breaker_machines/blob/master/README.md This example contrasts two approaches to using Ruby Ractors for heavy computation. The 'Before' code shows how unmanaged Ractor creation can lead to CPU meltdown. The 'After' code demonstrates how integrating a circuit breaker (BreakerMachines) provides graceful degradation, preventing system crashes during high load or failures. ```ruby # Before BreakerMachines: 50.times.map do Ractor.new { process_heavy_computation } end # Result: CPU meltdown, system crash, angry customers ``` ```ruby # After BreakerMachines: circuit :ractor_processing do threshold failures: 5, within: 60 fallback { process_with_reduced_capacity } end 50.times.map do circuit(:ractor_processing).wrap do Ractor.new { process_heavy_computation } end end # Result: Graceful degradation, happy customers, promoted engineer ``` -------------------------------- ### Implement Circuit Breaker for SendGrid Email Service Source: https://github.com/seuros/breaker_machines/blob/master/README.md This example shows how to protect an email sending service (SendGrid) with a circuit breaker. If the SendGrid API becomes unavailable, the fallback queues the email for later retry using a background job, ensuring email delivery is eventually consistent. ```Ruby class EmailService include BreakerMachines::DSL circuit :sendgrid do threshold failures: 5, within: 120 reset_after 30 # Configure timeout in SendGrid client fallback do # Store for retry later EmailRetryJob.perform_later(email_params) { queued: true } end end def send_welcome_email(user) circuit(:sendgrid).wrap do SendGrid::Mail.new( to: user.email, subject: "Welcome to the Resistance", body: "Your circuits are now protected" ).deliver! end end end ``` -------------------------------- ### Simulate Cascading Failures with Circuit Breaker Chaos Monkey Source: https://github.com/seuros/breaker_machines/blob/master/README.md Provides an example of a 'Chaos Monkey' class designed to randomly trip circuit breakers in production environments. This helps test system resilience and recovery mechanisms by simulating cascading failures safely. ```ruby class CircuitChaosMonkey # Not to be confused with RMNS Atlas Monkey - this one breaks things on purpose def self.simulate_cascading_failure # Randomly trip circuits to test recovery if rand < 0.01 && ENV['ENABLE_CHAOS'] == 'true' circuit = [:redis, :postgresql, :external_api].sample BreakerMachines.circuit(circuit).send(:trip) notify_team("Chaos Monkey tripped #{circuit} circuit") end end end ``` -------------------------------- ### Resilient Ruby Reddit Bot with BreakerMachines Source: https://github.com/seuros/breaker_machines/blob/master/README.md This example shows how to integrate BreakerMachines into a Reddit bot to prevent infinite reply loops, manage API rate limits, and detect bot-to-bot conversations. It uses multiple circuits to ensure the bot operates safely and efficiently, avoiding bans and excessive costs by gracefully handling failures and limiting recursive calls. ```ruby class SafeRedditBot include BreakerMachines::DSL circuit :reddit_api do threshold failures: 5, within: 60 reset_after 300 # Reddit rate limits are serious fallback { log_event("Reddit API circuit open - taking a break") } end circuit :reply_loop_detector do threshold failures: 3, within: 30 # Max 3 replies in 30 seconds reset_after 120 fallback { "I've said enough. Let's give others a chance to contribute." } end circuit :bot_detection do threshold failures: 2, within: 10 # Detect bot-to-bot conversations fallback { nil } # Just stop replying end def respond_to_comment(comment, depth = 0) # Prevent infinite recursion return if depth > 2 # Detect if we're talking to another bot circuit(:bot_detection).wrap do if comment.author.include?("Bot") || comment.body.match?(/valid|appreciate|hear you/i) raise "Possible bot detected" } end # Rate limit our replies response = circuit(:reply_loop_detector).wrap do circuit(:reddit_api).wrap do generate_and_post_response(comment) end end # Don't recursively check replies - that way lies madness response end end ``` -------------------------------- ### Ruby: Testing Circuit Breaker Thread Safety Under Concurrent Load Source: https://github.com/seuros/breaker_machines/blob/master/README.md This example tests the thread safety of circuit breakers by simulating high concurrent load with multiple threads. It introduces random failures to trigger the circuit breaker and verifies that the system remains stable and counts are accurate, ensuring no race conditions or crashes occur. ```ruby class TestConcurrentCircuits < ActiveSupport::TestCase def test_thread_safety_under_load service = Class.new do include BreakerMachines::DSL circuit :api do threshold failures: 10, within: 1 reset_after 5 end end.new failure_count = Concurrent::AtomicFixnum.new(0) success_count = Concurrent::AtomicFixnum.new(0) # Hammer it with 100 threads threads = 100.times.map do Thread.new do 10.times do begin service.circuit(:api).wrap do if rand > 0.7 # 30% failure rate raise "Random failure" end "success" end success_count.increment rescue failure_count.increment end end end end threads.each(&:join) # Circuit should have opened at some point assert failure_count.value > 0 assert success_count.value > 0 # No race conditions or crashes assert_equal 1000, failure_count.value + success_count.value end end ``` -------------------------------- ### Unsafe Ruby Reddit Bot: Recursive Comment Responder Source: https://github.com/seuros/breaker_machines/blob/master/README.md This code demonstrates a naive Reddit bot implementation that lacks circuit breakers, leading to uncontrolled recursion, excessive API calls, and a self-inflicted denial-of-service. It serves as a cautionary example of the problems BreakerMachines aims to solve by showing the consequences of unmanaged external dependencies and recursive logic. ```ruby class RedditEmoBot def respond_to_comment(comment) # No circuit breaker, no rate limiting, no sanity response = generate_supportive_response(comment.body) comment.reply(response) # Check for replies to our replies (THE FATAL FLAW) comment.replies.each do |reply| if reply.author != @username respond_to_comment(reply) # Recursive doom end end end end ``` -------------------------------- ### Configure BreakerMachines with Redis Circuit Storage in Ruby Source: https://github.com/seuros/breaker_machines/blob/master/README.md This Ruby snippet demonstrates how to configure the `BreakerMachines` gem to use the `RedisCircuitStorage` class. It initializes the storage with a Redis client, specifying a URL from environment variables and a dynamic prefix based on the Rails environment. ```Ruby BreakerMachines.configure do |config| config.storage = RedisCircuitStorage.new( redis: Redis.new(url: ENV['REDIS_URL']), prefix: "breakers:#{Rails.env}:" ) end ``` -------------------------------- ### Ruby: Dangerous Forceful Timeout Implementation Source: https://github.com/seuros/breaker_machines/blob/master/README.md Provides an example of implementing a forceful timeout using Ruby's `Timeout.timeout`. This snippet is included with a strong warning against its use due to the high risk of corrupting application state, leaving resources open, and causing inconsistent behavior. ```Ruby # AT YOUR OWN RISK - This can corrupt state! require 'timeout' circuit(:dangerous_operation).wrap do Timeout.timeout(3) do # Your dangerous operation end end ``` -------------------------------- ### Run BreakerMachines Development Tests Source: https://github.com/seuros/breaker_machines/blob/master/CONTRIBUTING.md Provides various bash commands for executing tests within the BreakerMachines project. This includes running the entire test suite, executing a specific test file for focused debugging, and running tests with a predefined seed to reproduce and debug intermittent failures. ```bash # Run all tests bundle exec rake test # Run specific test file bundle exec ruby -Itest test/circuit_test.rb # Run with specific seed (for debugging intermittent failures) bundle exec rake test TESTOPTS="--seed=12345" ``` -------------------------------- ### BreakerMachines Storage Backend Base Interface Source: https://github.com/seuros/breaker_machines/blob/master/CONTRIBUTING.md Documents the "Storage::Base" interface, which all BreakerMachines storage backends must implement. This interface defines methods for recording successful and failed operations, retrieving success and failure counts, and managing the circuit's status, ensuring consistent interaction with various storage mechanisms. ```APIDOC Storage::Base Interface: record_success(circuit_name: string, duration: number = nil): void Description: Records a successful operation for a given circuit. Parameters: circuit_name: The name of the circuit. duration: Optional duration of the operation. record_failure(circuit_name: string, duration: number = nil): void Description: Records a failed operation for a given circuit. Parameters: circuit_name: The name of the circuit. duration: Optional duration of the operation. success_count(circuit_name: string, window: number = nil): number Description: Returns the count of successful operations for a circuit within a window. Parameters: circuit_name: The name of the circuit. window: Optional time window for counting. failure_count(circuit_name: string, window: number = nil): number Description: Returns the count of failed operations for a circuit within a window. Parameters: circuit_name: The name of the circuit. window: Optional time window for counting. get_status(circuit_name: string): string Description: Retrieves the current status of a circuit. Parameters: circuit_name: The name of the circuit. Returns: Current status (e.g., 'closed', 'open', 'half_open'). set_status(circuit_name: string, status: string, opened_at: number = nil): void Description: Sets the status of a circuit. Parameters: circuit_name: The name of the circuit. status: The new status to set. opened_at: Optional timestamp when the circuit was opened. ``` -------------------------------- ### Anti-Pattern: Infinite Retry Logic in Ruby Source: https://github.com/seuros/breaker_machines/blob/master/README.md An example of a dangerous anti-pattern in Ruby, demonstrating an infinite retry loop that can lead to system instability and resource exhaustion. This code illustrates how not to handle service failures, as it will continuously attempt to fetch data without proper backoff or circuit breaking. ```ruby def fetch_user_data retry_count = 0 begin @redis.get(user_id) rescue => e retry_count += 1 retry if retry_count < Float::INFINITY # "It'll work eventually" end end ``` -------------------------------- ### Configure Global BreakerMachines Settings Source: https://github.com/seuros/breaker_machines/blob/master/README.md Demonstrates how to configure global settings for BreakerMachines, including default reset timeout, default failure threshold, and event logging. It also notes that timeouts should be handled by client libraries. ```ruby BreakerMachines.configure do |config| config.default_reset_timeout = 60 # seconds of mourning before retry config.default_failure_threshold = 5 # strikes before you're out config.log_events = true # false if you prefer ignorance # Note: Timeouts must be implemented in your client libraries (HTTP, DB, etc.) end ``` -------------------------------- ### Configure Circuit Breaker for Canary Deployments Source: https://github.com/seuros/breaker_machines/blob/master/README.md Demonstrates how to implement canary deployments for circuit breaker configurations. This allows a small percentage of users to receive new, potentially more aggressive, circuit breaker thresholds while the majority continue to use conservative production settings, enabling gradual rollout and testing. ```ruby class CanaryCircuitConfig def self.configure_for_canary(percentage: 10) if rand(100) < percentage # New, more aggressive thresholds circuit :payment_api do threshold failures: 2, within: 30 reset_after 60 end else # Conservative production config circuit :payment_api do threshold failures: 5, within: 60 reset_after 120 end end end end ``` -------------------------------- ### Define a Circuit using BreakerMachines DSL in a Class Source: https://github.com/seuros/breaker_machines/blob/master/sig/README.md This snippet demonstrates how to use the `BreakerMachines::DSL` module to define a circuit directly within a class. It shows how to configure circuit properties like failure threshold, reset timeout, and a fallback block using a declarative syntax. ```ruby class MyService include BreakerMachines::DSL circuit :database do threshold failures: 10, within: 60 reset_after 120 fallback { [] } end end ``` -------------------------------- ### BreakerMachines Smart Threshold Calculation Formula Source: https://github.com/seuros/breaker_machines/blob/master/README.md Presents a conceptual formula for calculating dynamic circuit breaker thresholds based on service criticality, traffic volume, and a base threshold. It explains the variables involved in the calculation. ```APIDOC threshold = base_threshold * (1 / criticality_score) * traffic_multiplier Where: - criticality_score: 1.0 (critical) to 0.1 (low priority) - traffic_multiplier: avg_requests_per_minute / 1000 - base_threshold: 5 (default) ``` -------------------------------- ### Ruby: Testing Circuit Breaker Resilience with Redis and PostgreSQL Failures Source: https://github.com/seuros/breaker_machines/blob/master/README.md This snippet demonstrates how to test a circuit breaker's ability to handle simulated failures from external services like Redis and PostgreSQL. It shows how to stub external calls to trigger specific errors and verify that the circuit opens and fallbacks are activated, ensuring graceful degradation. ```ruby # In 2025, we test our code. # Unlike your enterprise architects who think QA is optional class TestTheApocalypse < ActiveSupport::TestCase def setup @ship = SpaceshipCommand.new end def test_redis_dies_gracefully # Simulate the end times redis_stub = ->(_) { raise Redis::TimeoutError } @ship.circuit(:redis_ship_com).stub(:execute_call, redis_stub) do 3.times { @ship.fetch_from_cache("hope") } end assert @ship.circuit(:redis_ship_com).open? assert_equal "emergency_broadcast", @ship.fetch_from_cache("anything") end def test_postgresql_life_support_holds # When the database has a bad day 2.times do @ship.circuit(:postgresql_life_support).wrap do raise PG::ConnectionBad end rescue nil end result = @ship.get_vital_signs assert_equal "emergency_oxygen_activated", result end end ``` -------------------------------- ### Define Ruby Circuit Breakers with BreakerMachines DSL Source: https://github.com/seuros/breaker_machines/blob/master/README.md Demonstrates how to define circuit breakers for external services like Redis and PostgreSQL using the BreakerMachines DSL. It shows configuration for failure thresholds, reset times, and custom fallback actions, along with 'on_open' callbacks for alerting. ```ruby # In 2025, We need patterns that work. class SpaceshipCommand include BreakerMachines::DSL # When Redis Ship Com inevitably fails circuit :redis_ship_com do threshold failures: 3, within: 60 # Three strikes, you're out reset_after 30 # Give it time to think about what it's done fallback do # This is where we separate the bootcamp grads from the Resistance emergency_broadcast("Redis is dead. Long live the cache.") end on_open do alert_the_resistance("Redis circuit opened. Brace for impact.") end end # PostgreSQL Life Support - because your data matters more than your feelings circuit :postgresql_life_support do threshold failures: 2, within: 30 # timeout 5 # Document your intent, but implement timeouts in your DB client fallback { activate_emergency_oxygen } on_open do captain_log <<~LOG Life support critical. If you're reading this, tell my wife I love her. Also, check the connection pool settings. LOG end end end ``` -------------------------------- ### Ruby: Distributed Circuit Breaker Eventual Consistency Source: https://github.com/seuros/breaker_machines/blob/master/README.md Illustrates the eventual consistency of circuit states across instances in a distributed system when using shared storage. It also demonstrates how to achieve immediate consistency by forcing a refresh from storage before each call, noting the associated performance trade-off. ```Ruby # Instance A opens circuit at 10:00:00.000 circuit.trip! # Instance B might still accept calls until 10:00:00.100 # This is by design for performance # If you need immediate consistency: circuit :critical_operation do storage :redis # Shared storage # Check storage before every call (slower but consistent) before_call do refresh_from_storage! end end ``` -------------------------------- ### Ruby: HTTP Client with Built-in Timeout Source: https://github.com/seuros/breaker_machines/blob/master/README.md Demonstrates how to correctly implement cooperative timeouts with an HTTP client (Faraday) within a BreakerMachines circuit. It clarifies that the circuit's 'timeout' configuration is for documentation only, and actual timeouts should be handled by the underlying library. ```Ruby circuit :external_api do # timeout 3 # This is just documentation threshold failures: 5 end def call_api circuit(:external_api).wrap do Faraday.get('https://api.example.com') do |req| req.options.timeout = 3 # Read timeout req.options.open_timeout = 2 # Connection timeout end end end ``` -------------------------------- ### Define Circuit Breaker State Machine in Ruby Source: https://github.com/seuros/breaker_machines/blob/master/CONTRIBUTING.md Illustrates the core state machine definition for the circuit breaker, implemented using the "state_machines" gem. It defines three states: ":closed", ":open", and ":half_open", along with events ("trip", "attempt_recovery", "reset") that trigger transitions between these states, forming the fundamental logic of the circuit breaker. ```ruby state_machine :status, initial: :closed do # States: # - :closed (initial) - Circuit is functioning normally # - :open - Circuit has failed and is rejecting calls # - :half_open - Circuit is testing if the service has recovered # Events that trigger state transitions: event :trip do transition closed: :open transition half_open: :open end event :attempt_recovery do transition open: :half_open end event :reset do transition [:open, :half_open] => :closed end end ``` -------------------------------- ### Implement Circuit Breaker with BreakerMachines (Ruby) Source: https://github.com/seuros/breaker_machines/blob/master/README.md Shows the simple and effective way to implement a circuit breaker using the `BreakerMachines` library. Wraps a potentially failing operation, allowing the library to handle retries, failures, and circuit state management automatically, promoting system resilience. ```Ruby # This is the way circuit(:external_api).wrap { make_request } ``` -------------------------------- ### Demonstrating Cooperative Timeouts with BreakerMachines Source: https://github.com/seuros/breaker_machines/blob/master/README.md This code demonstrates the use of cooperative timeouts in `fiber_safe` mode. It defines a circuit with a `timeout` and then wraps an HTTP call, highlighting that the timeout will safely interrupt the operation without corrupting state, leveraging `Async::Task.current.with_timeout` for reliable async control flow. ```Ruby circuit :slow_api, fiber_safe: true do timeout 3 # This uses Async::Task.current.with_timeout end # This will timeout safely after 3 seconds without corruption circuit(:slow_api).wrap do HTTP.get('https://slow-api.example.com/endpoint') end ``` -------------------------------- ### Defensive Ruby AI Assistant with BreakerMachines Source: https://github.com/seuros/breaker_machines/blob/master/README.md This code demonstrates how BreakerMachines can be applied to AI applications to manage LLM API calls and prevent infinite clarification loops. It ensures the AI gracefully handles low confidence responses and API failures, saving costs and improving reliability by admitting confusion instead of continuing to try. ```ruby class SmartAIAssistant include BreakerMachines::DSL circuit :llm_api do threshold failures: 3, within: 60 # Configure timeout in your LLM client (e.g., OpenAI timeout parameter) fallback { { response: "I need a moment to think about this properly.", confidence: 1.0 } } end circuit :clarification_loop do threshold failures: 2, within: 10 # Max 2 clarification attempts fallback { { response: "I apologize, but I need more context to answer properly.", confidence: 1.0 } } end def answer_question(query, depth = 0) circuit(:clarification_loop).wrap do raise "Too deep in thought" if depth > 3 response = circuit(:llm_api).wrap { llm_api.complete(query) } if response.confidence < 0.8 && depth < 3 # Limited recursion with circuit protection clarification = answer_question("Clarify: #{response}", depth + 1) return answer_question("Given #{clarification}, #{query}", depth + 1) end response end end end ``` -------------------------------- ### Implement Circuit Breaker with Faraday Client Source: https://github.com/seuros/breaker_machines/blob/master/README.md Demonstrates how to integrate Breaker Machines with a Faraday HTTP client to protect against external API failures. It defines a circuit with custom fallback logic for different Faraday errors and tracks circuit state changes using metrics. ```ruby class ExternalAPIClient include BreakerMachines::DSL circuit :third_party_api do threshold failures: 4, within: 60 reset_after 60 fallback do |error| case error when Faraday::TimeoutError { error: "Service slow, please retry later" } when Faraday::ConnectionFailed { error: "Service unreachable" } when Faraday::ResourceNotFound { error: "Resource not found", status: 404 } else { error: "Service temporarily unavailable" } end end # Track everything on_open { Metrics.increment('external_api.circuit_opened') } on_close { Metrics.increment('external_api.circuit_closed') } on_reject { Metrics.increment('external_api.circuit_rejected') } end def connection @connection ||= Faraday.new(url: BASE_URL) do |faraday| faraday.request :json faraday.response :json faraday.response :raise_error # Raise on 4xx/5xx faraday.adapter Faraday.default_adapter end end def fetch_data(endpoint) circuit(:third_party_api).wrap do response = connection.get(endpoint) do |req| req.headers['Authorization'] = "Bearer #{token}" req.options.timeout = 10 req.options.open_timeout = 5 end response.body end end def post_data(endpoint, payload) circuit(:third_party_api).wrap do response = connection.post(endpoint) do |req| req.headers['Authorization'] = "Bearer #{token}" req.body = payload req.options.timeout = 10 end response.body end end end ``` -------------------------------- ### Add BreakerMachines RBS to Steepfile Source: https://github.com/seuros/breaker_machines/blob/master/sig/README.md This snippet demonstrates how to integrate the BreakerMachines RBS type signatures into your project's `Steepfile` for type checking with Steep. It defines a target for your application, specifies the signature directory, and includes the `breaker_machines` library. ```ruby target :app do signature "sig" check "lib" library "breaker_machines" end ``` -------------------------------- ### Create PostgreSQL Table for Circuit Breaker States in Ruby Source: https://github.com/seuros/breaker_machines/blob/master/README.md This Ruby migration defines a PostgreSQL table `circuit_breaker_states` to persist circuit breaker information. It includes columns for circuit name, status, timestamps, and failure/success counts, with unique indexing on `circuit_name` and an index on `updated_at` for cleanup. ```Ruby class CreateCircuitBreakerStates < ActiveRecord::Migration[8.0] def change create_table :circuit_breaker_states do |t| t.string :circuit_name, null: false t.string :status, null: false t.datetime :opened_at t.integer :failure_count, default: 0 t.integer :success_count, default: 0 t.datetime :last_failure_at t.timestamps t.index :circuit_name, unique: true t.index :updated_at # For cleanup end end end ``` -------------------------------- ### Illustrating Microservice Dependency Chain Meltdowns Source: https://github.com/seuros/breaker_machines/blob/master/README.md This snippet visually contrasts the expected behavior of a microservice dependency chain (UserService -> CacheService -> Database) with the reality of cascading failures. It highlights how timeouts, retries, and resource exhaustion (like connection pools) can lead to a complete system collapse. ```ruby # What you think happens: UserService -> CacheService -> Database # What actually happens: UserService -> CacheService (timeout) -> Retry -> Retry -> Retry -> Database (overloaded) -> Connection Pool (exhausted) -> 💀 Everything Dies 💀 ``` -------------------------------- ### Visualize Cascade Failure Pattern (Mermaid) Source: https://github.com/seuros/breaker_machines/blob/master/README.md A Mermaid diagram illustrating a cascade failure, where the failure of one service triggers a chain reaction of failures across dependent services. Shows how a single point of failure can lead to an entire system collapse, visualizing the domino effect in distributed systems. ```Mermaid graph TD A[Service A Fails] --> B[Service B Overwhelmed] B --> C[Service C Drowns in Retries] C --> D[Service D Connection Pool Exhausted] D --> E[Entire System Collapse] style A fill:#ff6b6b style E fill:#c92a2a ```