laravel-sentinel maintained by wal3fo
Wal3fo Laravel Sentinel
Production-ready circuit breaker for Laravel queues and service calls, built to stop cascading failures with closed, open, and half-open states.
Introduction
When a dependency starts failing (mail API, payment gateway, CRM, webhook target), retries alone can overload workers and amplify outages. A circuit breaker prevents that by temporarily short-circuiting failing paths, then probing recovery in a controlled way.
Wal3fo Laravel Sentinel is designed for Laravel queue-heavy applications where resilience and predictable failure handling matter. Use it when:
- jobs call unstable or rate-limited external services
- multiple workers can overwhelm a failing dependency
- you need controlled backoff and safe recovery behavior
- you want observable state transitions and operational commands
Features
- Queue middleware for automatic circuit checks and job release behavior
- Full circuit lifecycle:
closed,open,half_open - Cache-backed state and counters (Redis strongly recommended)
- Per-service configuration overrides
- Pluggable failure classification via contract
- Artisan commands for status and manual control
- Transition events for monitoring and alerting
- Laravel
10,11,12, and13support
Installation
composer require wal3fo/laravel-sentinel
Publish the package configuration:
php artisan vendor:publish --tag=sentinel-config
[!TIP] Use Redis as the cache backend for distributed queue workers and reliable lock behavior.
Configuration
Sentinel is configured in config/sentinel.php.
Global defaults are defined under defaults, then selectively overridden per service under services.
<?php
return [
'defaults' => [
'threshold' => 50.0,
'min_requests' => 10,
'cooldown_seconds' => 60,
'release_delay_seconds' => 30,
'half_open_probe_limit' => 1,
],
'services' => [
'mail' => [
'threshold' => 40.0,
'min_requests' => 20,
'cooldown_seconds' => 120,
'release_delay_seconds' => 20,
'half_open_probe_limit' => 1,
],
'crm' => [
'threshold' => 30.0,
'min_requests' => 8,
'cooldown_seconds' => 45,
'release_delay_seconds' => 15,
],
],
];
Key Options
threshold: failure-rate percentage that opens the circuitmin_requests: minimum attempts required before threshold evaluationcooldown_seconds: time to keep the circuit open before half-open probe windowrelease_delay_seconds: queue job release delay when circuit is not allowing execution
Per-Service Overrides
Any service key in services inherits from defaults and can override only what it needs.
Basic Usage
Queue Job Middleware
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Wal3fo\LaravelSentinel\Middleware\CircuitBreakerMiddleware;
final class SendEmailJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public function middleware(): array
{
return [
new CircuitBreakerMiddleware('mail'),
];
}
public function handle(): void
{
// Call external provider here.
}
}
Multiple services in one job:
public function middleware(): array
{
return [
new CircuitBreakerMiddleware(['mail', 'crm']),
];
}
Attribute Usage
When you prefer declarative service mapping, use the package attribute and instantiate middleware without arguments.
<?php
namespace App\Jobs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Wal3fo\LaravelSentinel\Attributes\UseCircuitBreaker;
use Wal3fo\LaravelSentinel\Middleware\CircuitBreakerMiddleware;
#[UseCircuitBreaker(['mail'])]
final class SendNotificationJob implements ShouldQueue
{
public function middleware(): array
{
return [new CircuitBreakerMiddleware()];
}
public function handle(): void
{
// Perform work.
}
}
How It Works
Sentinel tracks attempts and failures per service in cache.
closed: requests/jobs flow normally while failures are recorded.open: oncemin_requestsis met andfailure_rate >= threshold, executions are short-circuited.half_open: aftercooldown_seconds, Sentinel allows limited probes (half_open_probe_limit).- On probe success, the circuit closes and counters reset.
- On probe failure, the circuit re-opens and cooldown restarts.
Failure rate is calculated as:
$$ ext{failure_rate} = \frac{\text{failures}}{\text{attempts}} \times 100 $$
When a queue middleware check finds a circuit open (or half-open without an available probe slot), the job is released using the configured release_delay_seconds.
Failure Classification
By default, Sentinel uses:
Wal3fo\LaravelSentinel\Services\DefaultFailureClassifier
The default classifier ignores configured business exceptions and counts outage-like exceptions.
To customize behavior, provide your own classifier implementing:
Wal3fo\LaravelSentinel\Contracts\FailureClassifier
<?php
namespace App\Support;
use Throwable;
use Wal3fo\LaravelSentinel\Contracts\FailureClassifier;
final class ApiFailureClassifier implements FailureClassifier
{
public function shouldCount(Throwable $throwable, string $service, array $serviceConfig = []): bool
{
return ! $throwable instanceof \DomainException;
}
}
Register it in configuration:
'failure_classifier' => [
'class' => App\Support\ApiFailureClassifier::class,
],
Artisan Commands
php artisan sentinel:statusShows status for all configured services.php artisan sentinel:status mailShows detailed status for a single service.php artisan sentinel:open mailForces a service circuit toopen.php artisan sentinel:close mailForces a service circuit toclosed.php artisan sentinel:resetResets all configured service circuits and counters.php artisan sentinel:reset mailResets one service circuit and counters.
Events
Sentinel dispatches events on state changes:
Wal3fo\LaravelSentinel\Events\CircuitOpenedFired when a circuit transitions toopen.Wal3fo\LaravelSentinel\Events\CircuitHalfOpenedFired when cooldown expires and circuit entershalf_open.Wal3fo\LaravelSentinel\Events\CircuitClosedFired when recovery succeeds and circuit returns toclosed.
Each event extends Wal3fo\LaravelSentinel\Events\CircuitStateChanged and includes service, state, and status data.
Best Practices
- Apply circuit breakers to network or infrastructure-dependent operations, not pure business validation logic.
- Count failures that indicate dependency outage/timeouts; ignore business/domain exceptions where appropriate.
- Use Redis for cache and locks in multi-worker production environments.
- Set
min_requestshigh enough to avoid opening on low-volume noise. - Tune
thresholdper dependency based on real-world error patterns and SLOs.
Testing
Run the test suite:
composer test
The package includes coverage for:
- middleware behavior and release flow when circuits are open
- state transitions (
closed→open→half_open→closed) - half-open probe limits
- failure classification rules
- event dispatching
- artisan command behavior
Compatibility
- Laravel:
10,11,12,13 - PHP:
^8.1(effective minimum follows the selected Laravel version)
Contributing
Contributions are welcome.
- Fork the repository.
- Create a feature branch.
- Add tests for behavioral changes.
- Run
composer test. - Open a pull request with a clear description.
License
Released under the MIT License.