@fastify/circuit-breaker

repository·main·Indexed 41 days ago

https://github.com/fastify/fastify-circuit-breaker

A low-overhead circuit breaker plugin for Fastify version 4.0.3 that protects routes from cascading failures by monitoring error thresholds and timeouts. It implements the circuit breaker pattern using a preHandler hook to track failures and transition between open, half-open, and closed states.

Tokens
1.9K
Snippets
5
Records
9
Agent score
1%

What's inside fastify-circuit-breaker

  1. How @fastify/circuit-breaker works

    main

    The plugin implements a circuit breaker pattern for Fastify routes to prevent cascading failures.

    To use it:

    1. Register the plugin: Use fastify.register(require('@fastify/circuit-breaker'), [options]). This adds an onSend hook and exposes the circuitBreaker utility.
    2. Apply to routes: Call instance.circuitBreaker() within the preHandler option of a specific route. This places that specific route under the circuit breaking check.

    Important Caveat: The timeout mechanism in this plugin measures the time the route takes to execute and checks it after the route has finished. If the execution time exceeds the timeout, it returns an error even if the route produced a successful response. For wrapping external API calls with a classic timeout, consider using easy-breaker instead.

    const fastify = require('fastify')()
    
    fastify.register(require('@fastify/circuit-breaker'))
    
    fastify.register(function (instance, opts, next) {
      instance.route({
        method: 'GET',
        url: '/',
        preHandler: instance.circuitBreaker(),
        handler: function (req, reply) {
          // Your route logic here
        }
      })
      next()
    })
  2. Configure @fastify/circuit-breaker options

    main

    You can provide global configuration during plugin registration or override it for specific routes by passing options directly to the circuitBreaker() utility. Route-specific options take precedence.

    // Global configuration
    fastify.register(require('@fastify/circuit-breaker'), {
      threshold: 3,
      timeout: 5000,
      resetTimeout: 5000,
      onCircuitOpen: async (req, reply) => {
        reply.statusCode = 500
        throw new Error('a custom error')
      },
      onTimeout: async (req, reply) => {
        reply.statusCode = 504
        return 'timed out'
      }
    })
    
    // Route-specific override
    fastify.route({
      method: 'GET',
      url: '/specific',
      preHandler: fastify.circuitBreaker({
        threshold: 1
      }),
      handler: async (req, reply) => { /* ... */ }
    })
  3. Register the @fastify/circuit-breaker plugin

    main

    To use the circuit breaker, register the @fastify/circuit-breaker plugin with your Fastify instance. You can provide global configuration options that will serve as defaults for all routes using the circuit breaker.

    const fastify = require('fastify')()
    const circuitBreakerPlugin = require('@fastify/circuit-breaker')
    
    fastify.register(circuitBreakerPlugin, {
      timeout: 5000,
      resetTimeout: 10000,
      threshold: 3
    })
  4. Configure circuit breaker options

    main

    When registering the plugin or calling circuitBreaker(), you can provide the following options:

    OptionDefaultDescription
    timeout10000Maximum time (in ms) a request is allowed to run before being considered a failure.
    resetTimeout10000Time (in ms) the circuit stays in the OPEN state before transitioning to HALFOPEN.
    threshold5Number of consecutive failures required to trip the circuit to OPEN.
    timeoutErrorMessage'Timeout'The error message used when a TimeoutError is thrown.
    circuitOpenErrorMessage'Circuit open'The error message used when a CircuitOpenError is thrown.
    onCircuitOpenundefinedAn async function (req, reply) => payload called when the circuit is OPEN. If it returns a payload, it is sent as the response.
    onTimeoutundefinedAn async function (req, reply) => payload called when a request times out. If it returns a payload, it is sent as the response.
    cache500The maximum number of circuit breaker states to keep in the internal cache.
  5. Reference: Circuit Breaker Configuration Options

    main

    The following options are available for both global registration and the circuitBreaker() utility:

    • threshold: (number) The maximum number of failures accepted before opening the circuit. (Default: 5)
    • timeout: (number) The maximum number of milliseconds to wait before returning a TimeoutError. (Default: 10000)
    • resetTimeout: (number) Milliseconds before the circuit moves from open to half-open. (Default: 10000)
    • onCircuitOpen: (async function) Called when the circuit is open due to errors. Can modify the reply and return a string | Buffer | Stream payload. If an Error is thrown, it is routed to the error handler. Receives (req, reply).
    • onTimeout: (async function) Called when the circuit is open due to timeouts. Can modify the reply and return a string | Buffer | Stream | Error payload. Receives (req, reply).
    • timeoutErrorMessage: (string) Custom error message for timeout errors. (Default: 'Timeout')
    • circuitOpenErrorMessage: (string) Custom error message for circuit open errors. (Default: 'Circuit open')
  6. Apply circuit breaker to a route using circuitBreaker()

    main

    Once the plugin is registered, you can use the fastify.circuitBreaker() method within your route handlers (specifically as a preHandler hook) to protect specific routes. Each call to circuitBreaker() creates a unique circuit breaker instance for that specific route configuration.

    fastify.get('/protected-route', {
      preHandler: fastify.circuitBreaker({
        threshold: 5,
        timeout: 2000,
        onCircuitOpen: async (req, reply) => {
          return { error: 'Service temporarily unavailable' }
        }
      })
    }, async (request, reply) => {
      // Your route logic here
      return { hello: 'world' }
    })
  7. Handle CircuitOpenError and TimeoutError

    main

    The plugin throws specific errors when the circuit breaker logic is triggered. Both errors result in a 503 status code by default:

    • FST_ERR_CIRCUIT_BREAKER_OPEN: Thrown when the circuit is in the OPEN state or when a request is attempted while the circuit is HALFOPEN and another request is already running.
    • FST_ERR_CIRCUIT_BREAKER_TIMEOUT: Thrown when a request exceeds the configured timeout duration.