Skip to main content

CircuitBreakerStrategy

The CircuitBreakerStrategy prevents cascading failures by monitoring failure rates and temporarily blocking calls when thresholds are exceeded.

Overview​

The circuit breaker pattern protects your system from cascading failures by monitoring the failure rate of operations. When failures exceed a threshold, the circuit breaker opens and fails fast, giving the downstream system time to recover.

class CircuitBreakerStrategy<T> extends ResilienceStrategy<T> {
CircuitBreakerStrategy(CircuitBreakerStrategyOptions<T> options);

Future<Outcome<T>> executeCore<T>(
ResilienceCallback<T> callback,
ResilienceContext context,
ResilienceCallback<T> next,
);

CircuitBreakerState get state;
bool get isOpen;
bool get isClosed;
bool get isHalfOpen;
}

class CircuitBreakerStrategyOptions<T> {
const CircuitBreakerStrategyOptions({
this.failureRatio = 0.5,
this.minimumThroughput = 10,
this.samplingDuration = const Duration(seconds: 30),
this.breakDuration = const Duration(seconds: 60),
this.shouldHandle,
this.onOpened,
this.onClosed,
this.onHalfOpened,
});
}

enum CircuitBreakerState {
closed,
open,
halfOpen,
}

CircuitBreakerStrategyOptions Properties​

failureRatio​

The failure ratio threshold at which the circuit breaker opens.

Type: double
Default: 0.5 (50%)
Range: 0.0 to 1.0

CircuitBreakerStrategyOptions(
failureRatio: 0.3, // Open when 30% of calls fail
)

minimumThroughput​

Minimum number of calls required before the circuit breaker can open.

Type: int
Default: 10

CircuitBreakerStrategyOptions(
minimumThroughput: 20, // Need at least 20 calls
)

samplingDuration​

Duration over which failures are measured.

Type: Duration
Default: Duration(seconds: 30)

CircuitBreakerStrategyOptions(
samplingDuration: Duration(minutes: 1),
)

breakDuration​

Duration to keep the circuit breaker open before transitioning to half-open.

Type: Duration
Default: Duration(seconds: 60)

CircuitBreakerStrategyOptions(
breakDuration: Duration(minutes: 2),
)

shouldHandle​

Predicate to determine which outcomes count as failures.

Type: ShouldHandlePredicate<T>?
Default: null (all exceptions count as failures)

CircuitBreakerStrategyOptions(
shouldHandle: (outcome) =>
outcome.hasException &&
outcome.exception is! ArgumentException,
)

onOpened​

Callback invoked when the circuit breaker opens.

Type: OnCircuitBreakerOpenedCallback<T>?
Default: null

CircuitBreakerStrategyOptions(
onOpened: (context, args) {
logger.warn('Circuit breaker opened - failure ratio: ${args.failureRate}');
},
)

onClosed​

Callback invoked when the circuit breaker closes.

Type: OnCircuitBreakerClosedCallback<T>?
Default: null

CircuitBreakerStrategyOptions(
onClosed: (context, args) {
logger.info('Circuit breaker closed - system recovered');
},
)

onHalfOpened​

Callback invoked when the circuit breaker transitions to half-open.

Type: OnCircuitBreakerHalfOpenedCallback<T>?
Default: null

CircuitBreakerStrategyOptions(
onHalfOpened: (context, args) {
logger.info('Circuit breaker half-opened - testing recovery');
},
)

CircuitBreakerState​

closed​

Normal operation state where all calls are allowed through.

if (circuitBreaker.state == CircuitBreakerState.closed) {
// Normal operation
}

open​

Failure state where all calls are immediately rejected.

if (circuitBreaker.state == CircuitBreakerState.open) {
// Calls are being blocked
}

halfOpen​

Testing state where a limited number of calls are allowed to test recovery.

if (circuitBreaker.state == CircuitBreakerState.halfOpen) {
// Testing if service has recovered
}

Properties​

state​

Gets the current state of the circuit breaker.

