Imagine you're sending OTPs after users create an account. A typical Laravel job might look something like this:
tries = 5
backoff = [30, 60, 90, 120]
If sending the SMS fails, Laravel retries after 30 seconds, then 60 seconds, etc. If all retries fail, the job ends up in the failed jobs table.
So far, so good.
But what happens if your SMS provider is completely down? Let's say you suddenly have 1,000 OTP jobs waiting in the queue.
Without any protection, Laravel could make up to 5,000 API calls (1,000 jobs × 5 attempts) to a service that is already unavailable.
❌ This wastes worker resources, creates unnecessary traffic, and can put even more pressure on an already failing service.
✅ This is exactly the kind of problem the Circuit Breaker pattern solves.
A circuit breaker has three states:
- Closed – The service is healthy, so requests are allowed.
- Open – The service is considered unhealthy, so requests are blocked.
- Half-Open – After a cooldown period, allow a small number of requests to check whether the service has recovered.
Laravel provides a simple way to implement a basic circuit breaker using the ThrottlesExceptions job middleware.
For example, you can configure it to open the circuit after 5 HttpExceptions. Once that threshold is reached, Laravel pauses future executions for a period (for example, 5 minutes) instead of repeatedly calling the failing service.
📍 One thing that is easy to miss is that once the exception threshold is reached, the delay configured by ThrottlesExceptions takes precedence over the job's normal backoff. The job waits for the throttle period to expire before it is attempted again.
📍 Another important point is that the job's attempt count is still preserved. ThrottlesExceptions delays execution, but it doesn't reset or ignore the number of attempts. If your tries value is too small, the job can still end up in the failed jobs table after the allowed attempts are exhausted.
It's also a good idea to throttle only the exceptions that actually indicate the external service is unavailable. For example, HttpExceptions make sense, while validation or business logic exceptions usually shouldn't open the circuit.
ThrottlesExceptions is a great option if you just need a simple circuit breaker.
If you need more advanced behavior, especially proper Half-Open support, the laravel-fuse package gives you much more control over how the circuit behaves.
Small patterns like this can make a huge difference once your application starts processing thousands of jobs.