Git Product home page Git Product logo

brakes's Introduction

Dependency Status Build Status Coverage Status npm version Code Climate contributions welcome badges

brakes

Brakes is a circuit breaker library for Node. A circuit breaker provides latency and fault protection for distributed systems. Brakes will monitor your outgoing requests, and will trip an internal circuit if it begins to detect that the remote service is failing. Circuit protection allows you to redirect requests to sane fallbacks, and back-off the downstream services so they can recover. This module is largely based on Netflix's Hystrix

Requires Node 4.2.0 or higher

http://martinfowler.com/bliki/CircuitBreaker.html

https://github.com/Netflix/Hystrix/wiki/How-it-Works

https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern


Bluebird and Promisify

Bluebird

This module utilizes bluebird promises. For more on the features of bluebird visit their site: http://bluebirdjs.com/. Brakes uses bluebird over native promises in order to provide more feature rich promises and because bluebird offers performance comparable to that of raw callbacks.

Promisify

If you pass an async function that relies on callback, brakes will promisify it into a bluebird promise. If you pass a promise to brakes, it will use that promise as is.

Note: brakes will only detect async callback functions that use callbacks with one of the following names: cb, callback, callback_, or done.

Examples

Promise

  function promiseCall(foo){
    return new Promise((resolve, reject) =>{
      if (foo) resolve(foo);
      else reject(foo);
    });
  }

  const brake = new Brakes(promiseCall, {timeout: 150});

  brake.exec('bar')
    .then((result) =>{
      console.log(`result: ${result}`);
    })
    .catch(err =>{
      console.error(`error: ${err}`);
    });

Callback

  function asyncCall(foo, cb){
    if (foo) cb(null, foo);
    else cb(new Error(foo));
  }

  const brake = new Brakes(asyncCall, {timeout: 150});

  brake.exec('bar')
    .then((result) =>{
      console.log(`result: ${result}`);
    })
    .catch(err =>{
      console.error(`error: ${err}`);
    });

Fallback

  function promiseCall(foo){
    return new Promise((resolve, reject) =>{
      if (foo) resolve(foo);
      else reject(foo);
    });
  }

  function fallbackCall(foo){
    return new Promise((resolve, reject) =>{
      resolve('I always succeed');
    });
  }

  const brake = new Brakes(promiseCall, {timeout: 150});

  brake.fallback(fallbackCall)

  brake.exec(false)
    .then((result) =>{
      console.log(`result: ${result}`);
    })
    .catch(err =>{
      console.error(`error: ${err}`);
    });

Health check

  function promiseCall(foo){
    return new Promise((resolve, reject) =>{
      if (foo) resolve(foo);
      else reject(foo);
    });
  }

  function fallbackCall(foo){
    return new Promise((resolve, reject) =>{
      resolve('I always succeed');
    });
  }

  function healthCheckCall(foo){
    return new Promise((resolve, reject) => {
      //this will return 20% true, 80% false
      if (Math.random() > 0.8){
        resolve('Health check success');      
      } else {
        reject('Health check failed');
      }
    });
  }

  const brake = new Brakes(promiseCall, {timeout: 150});

  brake.fallback(fallbackCall);

  brake.healthCheck(healthCheckCall);

  brake.exec(false)
    .then((result) =>{
      console.log(`result: ${result}`);
    })
    .catch(err =>{
      console.error(`error: ${err}`);
    });

Sub Circuits

Brakes exposes the ability to create a main Brakes instance that can then contain subCircuits that all report to a central stats object. This allows all subCircuits to be tripped at the same time when the overall health of all the subCircuits crosses a defined threshold.

See examples/sub-circuit.js for a more complete example.

function promiseCall(foo){
  return new Promise((resolve, reject) =>{
    if (foo) resolve(foo);
    else reject(foo);
  });
}

const brake = new Brakes({
  timeout: 150,
  fallback: () => Promise.resolve('Response from fallback'),
});

const subCircuit1 = brake.subCircuit(promiseCall);
const subCircuit2 = brake.subCircuit(promiseCall);

subCircuit1.exec('bar')
  .then((result) =>{
    console.log(`result: ${result}`);
  })
  .catch(err =>{
    console.error(`error: ${err}`);
  });

// all stats are reported through the main brakes instance
brake.on('snapshot', snapshot => {
  console.log(`Stats received -> ${snapshot}`);
});

Demonstration

