bugloos/fault-tolerance-bundle
Installation
composer require bugloos/fault-tolerance-bundle
Ensure your project meets the requirements (PHP ≥7.4, Symfony ≥4.4).
Configuration
Add the bundle to config/bundles.php:
return [
// ...
Bugloos\FaultToleranceBundle\BugloosFaultToleranceBundle::class => ['all' => true],
];
Basic Setup
Configure Redis (required for caching) in config/packages/bugloos_fault_tolerance.yaml:
bugloos_fault_tolerance:
redis:
host: '127.0.0.1'
port: 6379
password: null
circuit_breaker:
failure_threshold: 3 # Number of failures before breaking
reset_timeout: 30 # Seconds before resetting
First Use Case Wrap a service call in a circuit breaker:
use Bugloos\FaultToleranceBundle\CircuitBreaker\CircuitBreaker;
use Bugloos\FaultToleranceBundle\CircuitBreaker\CircuitBreakerInterface;
$circuitBreaker = new CircuitBreaker(
new CircuitBreakerInterface(), // Your service
'my_service_key', // Unique identifier
3, // Failure threshold
30 // Reset timeout
);
try {
$result = $circuitBreaker->execute();
} catch (\Exception $e) {
// Fallback logic (e.g., cached data or default response)
}
Circuit Breaker for External APIs Use the bundle to wrap HTTP clients (e.g., Guzzle) to handle downstream service failures:
$client = new GuzzleClient();
$circuitBreaker = new CircuitBreaker(
$client->getAsync('https://api.example.com/data'),
'api_example_com',
5,
60
);
Caching Responses Cache successful responses to avoid repeated calls:
$circuitBreaker = new CircuitBreaker(
$service->getData(),
'cached_data_key',
3,
30,
3600 // Cache TTL (1 hour)
);
Fallback Strategies Define fallback logic for broken circuits:
try {
$data = $circuitBreaker->execute();
} catch (CircuitBreakerOpenException $e) {
$data = $this->getFallbackData(); // Static or cached fallback
}
Symfony Integration Use dependency injection to manage circuit breakers:
# config/services.yaml
services:
App\Service\ExternalApiService:
arguments:
$circuitBreaker: '@bugloos_fault_tolerance.circuit_breaker.external_api'
Define the circuit breaker as a service:
# config/packages/bugloos_fault_tolerance.yaml
bugloos_fault_tolerance:
circuit_breakers:
external_api:
service: App\Service\ExternalApiClient
key: 'external_api'
failure_threshold: 3
reset_timeout: 30
$circuitBreaker->onStateChange(function ($state) {
\Log::info("Circuit Breaker state changed: {$state}");
});
$circuitBreaker->onFailure(function () {
$this->metrics->increment('circuit_breaker_failures');
});
bugloos_fault_tolerance:
circuit_breaker:
failure_threshold: '%env(int:CIRCUIT_BREAKER_THRESHOLD)%'
Redis Dependency
bugloos_fault_tolerance.redis.State Persistence
Thread Safety
Fallback Data Freshness
last_updated timestamp to cached data and validate it before serving.Exception Handling
$circuitBreaker = new CircuitBreaker(
$service->getData(),
'key',
3,
30,
null,
[\TimeoutException::class, \ConnectException::class] // Specific exceptions
);
redis-cli to inspect keys:
redis-cli KEYS "*circuit_breaker*"
redis-cli GET "circuit_breaker:my_service_key"
# config/packages/monolog.yaml
handlers:
main:
level: debug
# ...
$mockService = $this->createMock(ServiceInterface::class);
$mockService->method('getData')->willThrowException(new \RuntimeException('Simulated failure'));
Custom Fallback Logic Override the default fallback behavior:
$circuitBreaker->setFallback(function () {
return $this->fallbackRepository->findById(1); // Custom fallback
});
Event Listeners Subscribe to circuit breaker events:
$circuitBreaker->onOpen(function () {
$this->alertingService->notify('Circuit breaker opened for my_service_key');
});
Custom Storage
Replace Redis with another storage backend (e.g., database) by implementing Bugloos\FaultToleranceBundle\Storage\StorageInterface.
Dynamic Thresholds Adjust thresholds at runtime:
$circuitBreaker->setFailureThreshold($newThreshold);
$circuitBreaker->setResetTimeout($newTimeout);
reset_timeout as TTL.service_name:operation) to avoid collisions.How can I help you explore Laravel packages today?