loading...
Cover image for Circuit breaker pattern in Laravel applications

Circuit breaker pattern in Laravel applications

tkorakas profile image Thanos Korakas ・3 min read

Let's start by framing the problem. A lot of applications make external requests to get or sent data. As we already know by the Fallacies of distributed computing
the network is not reliable and is one of those things that we can’t control in our applications. A failing network request happening repeatedly may drive your application to be unresponsive.

For example, you are building an application and you are using an external search service. Like we mentioned before the network is not our friend in this and one day the service starts having big delays or stops responding completely.

As you can easily understand, these are bad news for our application. Allowing an application to make requests in a situation like this will result in it underperforming or stop working entirely.

Circuit breaker pattern to the rescue

The circuit breaker is a design pattern used in modern software development. It is used to detect failures and encapsulates the logic of preventing a failure from constantly recurring, during maintenance, temporary external system failure or unexpected system difficulties. - Wikipedia

Circuit breaker is a pattern that prevents request execution against unresponsive services.

If you looking for more information on the topic I can highly recommend this article by Martin Fowler.

Laravel 7 ships with all the ingredients needed to build a circuit breaker:

I’ll try to keep the code as simple as possible and focus more on the pattern itself to better explain the concept.

Before the circuit breaker pattern our application looked something like this:

$response = Http::get('https://fake-search-service.com/api/v1/search?q=Laravel');

return view('articles.index', ['articles' => $response->body()];

The HTTP client will wait for a couple of seconds before throwing an exception so it’s a good practice to always define timeout on our requests.

Laravel's client comes with a handy method to define the timeout of our request.

$response = Http::timeout(2)->get('https://fake-search-service.com/api/v1/search?q=Laravel');

return view('articles.index', ['articles' => $response->body()];

Ok, but we are still not preventing our service to make a request to the unresponsive API.

$limiter = app(RateLimiter::class);
$actionKey = 'search_service';
$threshold = 10;

try {
    if ($limiter->tooManyAttempts($actionKey, $threshold)) {
        // Exceeded the maximum number of failed attempts.
        return $this->failOrFallback();
    }
    $response = Http::timeout(2)->get('https://fake-search-service.com/api/v1/search?q=Laravel');

    return view('articles.index', ['articles' => $response->body()]);
} catch (\Exception $exception) {
    $limiter->hit($actionKey, Carbon::now()->addMinutes(15));

    return $this->failOrFallback();
}

Using Laravel's RateLimiter we can count the failed attempts performed to the search API $limiter->hit($actionKey, Carbon::now()->addMinutes(15)).

By counting those attempts we can prevent our application from making any more requests to this API for a certain amount of time.

Before every request, we check if there are too many failed attempts $limiter->tooManyAttempts($actionKey, $threshold) and if the failed attempts have passed our threshold we will prevent our service to request the search API for 15 minutes.

With the failOrFallback method we can provide a fallback implementation like search in the database instead of the external service or return an appropriate error message to the user.

Having a lot of requests waiting to be answered by the unresponsive service will slow down our application. Using the circuit breaker pattern we can prevent our whole application from going down. The search page is down but without affecting the performance of the other pages.

If we are building an application using the microservices architecture, we can use this pattern for our services as well.

Moreover, it would be good to use shared storage (like Redis) so all the services will have a common way to learn if another service is down without requesting N times. Using this pattern will help the unresponsive service to recover faster instead of adding more load on top.

Like I said this is a simplistic implementation of the circuit breaker pattern. You can implement it in a more sophisticated way and handle many things more appropriately.

If you have implemented this pattern in Laravel in a different way I would love to read it so feel free to leave your feedback.

Discussion

markdown guide