1.0.2 • Published 4 months ago

easy-circuit-breaker v1.0.2

Weekly downloads
-
License
MIT
Repository
github
Last release
4 months ago

Easy Circuit Breaker

easy-circuit-breaker is a lightweight and powerful solution designed to protect your applications from unexpected failures. Built on top of the robust opossum circuit breaker library, it offers an intuitive API to seamlessly manage and control failure handling for your requests. Whether you're building a microservice architecture or managing complex network interactions, Easy Circuit Breaker ensures your system remains resilient and fault-tolerant.

Installation

To install the library, run the following command:

npm install easy-circuit-breaker

Usage

Importing the Library

import { CircuitBreaker, CircuitBreakerLevel } from 'easy-circuit-breaker';

nest.js example

import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import axios from 'axios';
import { CircuitBreaker, CircuitBreakerLevel } from 'easy-circuit-breaker';

@Injectable()
export class AppService {
  private readonly circuitBreaker: CircuitBreaker;
  private readonly logger = new Logger(AppService.name);

  constructor() {
    this.circuitBreaker = new CircuitBreaker({
      timeout: () => {
        this.logger.error('Circuit Breaker is timeout');
      },
      failure: () => {
        this.logger.error('Circuit Breaker is failure');
      },
      reject: () => {
        this.logger.error('Circuit Breaker is reject');
      },
    });
  }

  private readonly stockServiceUrl = 'http://localhost:3001/stock';

  async checkStock(productId: number): Promise<{ available: boolean }> {
    const response = await axios.get(`${this.stockServiceUrl}/${productId}`);
    return response.data;
  }

  async checkStockFallbackFunction() {
    return { available: true };
  }

  async createOrder(productId: number) {
    try {
      const result: { available: boolean } = await this.circuitBreaker.execute({
        level: CircuitBreakerLevel.Endpoint,
        requestFn: this.checkStock.bind(this),
        // fallbackFn: this.checkStockFallbackFunction.bind(this),
        name: 'checkStock',
        args: [productId],
      });

      if (result?.available) {
        return { message: 'Order placed successfully!' };
      } else {
        throw new HttpException(
          'Product is out of stock',
          HttpStatus.BAD_REQUEST
        );
      }
    } catch (error) {
      throw new HttpException(
        'Service Unavailable',
        HttpStatus.SERVICE_UNAVAILABLE
      );
    }
  }
}

express.js example

import express from 'express';
import axios from 'axios';
import { CircuitBreaker, CircuitBreakerLevel } from 'easy-circuit-breaker';

const app = express();
const port = 3000;

const circuitBreaker = new CircuitBreaker({
  timeout: () => console.error('Circuit Breaker timeout'),
  failure: () => console.error('Circuit Breaker failure'),
  reject: () => console.error('Circuit Breaker reject'),
});

const stockServiceUrl = 'http://localhost:3001/stock';

async function checkStock(productId: number): Promise<{ available: boolean }> {
  const response = await axios.get(`${stockServiceUrl}/${productId}`);
  return response.data;
}

async function checkStockFallbackFunction() {
  return { available: true };
}

app.get('/create-order/:productId', async (req, res) => {
  const productId = parseInt(req.params.productId, 10);
  try {
    const result: { available: boolean } = await circuitBreaker.execute({
      level: CircuitBreakerLevel.Endpoint,
      requestFn: checkStock,
      fallbackFn: checkStockFallbackFunction,
      name: 'checkStock',
      args: [productId],
    });

    if (result?.available) {
      res.send({ message: 'Order placed successfully!' });
    } else {
      res.status(400).send({ message: 'Product is out of stock' });
    }
  } catch (error) {
    res.status(503).send({ message: 'Service Unavailable' });
  }
});

app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});

Fallback Function Example

You can provide a fallback function that will be called when the request fails. Below is an example of how to implement a fallback function:

async checkStockFallbackFunction(productId: number) {
  return { available: true }; // Default fallback value
}