Type: CircuitBreakerState

final currentState = circuitBreaker.state;
print('Circuit breaker is ${currentState.name}');

isOpen​

Gets whether the circuit breaker is currently open.

Type: bool

if (circuitBreaker.isOpen) {
print('Circuit breaker is blocking calls');
}

isClosed​

Gets whether the circuit breaker is currently closed.

Type: bool

if (circuitBreaker.isClosed) {
print('Circuit breaker is allowing calls');
}

isHalfOpen​

Gets whether the circuit breaker is currently half-open.

Type: bool

if (circuitBreaker.isHalfOpen) {
print('Circuit breaker is testing recovery');
}

Callback Types​

ShouldHandlePredicate<T>​

typedef ShouldHandlePredicate<T> = bool Function(Outcome<T> outcome);

Determines whether an outcome counts as a failure for the circuit breaker.

OnCircuitBreakerOpenedCallback<T>​

typedef OnCircuitBreakerOpenedCallback<T> = void Function(
ResilienceContext context,
OnCircuitBreakerOpenedArgs args,
);

Called when the circuit breaker opens due to excessive failures.

OnCircuitBreakerClosedCallback<T>​

typedef OnCircuitBreakerClosedCallback<T> = void Function(
ResilienceContext context,
OnCircuitBreakerClosedArgs args,
);

Called when the circuit breaker closes after successful recovery.

OnCircuitBreakerHalfOpenedCallback<T>​

typedef OnCircuitBreakerHalfOpenedCallback<T> = void Function(
ResilienceContext context,
OnCircuitBreakerHalfOpenedArgs args,
);

Called when the circuit breaker transitions to half-open state.

Usage Examples​

Basic Circuit Breaker​

final circuitBreaker = CircuitBreakerStrategy(CircuitBreakerStrategyOptions(
failureRatio: 0.5,
minimumThroughput: 10,
samplingDuration: Duration(seconds: 30),
breakDuration: Duration(seconds: 60),
));

final pipeline = ResiliencePipelineBuilder()
.addStrategy(circuitBreaker)
.build();

Aggressive Circuit Breaker​

final circuitBreaker = CircuitBreakerStrategy(CircuitBreakerStrategyOptions(
failureRatio: 0.2, // Open at 20% failure rate
minimumThroughput: 5, // With just 5 calls
samplingDuration: Duration(seconds: 10),
breakDuration: Duration(seconds: 30),
));

Conservative Circuit Breaker​

final circuitBreaker = CircuitBreakerStrategy(CircuitBreakerStrategyOptions(
failureRatio: 0.8, // Open at 80% failure rate
minimumThroughput: 50, // Need 50 calls minimum
samplingDuration: Duration(minutes: 5),
breakDuration: Duration(minutes: 10),
));

Selective Failure Handling​

final circuitBreaker = CircuitBreakerStrategy(CircuitBreakerStrategyOptions(
failureRatio: 0.5,
shouldHandle: (outcome) {
if (!outcome.hasException) return false;

final exception = outcome.exception;

// Only count server errors, not client errors
if (exception is HttpException) {
return exception.statusCode >= 500;
}

// Count network and timeout errors
return exception is SocketException ||
exception is TimeoutException;
},
));

Circuit Breaker with Monitoring​

final circuitBreaker = CircuitBreakerStrategy(CircuitBreakerStrategyOptions(
failureRatio: 0.5,
minimumThroughput: 10,
onOpened: (context, args) {
logger.error('Circuit breaker OPENED - Failure rate: ${args.failureRate}');
metrics.incrementCounter('circuit_breaker_opened');

// Alert operations team
alertingService.sendAlert(
'Circuit breaker opened for ${context.getProperty("service")}',
severity: AlertSeverity.high,
);
},
onClosed: (context, args) {
logger.info('Circuit breaker CLOSED - Service recovered');
metrics.incrementCounter('circuit_breaker_closed');
},
onHalfOpened: (context, args) {
logger.info('Circuit breaker HALF-OPEN - Testing recovery');
metrics.incrementCounter('circuit_breaker_half_opened');
},
));

