Skip to content

CircuitBreaker Policy

Marco Breveglieri edited this page Nov 24, 2025 · 1 revision

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

Describes the possible states of the circuit breaker.

Declaration

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

type
  EBrokenCircuitException = class(Exception)
  private
    FInnerException: Exception;
  public
    constructor Create(const AMessage: string; AInnerException: Exception);
    property InnerException: Exception read FInnerException;
  end;

Constructor

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

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;

ICircuitBreakerPolicy Interface

The interface for the Circuit Breaker policy pattern.

Declaration

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

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;

Execute

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:

CircuitPolicy.Execute(
  procedure
  begin
    HTTP.Get('https://api.example.com/data');
  end);

Fail

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;

Isolate

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;

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 Isolated state
  • 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)

Within

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:

// 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

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

constructor Create(AExceptionTypes: TArray<ExceptClass>); 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

type
  TCircuitBreakerBuilder = class sealed(TPolicyBuilder<ICircuitBreakerPolicy>)
  public
    class function Handle(AExceptionTypes: TArray<ExceptClass>): ICircuitBreakerPolicy; override;
  end;

Class Methods

Handle

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;

Usage Examples

Example 1: Basic Circuit Breaker

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

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

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

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


← Retry Policy | API Reference Index | Fallback Policy →

Clone this wiki locally