async createOrder(productId: number) {
  try {
    const result: { available: boolean } = await this.circuitBreaker.execute({
      level: CircuitBreakerLevel.Endpoint,
      requestFn: this.checkStock.bind(this),
      fallbackFn: this.checkStockFallbackFunction.bind(this),
      name: 'checkStock',
      args: [productId],
      fallbackFnArgs: [productId], // Pass args to fallback function
    });

    if (result?.available) {
      return { message: 'Order placed successfully!' };
    } else {
      throw new HttpException('Product is out of stock', HttpStatus.BAD_REQUEST);
    }
  } catch (error) {
    throw new HttpException('Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);
  }
}

API

Circuit Breaker Levels

  • Endpoint: Circuit breaker for an individual endpoint.
  • Service: Circuit breaker for a specific service.
  • Application: Circuit breaker for the entire application.
  • Database: Circuit breaker for database calls.
  • External: Circuit breaker for external services.

Circuit Breaker Constructor

The CircuitBreaker class takes an optional eventHandlers object, which can be used to handle various circuit breaker events:

{
  fire?: () => void;
  reject?: () => void;
  timeout?: () => void;
  success?: () => void;
  failure?: () => void;
  open?: () => void;
  close?: () => void;
  halfOpen?: () => void;
  fallback?: () => void;
  semaphoreLocked?: () => void;
  healthCheckFailed?: () => void;
  shutdown?: () => void;
  cacheHit?: () => void;
  cacheMiss?: () => void;
}

Method: execute

The execute method runs the provided request function with circuit breaker protection:

async execute<T>(params: CircuitBreakerParams<T>): Promise<T>;

Parameters:

  • params: An object that contains:
    • level: (optional) The level of the circuit breaker
    • name: The name of the circuit breaker.
    • requestFn: The request function to be wrapped by the circuit breaker.
    • args: Arguments to be passed to the requestFn.
    • fallbackFn: (optional) fallback function to be executed in case of failure.
    • fallbackFnArgs: Arguments to be passed to the fallbackFnArgs.
    • options: (optional) custom circuit breaker options. If not provided the default values will be used.

Returns:

  • A promise that resolves to the result of the requestFn.

Example:

const result = await this.circuitBreaker.execute({
  level: CircuitBreakerLevel.Endpoint,
  requestFn: this.checkStock.bind(this),
  name: 'checkStock',
  args: [productId],
});

Options

The following options can be provided when creating a circuit breaker:

  • timeout: Max time (ms) for an operation to complete before failing. Default: 3000ms
  • errorThresholdPercentage: Failure rate (%) to trigger circuit breaker. Default: 50%
  • resetTimeout: Time (ms) before transitioning to "half-open" state. Default: 5000ms
  • rollingCountTimeout: Time window (ms) for tracking statistics. Default: 10000ms
  • rollingCountBuckets: Number of buckets in rollingCountTimeout window. Default: 10
  • name: Custom name for the circuit breaker. Default: 'CircuitBreaker'
  • rollingPercentilesEnabled: Enables percentile calculations. Default: false
  • capacity: Max concurrent requests. Default: 10
  • enabled: Enables circuit breaker on startup. Default: true
  • allowWarmUp: Prevents early circuit opening by ignoring failures initially. Default: false
  • volumeThreshold: Minimum requests before circuit breaker can open. Default: 5
  • errorFilter: Ignores specific errors if function returns true. Default: null
  • cache: Enables caching of first successful response. Default: false
  • cacheTTL: Cache expiration time (ms), 0 means never expires. Default: 0
  • cacheGetKey: Defines cache key. Default: null
  • cacheTransport: Custom caching mechanism with get, set, flush methods. Default: null
  • abortController: Uses AbortController to cancel async operations on timeout. Default: false
  • enableSnapshots: Enables snapshot events for statistics. Default: false
  • rotateBucketController: Shares EventEmitter for multiple circuit breakers. Default: null

Contributing

We welcome contributions of all kinds! To contribute, please check out the Contributing Guide.

Security Issues

If you find a security vulnerability, please refer to our Security Policies and Procedures.

Author

easy-circuit-breaker is developed and maintained by Ferhat Yalçın.

License

MIT License

1.0.2

4 months ago

1.0.1

4 months ago

1.0.0

4 months ago