For a terminal based demonstration:

General Demo npm install && node examples/example1.js

Fallback Demo npm install && node examples/fallback-example.js

Health check Demo npm install && node examples/healthCheck-example.js

Hystrix Stream Demo npm install && node examples/hystrix-example.js

Sub Circuits npm install && node examples/sub-circuit.js

Methods

Method Argument(s) Returns Description
getGlobalStats N/A globalStats Returns a reference to the global stats tracker
static getGlobalStats N/A globalStats Returns a reference to the global stats tracker
exec N/A Promise Executes the circuit
fallback function (must return promise or accept callback) N/A Registers a fallback function for the circuit
healthCheck function (must return promise or accept callback) N/A Registers a health check function for the circuit
subCircuit function (required), function (optional), opts (optional) N/A Create a new sub circuit that rolls up into the main circuit it is created under.
on eventName, function N/A Register an event listener
destroy N/A N/A Removes all listeners and deregisters with global stats tracker.
isOpen N/A boolean Returns true if circuit is open

Events

Every brake is an instance of EventEmitter that provides the following events:

  • exec: Event on request start
  • failure: Event on request failure
  • success: Event on request success
  • timeout: Event on request timeout
  • circuitClosed: Event fired when circuit is closed
  • circuitOpen: Event fired when circuit is open
  • snapshot: Event fired on stats snapshot
  • healthCheckFailed: Event fired on failure of each health check execution

Configuration

Available configuration options.

  • name: string to use for name of circuit. This is mostly used for reporting on stats.
  • group: string to use for group of circuit. This is mostly used for reporting on stats.
  • bucketSpan: time in ms that a specific bucket should remain active
  • statInterval: interval in ms that brakes should emit a snapshot event
  • percentiles: array<number> that defines the percentile levels that should be calculated on the stats object (i.e. 0.9 for 90th percentile)
  • bucketNum: # of buckets to retain in a rolling window
  • circuitDuration: time in ms that a circuit should remain broken
  • waitThreshold: number of requests to wait before testing circuit health
  • threshold: % threshold for successful calls. If the % of successful calls dips below this threshold the circuit will break
  • timeout: time in ms before a service call will timeout
  • isFailure: function that returns true if an error should be considered a failure (receives the error object returned by your command.) This allows for non-critical errors to be ignored by the circuit breaker
  • healthCheckInterval: time in ms interval between each execution of health check function
  • healthCheck: function to call for the health check (can be defined also with calling healthCheck function)
  • fallback: function to call for fallback (can be defined also with calling fallback function)
  • isPromise: boolean to opt out of check for callback in function. This affects the passed in function, health check and fallback
  • isFunction: boolean to opt out of check for callback, always promisifying in function. This affects the passed in function, health check and fallback
  • modifyError: modifies the error message by adding circuit name. default is true.

Stats

Based on the opts.statInterval an event will be fired at regular intervals that contains a snapshot of the running state of the application.

// ...
  brake.on('snapshot', snapshot => {
    console.log(`Stats received -> ${snapshot}`);
  });
// ...

Example Stats Object

{ name: 'defaultBrake',
  group: 'defaultBrakeGroup',
  time: 1463297869298,
  circuitDuration: 15000,
  threshold: 0.5,
  waitThreshold: 100,
  stats:
   { failed: 0,
     timedOut: 0,
     total: 249,
     shortCircuited: 0,
     latencyMean: 100,
     successful: 249,
     percentiles:
      { '0': 100,
        '1': 102,
        '0.25': 100,
        '0.5': 100,
        '0.75': 101,
        '0.9': 101,
        '0.95': 102,
        '0.99': 102,
        '0.995': 102 }
    }
  }

Global Stats Stream

Brakes automatically tracks all created instances of brakes and provides a global stats stream for easy consumption and reporting on all brakes instances. These streams will aggregate all stat events into one single stream.

const globalStats = Brakes.getGlobalStats();

globalStats.getRawStream().on('data', (stats) =>{
  console.log('received global stats ->', stats);
});

Hystrix Dashboard

Using the global stats stream with a special transform, brakes makes it incredibly easy to generate a SSE stream that is compliant with the hystrix dashboard and turbine.

monitorer

Example:

const globalStats = Brakes.getGlobalStats();

