avatarOleh Zaporozhets

Summary

The web content describes how to implement a circuit breaker pattern in TypeScript to prevent excessive stress on a system by halting requests under certain conditions.

Abstract

The article provides a comprehensive guide on creating a circuit breaker in TypeScript, which is a mechanism to prevent a system from making requests to external services when certain failure conditions are met, such as a threshold of errors or a rate limit. It outlines the use of the axios library for request handling, the implementation of the circuit breaker interface and class with error handling and timeout logic, and the integration of an event emitter for notifying about the circuit breaker's state changes. The author emphasizes the importance of understanding the underlying mechanics of such safety patterns, despite the availability of pre-built solutions, and concludes with a practical example involving an express server to simulate errors and demonstrate the circuit breaker in action.

Opinions

  • The author believes that everyone can benefit from understanding how to implement a circuit breaker, whether to refresh knowledge or learn something new.
  • It is suggested that while libraries exist for implementing a circuit breaker, it is valuable to know how to build one from scratch.
  • The author indicates that the circuit breaker pattern is more about protecting a system from cascading failures rather than directly dealing with back pressure.
  • The article implies that using an event emitter with a circuit breaker enhances the pattern by allowing for better monitoring and response to state changes.
  • The author recommends using established solutions for production environments but encourages learning through implementation for educational purposes.
  • A recommendation is made for an AI service that offers similar capabilities to ChatGPT Plus (GPT-4) at a more cost-effective price, highlighting its value for those interested in such technologies.

How to Create Circuit Breaker in TypeScript

Build your own circuit breaker in pure TypeScript

Photo by Troy Bridges on Unsplash

Hello World!

Today I want to share with you how you can create another safety mechanism for your awesome code. I believe everyone will find it useful, somebody can refresh their knowledge another — pick up something new.

Circuit Breaker?

First things first — let’s figure out what a circuit breaker is. From its name, you may know, that it is an automatically operated electrical switch, designed to turn off electricity in case of an overload or short spike to protect an electrical circuit from damage.

This statement is acceptable for us, with the only difference — we’ll protect someone’s system. To protect our system we have to use another approach — back pressure, but the article isn’t about it.

In the context of programming, we want to have a mechanism that will block sending requests to 3rd party services. It may be needed in cases when we use some other services, but we are limited with some amount of requests per minute.

Preparation

Before we start I have to note, that there are libraries, which provide this solution, so there is no need to implement it by yourself, but definitely — it’s good to know how to do it.

In our case, we will use axios, since it can easily abort a request.

We can begin our code from a simple Httpclass, which will be responsible for sending requests.

It has a pretty simple interface…

import { AxiosInstance } from 'axios';
interface IHttp {
  instance: AxiosInstance;
}

…and implementation as well:

The example of usage:

Implementation

Let’s begin with an interface. It will have only one public method to know if a circuit breaker is open:

interface ICircuitBreaker {
  getStatus(): boolean;
}

CircuitBreaker will be a class, which accepts Http class and some options. Let’s describe these options via aninterface as well:

interface ICircuitBreakerOptions {
  timeout: number;
  errorHandler: (error: any) => boolean;
}

timeout is a period of time for which a circuit breaker will block requests — cooldown.

errorHandler is the function, that will receive an error from a request, and based on a response from this function a circuit breaker will be opened. We need it to open a circuit breaker at a certain time — usually when we have 429 error but not 400 or 404.

Our basic class looks like this:

In a constructor, we accept Http class (we will use it in the future), options — timeout and errorHandler function. Also by default, a class has the isOpen boolean, which indicates if a circuit breaker is open or not. The method getStatus just returns the current status.

Now it’s time to introduce the real logic. We will add two more functions interceptErrorResponse and openCircuitBreaker. The first one is responsible for processing an error from a request, the second one — for opening a circuit breaker.

The logic is pretty simple — when it catches an error, it passes it to this.errorHandler. If the function returns true, it means that the circuit breaker should be open. We trigger a related function if it hasn’t been opened before. Inside openCircuitBreaker we just change the status of isOpen and set a timeout (with a necessary duration from this.timeout) to switch it back.

Once it is open we need a mechanism to stop sending requests. In our case, it will be interceptRequest function.

This function uses the ability of axios to abort a request. If a circuit breaker is open we pass cancelToken to prevent sending of the request and pass related error message — ‘Circuit breaker is open’.

After all, we need to connect our functions with axios instance from Http class. Inside a constructor we will use interceptors to do it.

this is needed not to lose the context of a circuit breaker class.

It’s finished now:

Advanced Version

However, our version works fine there is always some space for improvements. I offer to add events to our circuit breaker. To do it we need an event emitter and we can try to create it by ourselves. The article about creating your own event emitter can be found here.

Let’s start from an interface:

interface IEventEmitter {
  on(name: string, listener: Function): void;
  removeListener(name: string, listener: Function): void;
  emit(name: string, data?: any): void;
}

As you can see our future class will consist of 3 public methods: on for attaching listeners to a specific event, removeListener for removing listeners, and last but not least — emit to emit an event.

I will skip steps for creating an event emitter since this article isn’t about it. Here is the final result:

Now we are able to integrate an event emitter with our circuit breaker class. Let’s start from an interface:

type CircuitBreakerEvents = 'OPEN' | 'CLOSE';
interface ICircuitBreakerWithEmitter extends ICircuitBreaker {
  on(event: CircuitBreakerEvents, listener: Function): void;
}

…and a class:

The difference:

  • We’ve updated the constructor and some properties. Now it has initialization of EventEmitter;
  • We’ve implemented the on method, which just proxies events to the real event emitter;
  • Inside openCircuitBreaker we trigger EventEmitter in the necessary place (on opening and closing).

Testing

Now it’s time to test our work. First of all, we need a third-party service, which will return a necessary error when we need it. For this purpose, we can create a simple express server:

It has the following logic — on each 3rd request it returns 400 error and on each 5th request — 429 error.

Ok, now the last step — prepare the circuit breaker and an http client:

Every 2 seconds our server will send a request GET http://localhost:5000/test/ with the help of TestHttp. When a request returns 429 error - CircuitBreakerWithEmitter blocks triggering an http client for 10 seconds. When the circuit breaker is opened/closed - a related message will be sent by an event emitter.

The Presentation

In the gif above you can see, that the circuit breaker ignores 400 errors (which happens on each 3rd request). It opens a circuit breaker on each 5th request when the server returns a 429 error. Also, when a circuit breaker is open, requests can’t be sent and they are aborted.

Summary

In the end, I wanted to highlight one more time — the example above can be used for learning purposes rather than for a production environment. There are a lot of solutions that can be found and definitely I would suggest using them. At the same time, it is always good to know what is “under the hood”.

Typescript
JavaScript
Programming
Web Development
Software Development
Recommended from ReadMedium