Skip to content

Circuit Breaker: Decorators and tools that can easily apply the Circuit Breaker pattern.

License

Notifications You must be signed in to change notification settings

kibae/node-circuit-breaker

Repository files navigation

Circuit Breaker

  • Decorators and tools that can easily apply the Circuit Breaker pattern.
  • Node version >= 16
    • Depends on worker thread and BroadcastChannel.

Node.js CI NPM Version License

Install

  • NPM
$ npm install node-circuit-breaker --save
  • Yarn
$ yarn add node-circuit-breaker
  • You may need additional axios, @nestjs/common, and typeorm packages to use the Error Wrapper.

API

@CircuitBreaker Method Decorator

  • Options
Member Sub Type Required Description
fallback Function string Object required An error object to return or a function to run when CircuitBreaker state:Open
  • Function : Executes the specified function instead.
  • String : The name of another method within the object in the current context to execute instead.
    eg) xxxxFallback
  • Object : Returns the specified value immediately.
rules Array<CircuitBreakerRule> required Rules for determining state:Open
exceptions Array<typeof Error> required Exception list
times number required Number of exceptions within the period
inSeconds number required Period(seconds) for counting exceptions
fallbackForSeconds number required Period(seconds) to respond with fallback before Open -> HalfOpen
timeoutMilliSeconds number If the execution of the method takes longer than the specified time (ms), ExecutionTimeoutError is generated internally. You can use ExecutionTimeoutError in CircuitBreaker rules.
scope CircuitBreakerScope
  • CircuitBreakerScope.DEFAULT : Ignore the context and catch all exceptions raised by the method
  • CircuitBreakerScope.INSTANCE : Catch only exceptions that occur in methods within instance context
onCircuitOpen (circuit: Circuit) => Promise<boolean> A callback called when state changes to Open. If false is returned, the state is not changed.
onCircuitClose (circuit: Circuit) => Promise<boolean> A callback called when state changes to Closed. If false is returned, the state is not changed.
ignore (error: any, circuit: Circuit) => Promise<boolean> A callback that is called when an error occurs. If true is returned, the error is ignored and the circuit is not affected.
onError (error: any, circuit: Circuit) => Promise<void> A callback that is called when an error occurs.

CircuitBreakerManager

  • CircuitBreakerManager.terminate()
    • Terminate CircuitBreaker Worker. Circuit state change stops.

Usage

class Test {
  @CircuitBreaker({
    // If state:Open, execute test1Fallback(...) within the same instance.
    fallback: 'test1Fallback',
    rules: [
      // TypeormQueryTimeoutError, RequestTimeoutException, GatewayTimeoutException
      // If an error occurs 10 times within 60 seconds, state:Open is set and a fallback is performed. 
      {
        exceptions: [TypeormQueryTimeoutError, RequestTimeoutException, GatewayTimeoutException],
        times: 10,
        inSeconds: 60
      },
      // BadGatewayException, TypeormConnectionFailedError, ServiceUnavailableException
      // If an error occurs 30 times within 5 seconds, state:Open is set and fallback is performed.
      {
        exceptions: [BadGatewayException, TypeormConnectionFailedError, ServiceUnavailableException],
        times: 30,
        inSeconds: 5
      }
    ],
    // After state:Open, it becomes state:HalfOpen after 5 seconds, and some calls are performed normally.
    // If an error occurs at this time, it changes back to state:Open, and then changes to state:HalfOpen again after 5 seconds.
    // If a normal response is made in state:HalfOpen state, it becomes state:Closed state.
    fallbackForSeconds: 5,
  })
  test1(arg: string) {
    if (arg === 'error') throw new BadGatewayException();
    return arg;
  }

  test1Fallback(arg: string) {
    return 'fallback';
  }

  @CircuitBreaker({
    scope: CircuitBreakerScope.INSTANCE,
    fallback: TestFallback.test2,
    // If the method takes more than 1000ms to execute, an ExecutionTimeoutError error is generated.
    // ExecutionTimeoutError is not thrown, only passed inside the circuit breaker.
    timeoutMilliSeconds: 1000,
    rules: [
      { exceptions: [BadGatewayException], times: 10, inSeconds: 5 },
      { exceptions: [ExecutionTimeoutError], times: 3, inSeconds: 10 }
    ],
    fallbackForSeconds: 3,
  })
  test2(arg: string) {
    if (arg === 'error') throw new BadGatewayException();
    return arg;
  }
}

class TestFallback {
  static test2(arg: string) {
    return arg;
  }
}

Contributors


Error Wrapper

  • Provides error wrappers for frequently used functions.
  • The provided Error Wrapper and all kinds of Errors can be caught by @CircuitBreaker({ rules[].exceptions[...] }).

ExecutionTimeoutError

  • @CircuitBreaker({...}) If timeoutMilliSeconds is set, ExecutionTimeoutError occurs if the execution time of the method is longer than the set time.

TypeORM

  • Refines QueryFailedError if typeorm is installed.
  • If the SQL query fails, you can catch the error by identifying whether it is a connection issue or a timeout.
Source Error Converted Condition
QueryFailedError TypeormConnectionFailedError QueryFailedError.driverError contains connection
QueryFailedError TypeormQueryTimeoutError QueryFailedError.driverError contains timeout

Axios -> Nestjs HttpException

  • Axios returns an error in the form of AxiosError when an error occurs, so it is difficult to use in exception filter.
  • By converting AxiosError into HttpException form of Nestjs and using it as a rule of CircuitBreaker, detailed rule setting is possible.
  • Requires axios, @nestjs/common packages.
  • Transform rules