/*
Create SSE Hystrix compliant Server
*/
http.createServer((req, res) => {
  res.setHeader('Content-Type', 'text/event-stream;charset=UTF-8');
  res.setHeader('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate');
  res.setHeader('Pragma', 'no-cache');
  globalStats.getHystrixStream().pipe(res);
}).listen(8081, () => {
  console.log('---------------------');
  console.log('Hystrix Stream now live at localhost:8081/hystrix.stream');
  console.log('---------------------');
});

To aid in testing it might be useful to have a local instance of the hystrix dashboard running:

docker run -d -p 8080:8080 -p 9002:9002 --name hystrix-dashboard mlabouardy/hystrix-dashboard:latest

Additional Reading: Hystrix Metrics Event Stream, Turbine, Hystrix Dashboard


Development

We gladly welcome pull requests and code contributions. To develop brakes locally clone the repo and use the following commands to aid in development:

npm install
npm run test
npm run test:lint
npm run coverage

Change Log

See: https://github.com/awolden/brakes/releases

brakes's People

Contributors

andrew avatar awolden avatar greenkeeperio-bot avatar jakepusateri avatar juancoen avatar livet01 avatar peter-vdc avatar seanohollaren avatar simenb avatar uyu423 avatar uyumazhakan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

brakes's Issues

Improve circuit breaker error

When a circuit breaker is opened the module should return a more meaninful error object than it currently does. Right now, it just returns a string. It should return an actual error that includes a snapshot of the current stats.

Reported request count never goes down?

I'm using version 2.5.1 and I try to view the stream from the hystrix dashboard but it seems to me that the requestCount never goes down even though the no one calls the Brakes "command":

image

The data that is reported by the hystrix stream looks like this:

