# Circuit Breaker Policy API **Unit**: `Murphy.Policy.CircuitBreaker` The Circuit Breaker policy prevents cascading failures by temporarily blocking calls to failing services, giving them time to recover. ## Overview When a service fails repeatedly, continuing to call it: - Wastes resources (threads, connections, time) - Increases latency for users - Can make the problem worse (overloading the failing service) The Circuit Breaker pattern solves this by "opening" the circuit after a threshold of failures, immediately rejecting calls without attempting execution. After a recovery period, it allows test calls to check if the service has recovered. ## Table of Contents - [TCircuitState Enumeration](#tcircuitstate-enumeration) - [EBrokenCircuitException Class](#ebrokencircuitexception-class) - [ICircuitBreakerPolicy Interface](#icircuitbreakerpolicy-interface) - [TCircuitBreakerPolicy Class](#tcircuitbreakerpolicy-class) - [TCircuitBreakerBuilder Class](#tcircuitbreakerbuilder-class) - [Usage Examples](#usage-examples) --- ## TCircuitState Enumeration Describes the possible states of the circuit breaker. ### Declaration ```pascal type TCircuitState = (Closed, Open, HalfOpen, Isolated); ``` ### Values #### Closed **Description**: Normal operation - execution of actions is allowed. **Behavior**: - All calls execute normally - Failures are counted - Circuit opens when failure threshold exceeded #### Open **Description**: Circuit is broken - execution is blocked due to failures. **Behavior**: - All calls immediately throw `EBrokenCircuitException` - No execution attempts are made - After recovery duration expires, transitions to `HalfOpen` #### HalfOpen **Description**: Testing recovery - allowing test calls to check if service recovered. **Behavior**: - Calls are allowed to execute - On success: circuit closes (returns to `Closed`) - On failure: circuit immediately re-opens (returns to `Open`) #### Isolated **Description**: Manually blocked - circuit has been manually opened via `Isolate()`. **Behavior**: - All calls immediately throw `EBrokenCircuitException` - Remains isolated until `Reset()` is called - Does not automatically recover --- ## EBrokenCircuitException Class Exception thrown when attempting to execute code while circuit is open or isolated. ### Declaration ```pascal type EBrokenCircuitException = class(Exception) private FInnerException: Exception; public constructor Create(const AMessage: string; AInnerException: Exception); property InnerException: Exception read FInnerException; end; ``` ### Constructor ```pascal constructor Create(const AMessage: string; AInnerException: Exception); ``` **Parameters**: - `AMessage: string` - Error message describing the broken circuit - `AInnerException: Exception` - The original exception that caused the circuit to open ### Properties #### InnerException ```pascal property InnerException: Exception read FInnerException; ``` **Description**: The exception that caused the circuit to open. **Type**: `Exception` (read-only) **Usage**: ```pascal try CircuitPolicy.Execute( procedure begin CallExternalService; end); except on E: EBrokenCircuitException do begin WriteLn('Circuit is open!'); if Assigned(E.InnerException) then WriteLn('Original error: ', E.InnerException.Message); end; end; ``` --- ## ICircuitBreakerPolicy Interface The interface for the Circuit Breaker policy pattern. ### Declaration ```pascal type ICircuitBreakerPolicy = interface(IPolicy) ['{79052D2E-9BE6-42BC-B2F2-C422E991A1F4}'] function CircuitState: TCircuitState; procedure Execute(AProc: TProc); function Fail(ATimes: Integer = 2): ICircuitBreakerPolicy; procedure Isolate; procedure Reset; function Within(ADuration: TTimeSpan): ICircuitBreakerPolicy; end; ``` ### Methods #### CircuitState ```pascal function CircuitState: TCircuitState; ``` **Description**: Returns the current state of the circuit. **Returns**: `TCircuitState` - Current circuit state **Example**: ```pascal var State: TCircuitState; begin State := CircuitPolicy.CircuitState; case State of TCircuitState.Closed: WriteLn('Circuit is closed - operating normally'); TCircuitState.Open: WriteLn('Circuit is open - calls are blocked'); TCircuitState.HalfOpen: WriteLn('Circuit is half-open - testing recovery'); TCircuitState.Isolated: WriteLn('Circuit is isolated - manually blocked'); end; end; ``` #### Execute ```pascal procedure Execute(AProc: TProc); ``` **Description**: Executes the provided procedure with circuit breaker protection. **Parameters**: - `AProc: TProc` - Anonymous procedure to execute **Behavior**: 1. **If Isolated**: Throws `EBrokenCircuitException` immediately 2. **If Open**: - Checks if recovery duration has passed - If not: Throws `EBrokenCircuitException` - If yes: Transitions to `HalfOpen` and executes 3. **If Closed or HalfOpen**: Executes `AProc` - On success in `HalfOpen`: Resets to `Closed` - On handled exception: Increments fail count - Opens circuit if fail count reaches threshold - Opens circuit immediately if already in `HalfOpen` **Exceptions**: - `EBrokenCircuitException` - Circuit is open or isolated - Re-raises handled exceptions after updating state - Re-raises unhandled exceptions without affecting state **Example**: ```pascal CircuitPolicy.Execute( procedure begin HTTP.Get('https://api.example.com/data'); end); ``` #### Fail ```pascal function Fail(ATimes: Integer = 2): ICircuitBreakerPolicy; ``` **Description**: Configures the number of failures required to open the circuit. **Parameters**: - `ATimes: Integer` - Failure threshold (default: 2) **Returns**: `ICircuitBreakerPolicy` - Self for method chaining **Default**: `2` failures **Example**: ```pascal // Open circuit after 5 failures Policy := TCircuitBreakerBuilder .Handle(Exception) .Fail(5) .Within(TTimeSpan.FromSeconds(30)) .Build; ``` #### Isolate ```pascal procedure Isolate; ``` **Description**: Manually opens the circuit and places it in `Isolated` state. **Behavior**: - Immediately sets state to `Isolated` - All subsequent calls throw `EBrokenCircuitException` - Circuit remains isolated until `Reset()` is called - Does not automatically recover **Use Cases**: - Manual intervention during deployment - Taking a service offline for maintenance - Emergency circuit breaker activation **Example**: ```pascal // Manually isolate the circuit CircuitPolicy.Isolate; // Later... manually reset when ready CircuitPolicy.Reset; ``` #### Reset ```pascal procedure Reset; ``` **Description**: Manually closes the circuit and resets all state. **Behavior**: - Sets state to `Closed` - Resets failure count to 0 - Clears broken timestamp - Clears stored exception **Use Cases**: - Recovery from `Isolated` state - Manual reset after known issue is fixed - Testing scenarios **Example**: ```pascal // Reset the circuit to normal operation CircuitPolicy.Reset; WriteLn('Circuit state: ', Ord(CircuitPolicy.CircuitState)); // 0 (Closed) ``` #### Within ```pascal function Within(ADuration: TTimeSpan): ICircuitBreakerPolicy; ``` **Description**: Configures the recovery duration - how long the circuit stays open before testing recovery. **Parameters**: - `ADuration: TTimeSpan` - Duration to wait before transitioning from `Open` to `HalfOpen` **Returns**: `ICircuitBreakerPolicy` - Self for method chaining **Default**: `30 seconds` **Example**: ```pascal // Wait 1 minute before testing recovery Policy := TCircuitBreakerBuilder .Handle(Exception) .Fail(3) .Within(TTimeSpan.FromMinutes(1)) .Build; // Wait only 10 seconds (faster recovery for less critical services) Policy := TCircuitBreakerBuilder .Handle(Exception) .Fail(5) .Within(TTimeSpan.FromSeconds(10)) .Build; ``` --- ## TCircuitBreakerPolicy Class Concrete implementation of the Circuit Breaker policy pattern. ### Declaration ```pascal type TCircuitBreakerPolicy = class(TPolicy, ICircuitBreakerPolicy) private FBrokenAt: TDateTime; FBrokenFor: Exception; FCircuitState: TCircuitState; FFailCount: Integer; FFailTimes: Integer; FWithinDuration: TTimeSpan; public constructor Create(AExceptionTypes: TArray); override; // ... interface methods end; ``` ### Constructor ```pascal constructor Create(AExceptionTypes: TArray); override; ``` **Defaults**: - `FCircuitState`: `Closed` - `FFailTimes`: `2` - `FWithinDuration`: `30 seconds` - `FBrokenAt`: `MinDateTime` - `FFailCount`: `0` ### State Machine ``` ┌─────────┐ │ Closed │ ◄──────────────────┐ └────┬────┘ │ │ │ Failure count >= threshold Success │ │ ┌────▼────┐ ┌──┴──────┐ │ Open │───Duration────►│HalfOpen │ └─────────┘ elapsed └─────────┘ │ │ │ Failure └─────────────────────────┘ (resets duration) ┌──────────┐ │ Isolated │ (manual control) └──────────┘ ``` --- ## TCircuitBreakerBuilder Class Builder class for creating circuit breaker policies. ### Declaration ```pascal type TCircuitBreakerBuilder = class sealed(TPolicyBuilder) public class function Handle(AExceptionTypes: TArray): ICircuitBreakerPolicy; override; end; ``` ### Class Methods #### Handle ```pascal class function Handle(AExceptionTypes: TArray): ICircuitBreakerPolicy; override; ``` **Description**: Creates a circuit breaker policy configured to handle specified exceptions. **Usage**: ```pascal Policy := TCircuitBreakerBuilder .Handle(EIdHTTPProtocolException) .Fail(5) .Within(TTimeSpan.FromSeconds(30)) .Build; ``` --- ## Usage Examples ### Example 1: Basic Circuit Breaker ```pascal var Policy: ICircuitBreakerPolicy; I: Integer; begin Policy := TCircuitBreakerBuilder .Handle(EIdHTTPProtocolException) .Fail(3) .Within(TTimeSpan.FromSeconds(10)) .Build; for I := 1 to 10 do begin try Policy.Execute( procedure begin WriteLn('Calling service...'); CallFlakyService; // Might fail end); WriteLn('Success!'); except on E: EBrokenCircuitException do WriteLn('Circuit is open - service unavailable'); on E: Exception do WriteLn('Service error: ', E.Message); end; Sleep(2000); end; end; ``` ### Example 2: Monitoring Circuit State ```pascal var Policy: ICircuitBreakerPolicy; begin Policy := TCircuitBreakerBuilder .Handle(Exception) .Fail(2) .Within(TTimeSpan.FromSeconds(15)) .Build; while True do begin WriteLn(Format('Circuit State: %s', [ GetEnumName(TypeInfo(TCircuitState), Ord(Policy.CircuitState)) ])); try Policy.Execute( procedure begin MakeRequest; end); except on E: EBrokenCircuitException do WriteLn('Circuit broken - waiting for recovery'); end; Sleep(5000); end; end; ``` ### Example 3: Manual Circuit Control ```pascal var Policy: ICircuitBreakerPolicy; begin Policy := TCircuitBreakerBuilder .Handle(Exception) .Fail(3) .Build; // Manually isolate for maintenance WriteLn('Taking service offline for maintenance...'); Policy.Isolate; // All calls will fail fast try Policy.Execute(procedure begin MakeRequest; end); except on E: EBrokenCircuitException do WriteLn('Service is offline'); end; // Perform maintenance... WriteLn('Maintenance complete. Bringing service online...'); Policy.Reset; // Circuit is now closed Policy.Execute(procedure begin MakeRequest; end); end; ``` ### Example 4: Circuit Breaker with Logging ```pascal var Policy: ICircuitBreakerPolicy; begin Policy := TCircuitBreakerBuilder .Handle([EIdHTTPProtocolException, EIdSocketError]) .Fail(5) .Within(TTimeSpan.FromMinutes(1)) .Build; try Policy.Execute( procedure begin CallExternalAPI; end); except on E: EBrokenCircuitException do begin Log.Error('Circuit breaker is open - service unavailable'); if Assigned(E.InnerException) then Log.Error('Root cause: ' + E.InnerException.Message); // Use fallback or cached data UseAlternativeData; end; end; end; ``` --- ## See Also - [Circuit Breaker Pattern Guide](../Patterns/CircuitBreaker.md) - Comprehensive usage guide - [Base Policy API](Base-Policy.md) - Inherited functionality - [Combining Patterns](../Patterns/Combining-Patterns.md) - Using circuit breaker with other policies - [Best Practices](../Advanced/Best-Practices.md) - Production recommendations --- [← Retry Policy](Retry-Policy.md) | [API Reference Index](README.md) | [Fallback Policy →](Fallback-Policy.md)