-
Notifications
You must be signed in to change notification settings - Fork 1
CircuitBreaker Policy
Unit: Murphy.Policy.CircuitBreaker
The Circuit Breaker policy prevents cascading failures by temporarily blocking calls to failing services, giving them time to recover.
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.
- TCircuitState Enumeration
- EBrokenCircuitException Class
- ICircuitBreakerPolicy Interface
- TCircuitBreakerPolicy Class
- TCircuitBreakerBuilder Class
- Usage Examples
Describes the possible states of the circuit breaker.
type
TCircuitState = (Closed, Open, HalfOpen, Isolated);Description: Normal operation - execution of actions is allowed.
Behavior:
- All calls execute normally
- Failures are counted
- Circuit opens when failure threshold exceeded
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
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)
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
Exception thrown when attempting to execute code while circuit is open or isolated.
type
EBrokenCircuitException = class(Exception)
private
FInnerException: Exception;
public
constructor Create(const AMessage: string; AInnerException: Exception);
property InnerException: Exception read FInnerException;
end;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
property InnerException: Exception read FInnerException;Description: The exception that caused the circuit to open.
Type: Exception (read-only)
Usage:
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;The interface for the Circuit Breaker policy pattern.
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;function CircuitState: TCircuitState;Description: Returns the current state of the circuit.
Returns: TCircuitState - Current circuit state
Example:
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;procedure Execute(AProc: TProc);Description: Executes the provided procedure with circuit breaker protection.
Parameters:
-
AProc: TProc- Anonymous procedure to execute
Behavior:
-
If Isolated: Throws
EBrokenCircuitExceptionimmediately -
If Open:
- Checks if recovery duration has passed
- If not: Throws
EBrokenCircuitException - If yes: Transitions to
HalfOpenand executes
-
If Closed or HalfOpen: Executes
AProc- On success in
HalfOpen: Resets toClosed - On handled exception: Increments fail count
- Opens circuit if fail count reaches threshold
- Opens circuit immediately if already in
HalfOpen
- On success in
Exceptions:
-
EBrokenCircuitException- Circuit is open or isolated - Re-raises handled exceptions after updating state
- Re-raises unhandled exceptions without affecting state
Example:
CircuitPolicy.Execute(
procedure
begin
HTTP.Get('https://api.example.com/data');
end);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:
// Open circuit after 5 failures
Policy := TCircuitBreakerBuilder
.Handle(Exception)
.Fail(5)
.Within(TTimeSpan.FromSeconds(30))
.Build;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:
// Manually isolate the circuit
CircuitPolicy.Isolate;
// Later... manually reset when ready
CircuitPolicy.Reset;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
Isolatedstate - Manual reset after known issue is fixed
- Testing scenarios
Example:
// Reset the circuit to normal operation
CircuitPolicy.Reset;
WriteLn('Circuit state: ', Ord(CircuitPolicy.CircuitState)); // 0 (Closed)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 fromOpentoHalfOpen
Returns: ICircuitBreakerPolicy - Self for method chaining
Default: 30 seconds
Example:
// 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;Concrete implementation of the Circuit Breaker policy pattern.
type
TCircuitBreakerPolicy = class(TPolicy, ICircuitBreakerPolicy)
private
FBrokenAt: TDateTime;
FBrokenFor: Exception;
FCircuitState: TCircuitState;
FFailCount: Integer;
FFailTimes: Integer;
FWithinDuration: TTimeSpan;
public
constructor Create(AExceptionTypes: TArray<ExceptClass>); override;
// ... interface methods
end;constructor Create(AExceptionTypes: TArray<ExceptClass>); override;Defaults:
-
FCircuitState:Closed -
FFailTimes:2 -
FWithinDuration:30 seconds -
FBrokenAt:MinDateTime -
FFailCount:0
┌─────────┐
│ Closed │ ◄──────────────────┐
└────┬────┘ │
│ │
Failure count >= threshold Success
│ │
┌────▼────┐ ┌──┴──────┐
│ Open │───Duration────►│HalfOpen │
└─────────┘ elapsed └─────────┘
│ │
│ Failure
└─────────────────────────┘
(resets duration)
┌──────────┐
│ Isolated │ (manual control)
└──────────┘
Builder class for creating circuit breaker policies.
type
TCircuitBreakerBuilder = class sealed(TPolicyBuilder<ICircuitBreakerPolicy>)
public
class function Handle(AExceptionTypes: TArray<ExceptClass>): ICircuitBreakerPolicy; override;
end;class function Handle(AExceptionTypes: TArray<ExceptClass>): ICircuitBreakerPolicy; override;Description: Creates a circuit breaker policy configured to handle specified exceptions.
Usage:
Policy := TCircuitBreakerBuilder
.Handle(EIdHTTPProtocolException)
.Fail(5)
.Within(TTimeSpan.FromSeconds(30))
.Build;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;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;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;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;- Circuit Breaker Pattern Guide - Comprehensive usage guide
- Base Policy API - Inherited functionality
- Combining Patterns - Using circuit breaker with other policies
- Best Practices - Production recommendations