Database Circuit Breaker​

final dbCircuitBreaker = CircuitBreakerStrategy(CircuitBreakerStrategyOptions(
failureRatio: 0.6, // Databases can handle more load
minimumThroughput: 20,
samplingDuration: Duration(minutes: 2),
breakDuration: Duration(minutes: 5),
shouldHandle: (outcome) {
if (!outcome.hasException) return false;

final exception = outcome.exception;

// Don't count syntax errors as circuit breaker failures
return exception is! SqlSyntaxException &&
exception is! ArgumentException;
},
));

HTTP Service Circuit Breaker​

final httpCircuitBreaker = CircuitBreakerStrategy(CircuitBreakerStrategyOptions(
failureRatio: 0.4,
minimumThroughput: 15,
samplingDuration: Duration(seconds: 45),
breakDuration: Duration(seconds: 90),
shouldHandle: (outcome) {
if (!outcome.hasException) return false;

final exception = outcome.exception;

if (exception is HttpException) {
// Only 5xx errors trigger circuit breaker
return exception.statusCode >= 500;
}

return exception is SocketException ||
exception is TimeoutException;
},
onOpened: (context, args) {
final service = context.getProperty<String>('serviceName') ?? 'unknown';
logger.warn('HTTP service circuit breaker opened for $service');
},
));

Circuit Breaker State Monitoring​

final circuitBreaker = CircuitBreakerStrategy(CircuitBreakerStrategyOptions(
failureRatio: 0.5,
minimumThroughput: 10,
));

// Check state before important operations
if (circuitBreaker.isOpen) {
// Circuit breaker is open, use cached data or alternative service
return getCachedData();
}

// Or check state in context
context.setProperty('circuitBreakerState', circuitBreaker.state.name);

Multiple Circuit Breakers​

// Different circuit breakers for different services
final userServiceCB = CircuitBreakerStrategy(CircuitBreakerStrategyOptions(
failureRatio: 0.3,
minimumThroughput: 10,
breakDuration: Duration(minutes: 1),
));

final paymentServiceCB = CircuitBreakerStrategy(CircuitBreakerStrategyOptions(
failureRatio: 0.1, // Payment service needs higher reliability
minimumThroughput: 5,
breakDuration: Duration(minutes: 5),
));

// Use in different pipelines
final userPipeline = ResiliencePipelineBuilder()
.addStrategy(userServiceCB)
.build();

final paymentPipeline = ResiliencePipelineBuilder()
.addStrategy(paymentServiceCB)
.build();

Best Practices​

Configure Thresholds Appropriately​

// For critical services - lower threshold
CircuitBreakerStrategyOptions(
failureRatio: 0.2,
minimumThroughput: 5,
)

// For non-critical services - higher threshold
CircuitBreakerStrategyOptions(
failureRatio: 0.7,
minimumThroughput: 20,
)

Set Appropriate Timeouts​

CircuitBreakerStrategyOptions(
samplingDuration: Duration(seconds: 30), // Don't make too short
breakDuration: Duration(minutes: 2), // Give service time to recover
)

Handle Different Error Types​

CircuitBreakerStrategyOptions(
shouldHandle: (outcome) {
if (!outcome.hasException) return false;

final exception = outcome.exception;

// Don't trigger circuit breaker for client errors
if (exception is ValidationException ||
exception is AuthenticationException) {
return false;
}

// Trigger for server/infrastructure errors
return true;
},
)

Monitor Circuit Breaker Events​

CircuitBreakerStrategyOptions(
onOpened: (context, args) {
// Log and alert
logger.error('Circuit breaker opened');
alertService.notify('Service degradation detected');

// Update health check
healthCheck.markUnhealthy('Circuit breaker open');
},
onClosed: (context, args) {
// Log recovery
logger.info('Circuit breaker closed - service recovered');
healthCheck.markHealthy();
},
)