dart_circuit_breaker 0.0.1
dart_circuit_breaker: ^0.0.1 copied to clipboard
A Dart implementation of the Circuit Breaker pattern for fault tolerance and resilience.
Circuit Breaker #
A Dart implementation of the Circuit Breaker pattern for building resilient applications that can handle failures gracefully.
Overview #
The Circuit Breaker pattern is a design pattern used in software development to detect failures and encapsulate the logic of preventing a failure from constantly recurring during maintenance, temporary external system failure, or unexpected system difficulties.
Features #
- Three States: Closed, Open, and Half-Open states with automatic transitions
- Configurable Thresholds: Set failure thresholds and recovery requirements
- Generic Type Support: Works with any return type
T - Comprehensive Error Handling: Custom exceptions and proper error propagation
- Thread-Safe: Safe for concurrent usage
- Well Tested: Comprehensive test coverage including edge cases
States #
Closed State #
- Normal Operation: All calls pass through to the wrapped operation
- Failure Tracking: Counts consecutive failures
- Transition: Opens when failure count reaches the threshold
Open State #
- Fail Fast: All calls are immediately rejected with
CircuitOpenException - No Execution: The wrapped operation is not executed
- Transition: Automatically transitions to Half-Open after the timeout period
Half-Open State #
- Limited Testing: Allows calls through to test if the service has recovered
- Success Tracking: Counts successful calls
- Transitions:
- To Closed: After enough successful calls (halfOpenThreshold)
- To Open: Immediately on any failure
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
dart_circuit_breaker: ^0.0.1
Usage #
Basic Usage #
import 'package:dart_circuit_breaker/dart_circuit_breaker.dart';
// Create a circuit breaker
final circuitBreaker = CircuitBreaker(
threshold: 3, // Open after 3 failures
timeout: const Duration(seconds: 30), // Stay open for 30 seconds
halfOpenThreshold: 2, // Close after 2 successes in half-open
);
// Use the circuit breaker
try {
final result = await circuitBreaker.execute(() async {
// Your potentially failing operation here
return await httpClient.get('https://api.example.com/data');
});
print('Success: $result');
} on CircuitOpenException catch (e) {
print('Circuit is open: ${e.message}');
// Handle the circuit being open (maybe use cached data)
} catch (e) {
print('Operation failed: $e');
// Handle other errors
}
Advanced Usage #
// HTTP Client with Circuit Breaker
class ResilientHttpClient {
final CircuitBreaker _circuitBreaker;
final HttpClient _httpClient;
ResilientHttpClient({
int threshold = 5,
Duration timeout = const Duration(minutes: 1),
int halfOpenThreshold = 3,
}) : _circuitBreaker = CircuitBreaker(
threshold: threshold,
timeout: timeout,
halfOpenThreshold: halfOpenThreshold,
),
_httpClient = HttpClient();
Future<String> get(String url) async {
return await _circuitBreaker.execute(() async {
final request = await _httpClient.getUrl(Uri.parse(url));
final response = await request.close();
if (response.statusCode != 200) {
throw HttpException('HTTP ${response.statusCode}');
}
return await response.transform(utf8.decoder).join();
});
}
void dispose() {
_httpClient.close();
}
}
Database Operations #
class DatabaseService {
final CircuitBreaker _circuitBreaker = CircuitBreaker(
threshold: 3,
timeout: const Duration(seconds: 10),
halfOpenThreshold: 2,
);
Future<List<User>> getUsers() async {
return await _circuitBreaker.execute(() async {
// Simulate database operation
final connection = await Database.connect();
try {
return await connection.query('SELECT * FROM users');
} finally {
await connection.close();
}
});
}
}
Configuration #
Constructor Parameters #
threshold(required): Number of consecutive failures before opening the circuittimeout(required): Duration to keep the circuit open before transitioning to half-openhalfOpenThreshold(required): Number of successful calls in half-open state needed to close the circuit
State Monitoring #
print('Current state: ${circuitBreaker.state}');
print('Failure count: ${circuitBreaker.failureCount}');
print('Success count: ${circuitBreaker.successCount}');
print('Circuit breaker info: $circuitBreaker');
Manual Reset #
// Manually reset the circuit breaker to closed state
circuitBreaker.reset();
Error Handling #
The circuit breaker provides specific error handling:
try {
final result = await circuitBreaker.execute(yourOperation);
// Handle success
} on CircuitOpenException catch (e) {
// Circuit is open - implement fallback logic
// e.g., return cached data, show cached content, etc.
} on TimeoutException catch (e) {
// Operation timed out
} on NetworkException catch (e) {
// Network-related errors
} catch (e) {
// Other errors from your operation
}
Best Practices #
- Choose Appropriate Thresholds: Set thresholds based on your service's characteristics and requirements
- Implement Fallbacks: Always have a fallback strategy when the circuit is open
- Monitor Circuit State: Log state transitions for debugging and monitoring
- Use Different Circuits: Use separate circuit breakers for different external services
- Consider Timeout Values: Set timeout values that allow sufficient time for service recovery
Testing #
The library includes comprehensive tests. Run them with:
dart test
Example #
Run the included example:
dart run example/circuit_breaker_example.dart
This example demonstrates:
- Normal operation in closed state
- Circuit opening due to failures
- Rejection of calls in open state
- Recovery process through half-open state
- Transition back to closed state
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
License #
This project is licensed under the MIT License - see the LICENSE file for details.