data: {"type":"HystrixCommand","name":"MyName","group":"MyGroup","currentTime":1487861651232,"isCircuitBreakerOpen":false,"errorPercentage":0,"errorCount":0,"requestCount":53,"rollingCountBadRequests":0,"rollingCountCollapsedRequests":0,"rollingCountExceptionsThrown":0,"rollingCountFailure":0,"rollingCountFallbackFailure":0,"rollingCountFallbackRejection":0,"rollingCountFallbackSuccess":0,"rollingCountResponsesFromCache":0,"rollingCountSemaphoreRejected":0,"rollingCountShortCircuited":0,"rollingCountSuccess":53,"rollingCountThreadPoolRejected":0,"rollingCountTimeout":0,"currentConcurrentExecutionCount":0,"latencyExecute_mean":758,"latencyExecute":{"0":600,"25":664,"50":724,"75":868,"90":906,"95":912,"99":951,"100":951,"99.5":951},"latencyTotal_mean":15,"latencyTotal":{"0":600,"25":664,"50":724,"75":868,"90":906,"95":912,"99":951,"100":951,"99.5":951},"propertyValue_circuitBreakerRequestVolumeThreshold":100,"propertyValue_circuitBreakerSleepWindowInMilliseconds":5000,"propertyValue_circuitBreakerErrorThresholdPercentage":0.5,"propertyValue_circuitBreakerForceOpen":false,"propertyValue_circuitBreakerForceClosed":false,"propertyValue_circuitBreakerEnabled":true,"propertyValue_executionIsolationStrategy":"THREAD","propertyValue_executionIsolationThreadTimeoutInMilliseconds":800,"propertyValue_executionIsolationThreadInterruptOnTimeout":true,"propertyValue_executionIsolationThreadPoolKeyOverride":null,"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests":20,"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,"propertyValue_requestCacheEnabled":false,"propertyValue_requestLogEnabled":false,"reportingHosts":1}

My Brakes "command" looks like this (defined "globally" in the module):

const myHystrixCommand = new Brakes(queryBitBucket, {
    statInterval: 1000,
    threshold: 0.5,
    circuitDuration: 5000,
    timeout: 5000,
    name: 'MyName',
    group: 'MyGroup',
    isPromise: true
});

Am I doing something wrong or why is the requestCount always showing 53? (which is the total number of calls I've made).

Circuits should be nameable

When a circuit is created you should be able to pass a name and have that state stored internally within the circuit breaker.

What if something is a callback/async function AND a Promise

Working with request-promise ;

e.g.

var rp = require('request-promise');
var Brakes = require('brakes');

var options = {
  uri: 'http://some-json-api.com/',
  json: true
};

var brake = new Brakes(request, { timeOut: 1000 });

return brake.exec(options)
  .then( response => {
    return processResponse(response);
  })
  .catch(e => {
    return processError(e.statusCode);
  });

request-promise wraps the normal request object, which is a function that has a callback parameter, and makes it return a promise. Wrapping it causes the behaviour to change because Circuit prefers async callback functions ; instead of the usual behaviour with these options which is for response to be an object representing the processed JSON response, and for errors to end up in .catch, all responses including errors end up in the .then() block because Circuit uses the default promise callback style instead of request-promise's Promise wrapping.

Not sure the best way to resolve this but maybe an option to force the Promise treatment instead of using the callback style? Not a big thing to write code to work around this (and ditch request-promise in the process, since we're not using it's features).

Add isSuccess option?

When using fetch to do remote http calls we will get a promise back. If the service response is a 5xx satus this is still considered a "resolving" promise. When this happens i would brakes to consider this as a failure. Today this means I have convert the resolving promise in to a rejecting one, which feels a bit awkward.

If there where an isSuccess option I could easily verify that the response code is not in the 500-range.

Circuit breaker design is a bit off

Hi.
So your circuit breaker has the following flow:

closed -> errorsOverThreshold -> open ->

This could potentially do more harm than good.

I'd expect the circuit breaker to NOT have a timer (the healthcheck is a nice addition), and let a request come in once in a while to check if the circuit can potentially be closed, if so, let a bit more requests come in and so on. This should be throttled. (hystrixjs does it,albeit not ideal but better: https://bitbucket.org/igor_sechyn/hystrixjs/src/a8e30f520660a696362b3605d959794883c5368b/lib/command/CircuitBreaker.js?at=master&fileviewer=file-view-default#CircuitBreaker.js-53)

I think you should reconsider that design on your end.

Fallbacks executions

Hello!

I am implementing this circuit breaker for node.js and i noted something strange. In a normal flow for my, in this case, API call i always see that the fallback functions is being called, the service is working ok and the response is totally fine, but whenever i set a breakpoint in the fallback, it stops right there, even if the flow goes ok.
I am not sure why is it executing if there are not errors in the api call.

Thanks!

Single-arg fat arrow function breaks brakes

Sending a fat arrow function without parens (one arg only) into brakes causes an exception: TypeError: Cannot read property '1' of null

It appears to be breaking on the regex in getFnArgs in lib/utils.js:

/^[function\s]?.*?\(([^)]*)\)/

nvmrc is right?

nvm use says 4.8.4 but the syntax used isn't being transpiled for that.. what is the last version of node that we are supposed to provide support for?

Supporting nodejs in cluster mode + 'this' context in callback/fallback

Hi.
I see that there is not support for cluster mode. Could this be done with minimum overhead (aggregate before sending to master)?
Because stats are collected on workers, and master will expose the SSE stream, I'm unsure this works out of the box.

As well as context support. for example, I'd like to execute a method from my class. Currently, I see that you're using .apply(this (where this is Circuit.js), args);. Is it possible to configure this per circuit?

thanks!

Brakes modifies original error message

Hi

brakes modifies error messages coming from a downstream, my downstream service returns
{ message: 'We are unable to process your request, please try again later', status: 400 }, then brakes change the message to [Breaker: myservice] [object] [object]. I can't display that message to the user. How can I get the original error message?

What happen when the circuit is opened?

Probably it is my lack in understanding the circuit brake pattern, but let's take examples/healthCheck-example.js. What happen when the circuit is opened?

Timing the statInterval based on bucketNum & bucketSpan

In short, I am struggling to understand how to minimize redundancy in the data/stats within the "snapshot" events.

My goal is to ensure that each "snapshot" contains only data which has been measured after the previous "snapshot." In other words, I do not want a "snapshot" to report on data which has already been reported on in a previous "snapshot."

My issue is that I cannot seem to properly assess which bucket or buckets are included in each "snapshot" event.

For example, if I configured the brakes instance options to have say:
{ bucketNum: 6, bucketSpan: 60000, statInterval: 6 * 60000 }, would each "snapshot" event report on all the data measured by all 6 buckets in the last 6 minutes? Or would it report on only the data measured in the very last bucket?

Again, my goal is to select the above options such that my "snapshot" stats do not report data that has already been included in a previous "snapshot," while also not missing any unreported data in the time elapsed since the last "snapshot."

Any insight into this would be very much appreciated, and thank you in advance!

Adding isFailure function

enabling isFailure setting by calling a function (the same way available for healthCheck and fallback) would make the structure more consistent.
Thanks!

transpile lib?

Is there a plan to transpile code for supporting browser usage or maybe atleast providing support in some areas? Lots of times node_modules are excluded in webpack settings and code that isn't transpiled (static or =>) end up throwing errors in isomorphic land...

CPU growth

Using the default setting, we experienced growth in CPU usage and increasing performance degradation throughout the day. We were forced to add a cron job to bounce the service every 6 hours. When I removed brakes, CPU usage returned to normal.

Do you want TypeScript types in this repo?

Hi! 👋

I'm planning to use Brakes in a TypeScript project, and noticed that there are no typings available in the repo or via DefinitelyTyped. Before spending a lot of time, I have two questions.

First: Would you be interested in migrating this repo to TypeScript?

I naïvely cloned and started typing the whole project, in the hopes that I would be able to annotate a few variables and get type definitions for free. A couple of hours later, I realised the error of my ways, and gave up. The almost-functional result is available at https://github.com/awolden/brakes/compare/master...theneva:typescript?expand=1

If no, I won't spend any more time on typing the whole thing. For now, I've resigned to manually writing a type definition file for the Brakes class.

Second question: Would you accept the module type definition file directly into this repo (similar to what is done in for example https://github.com/sindresorhus/copy-text-to-clipboard), or should I go via DefinitelyTyped to publish it?

Runaway memory growth/CPU utilization

Hi,

I've implemented brakes in a Lambda function in AWS, and have seen a gradual performance degradation over the course of about an hour.

We can observe the lambda running slower and slower up until a point where it takes about 10-30 seconds for it to execute, whereas on a fresh startup, requests are processed within a few milliseconds.

Memory consumption appears to increase linearly.

Our usage looks like this:

class Foo extends Service {

    init(stuff) {
        return super.init(stuff)
        .then(res => {
            this.getFooCB = new Brakes(
                 this.getFoo.bind(this), // Bind the scope, or won't have access to class vars
                 {
                     circuitDuration: 15000,
                     timeout: 10000,
                     waitThreshold:0 // After how many requests should we check?
                 });            
        });
    }

   getFoo(stuff) {
      // do some async thing
   }

  useFoo() {
    return this.getFooCB.exec(stuff).then(res => {...});
  }

Slave circuits not working as per the examples

I'm using the following code:

const Brakes = require('brakes');
const fallback = require('../lib/circuit-breaker-fallback');

const breakerOpts = {
      timeout: 150,
      fallback,
};

const promisifiedPUT = () => {
  return new Promise((resolve, reject) => {
    serviceCall(optsObj, (err, res) => {
      if (err) {
          reject(err);
      } else {
          resolve(res);
      }
    });
  });
};

const brake = new Brakes(breakerOpts);
const slaveCircuit = brake.slaveCircuit(promisifiedPUT);

slaveCircuit.exec()
  .then(handleSuccess)
  .catch(handleFailure);
...

I get the following error:

/path/node_modules/brakes/lib/Brakes.js:174
      throw new Error(consts.NO_FUNCTION);
      ^

Error: This instance of brakes has no master circuit. You must use a slave circuit.
    at Brakes.fallback (/path/node_modules/brakes/lib/Brakes.js:174:13)
    at new Brakes (/path/node_modules/brakes/lib/Brakes.js:71:12)
    at Object.<anonymous> (/path/server/models/greeting.js:17:23)

Following the trail, I get to here and here. This suggests to me that the constructor requires a function and a settings object, but the examples only show a setting object being passed in:

const brake = new Brakes({
  statInterval: 2500,
  threshold: 0.5,
  circuitDuration: 15000,
  timeout: 250
});

const greetingBrake = new Brakes(() => {}, breakerOpts); seems to work, but I'm not sure that's how I'm supposed to be using the library.

Any thoughts? Suggestions?

Two Hystrix servers

Why in the examples/hystrix-example.js there are two Hystrix servers created? One is not enough?

Brakes doesn't work with service calls that are arrow functions

const Brakes = require('brakes');

const prom = (foo) => {
    return new Promise((resolve, reject) => {
        resolve(foo);
    });
}

const brake = new Brakes(prom);

brake.exec('foo').then(result => {
    console.log(result)
}, err => {
    console.error(err);
});

Causes the following error:

./node_modules/brakes/node_modules/promisify-node/utils/args.js:9
  var args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1];
                                                              ^

TypeError: Cannot read property '1' of null
    at module.exports (./node_modules/brakes/node_modules/promisify-node/utils/args.js:9:63)
    at processExports (./node_modules/brakes/node_modules/promisify-node/index.js:61:29)
    at module.exports (./node_modules/brakes/node_modules/promisify-node/index.js:164:10)
    at new Brakes (./node_modules/brakes/lib/Brakes.js:32:25)
    at Object.<anonymous> (./index.js:10:15)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Function.Module.runMain (module.js:467:10)

This seems to be caused by reflection that promisify-node is doing under the covers to detect meta data about the passed in function.

Update Options

In my code I need to update options for a brake already instantiated (to be more specific I need to update the timeoutvalue).

I'm using Brakes internals to do that:

brake._masterCircuit._opts = Object.assign( {}, brake._masterCircuit._opts, newOptions );

Could be a nice to have an updateOptions(opts) method attached to brake instance to have the same result without touching internals.

2.6.0 breaks non-string errors

2.6.0 introduced "Add the breaker name to circuit rejections".

This breaks existing installations where err.message is not a string:

"[Breaker: Basket] [object Object]" is not helpful. Please either check if err.message is a String, or make adding the circuit-name optional.

Open circuit based on number of failures the last n ms, not n requests

We'd like to have a sliding window being checked for percentage of failures, instead of a definite number of requests. Levee has this, for instance.
Any particular reason why this is not implemented here? Would you be opposed to a PR adding it?

E.g. There is more than 25% failures the last second, open the circuit.

Edge case where a circuit can be opened but report failure stats below the circuit breaking threshold

Found an edge case where the circuit can be opened, but brakes is showing request stats that indicate that it shouldn't have opened (because failure percentage is below threshold).

I think what might be happening is:

  1. With several requests pending, one comes back as a failure.
  2. Brakes sees this puts the server over the user-specified failure threshold (e.g. 50%) and opens the circuit, reporting a failure rate of 51%.
  3. The other pending requests return successfully after the circuit has opened, which alters the request stats (bringing failure percentage down to 48%).
  4. Brakes rejects all new requests to the server, but reports that it's doing so because the failure rate (48%) is higher than the failure threshold (50%).

This isn't a case of brakes doing the wrong thing. It's just a rare case (which will probably only happen at low numbers of requests) where the way brakes reports things could be confusing.

Incorrect terminology around circuit breaking

Right now when a circuit is broken it registers it as being closed, and when a circuit is still viable it registers it as open. These are backwards. The terminology should be changed such that:

open: requests automatically fail and and circuit is tripped
closed: requests are properly flowing through to the request function passed

waitThreshold is too high by default

The default setting for waitThreshold is currently set to 100 which is way too high IMO.
Imagine a scenario in which the backend system becomes unresponsive and stays broken for a few hours. Assuming you set the circuitDuration to 30 seconds, you would dish out 100 failing requests before opening the circuit breaker again - every 30 seconds. Don't you think that 100 requests is a bit too much for a system which is already struggling?

Of course it is up to the user to tweak the waitThreshold according to their needs but speaking of myself personally, I was very confused when I tried the library in my project and the circuit breaker wouldn't open immediately even though the failure rate was at 100%.

I would think that a waitThreshold of 5 to 10 would be much more appropriate.

GlobalStatsStream does not track all instances after update to Node v12.16.0

Brakes version: 3.0.1

After having updated to Node v12.16.0 globalStatsStream does not track the instances correctly. I have two brakes instantiated but only the first brake does notify via stream for only one first event. Example:

        const brake1 = new Brakes(promiseCall, {
            name: "MyDefault",
            bucketSpan: 5000,
            bucketNum: 3,
            waitThreshold: 5,
            timeout: 200,
            threshold: 0.5,
            circuitDuration: 30000,
        });

        const brake2 = new Brakes(promiseCall, {
            name: "XXXX",
            bucketSpan: 5000,
            bucketNum: 3,
            waitThreshold: 5,
            timeout: 200,
            threshold: 0.5,
            circuitDuration: 30000,
        });

        Brakes.getGlobalStats().getHystrixStream().on('data', (stats) => {
            console.log('received global stats ->', stats);
        });

With Node v12.15.0 it is working as expected. Alle events are propagated via the stats stream. Any known issues about this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.