{"_id":"@bennadel/circuit-breaker","_rev":"3-fc7211fff96a47db26c4ee8c3873de9b","name":"@bennadel/circuit-breaker","description":"A flexible circuit breaker for Node.js (requires ES6 class modules).","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"@bennadel/circuit-breaker","version":"0.0.1","description":"A flexible circuit breaker for Node.js (requires ES6 class modules).","repository":{"type":"git","url":"git+https://github.com/bennadel/Node-Circuit-Breaker.git"},"main":"./lib/index.js","scripts":{"test":"mocha"},"keywords":["circuit","breaker","circuit breaker","availability","reliability","fail fast"],"author":{"name":"Ben Nadel"},"license":"ISC","devDependencies":{"chai":"^4.0.2","mocha":"^3.4.2"},"gitHead":"abea1e4e645eba694f6844b839165b3080ab8271","bugs":{"url":"https://github.com/bennadel/Node-Circuit-Breaker/issues"},"homepage":"https://github.com/bennadel/Node-Circuit-Breaker#readme","_id":"@bennadel/circuit-breaker@0.0.1","_shasum":"c2d0862c459e0d13dbc0626e1885b4407b195e4b","_from":".","_npmVersion":"4.2.0","_nodeVersion":"7.10.0","_npmUser":{"name":"bennadel","email":"ben@bennadel.com"},"dist":{"shasum":"c2d0862c459e0d13dbc0626e1885b4407b195e4b","tarball":"https://registry.npmjs.org/@bennadel/circuit-breaker/-/circuit-breaker-0.0.1.tgz","integrity":"sha512-w3nfktXKtfQzyi8czwnxFxBIfseKYMZtIA1YRXyIPM+ktIvQE1nBimMdOjy1uHvxUQUp/JvjalT6lxtfuaex3Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDDHW/gnFmw+oRKcesJgTHVwn84KMTbqHn9b7QQszld/wIhAJCHj74pIXIV09GPBmUIX1v4h5I4N/4lVWXlhPSauVoB"}]},"maintainers":[{"name":"bennadel","email":"ben@bennadel.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/circuit-breaker-0.0.1.tgz_1499682023622_0.26750621385872364"}}},"readme":"\n# Node Circuit Breaker\n\nby [Ben Nadel][bennadel] (on [Google+][googleplus])\n\nThis is a **Node.js** implementation of the **Circuit Breaker** pattern as popularized \nin [Michael T. Nygard's book - Release It!][release-it]. The Circuit Breaker is intended \nto proxy the consumption of upstream resources such that failures in the upstream resource\npropagate to the current system in a predictable manner. To be clear, the Circuit Breaker\ndoesn't prevent failures; rather, it helps your application manage failures proactively, \nfailing fast and / or providing fallback values when applicable.\n\nThe Circuit Breaker proxies the consumption of upstream resources; but, it does not have\nintimate knowledge of the upstream resource. As such, the scope of the Circuit Breaker \ncan be as course or as granular as you think is appropriate. For example, you can have \none Circuit Breaker that represents an entire upstream resource. Or, you can create an\nindividual Circuit Breaker _for each method_ in an upstream resource. The more granular\nyour Circuit Breakers, the less likely you are to get false positives.\n\n## Default Usage\n\nEach Circuit Breaker is a composition of several objects that work together to provide \nthe tracking and the fail-fast functionality. Fortunately, you don't have to know about \nthis unless you are building custom implementations. All you have to do is ask the \nCircuit Breaker Factory for an instance with the given settings.\n\nThe easiest way to create a Circuit Breaker is to create one with no settings at all. \nDoing so will create a Circuit Breaker with \"good\" defaults:\n\n```js\nvar CircuitBreakerFactory = require( \"@bennadel/circuit-breaker\" ).CircuitBreakerFactory;\n\nvar circuitBreaker = CircuitBreakerFactory.create();\n\n// Invoke as closure.\ncircuitBreaker.execute(\n    function() {\n        return( upstreamResource.load() );\n    }\n);\n\n// Invoke as closure with context and arguments.\ncircuitBreaker.executeInContext(\n    upstreamResource,\n    function( param1, param2 ) {\n        return( this.load( param1, param2 ) );\n    },\n    [ \"arg1\", \"arg2\" ]\n);\n\n// Invoke as method on an object.\ncircuitBreaker.executeMethod( upstreamResource, \"load\", [ \"arg1\", \"arg2\" ] );\n```\n\nAs you can see, there are three ways to run commands through a Circuit Breaker:\n\n* `execute( command [, fallback ] )`\n* `executeInContext( context, command [, args [, fallback ] ] )`\n* `executeMethod( context, methodName [, args [, fallback ] ] )`\n\nEach `execute*` method returns a Promise that will be fulfilled in resolution if the \nexecution was successful; or, fulfilled in rejection if the execution threw an error (or \nwas bypassed based on the state of the Circuit Breaker). The underlying method / function\nthat is being invoked should return a Promise or a synchronous value. Or, it can omit a \nreturn if none is needed.\n\n## Configuration Usage\n\nThe `.create()` method of the Circuit Breaker Factory works without any arguments; but, \nyou can provide a hash of settings that will be used to generate the Circuit Breaker. \nEvery one of the following settings is _optional_:\n\n* `id` - The unique identifier of the underlying state instance, which is used for \n  logging.\n* `requestTimeout` - The time (in milliseconds) that a pending request is allowed to hang\n  (ie, not complete) before being timed-out in error.\n* `volumeThreshold` - The number of requests that have to be completed (within the \n  rolling metrics window) before failure percentages can be calculated.\n* `failureThreshold` - The percent (in whole numbers) of failures that can occur in the\n  rolling metrics window before the state of the Circuit Breaker switches to _opened_.\n* `activeThreshold` - The number of concurrent requests that can hang (ie, not complete) \n  before the state of the Circuit Breaker switches to _opened_.\n* `isFailure` - The function that determines if the given failure is an error; or, if \n  it should be classified as a success (such as a 404 response).\n* `fallback` - The global fallback to be used for all executions in the Circuit Breaker \n  (which can be overridden locally with each execution).\n* `monitor` - The monitor -- Function or instance -- for external logging (ex, StatsD logging).\n* `bucketCount` - The number of buckets to be used to collect rolling stats in the \n  rolling metrics window.\n* `bucketDuration` - The duration (in milliseconds) of each bucket within the rolling\n  metrics window.\n\n_**NOTE**: The duration of the rolling metrics window will be `bucketCount * bucketDuration`.\nThis is also the amount of time that the Circuit Breaker will **remain opened** after \nfailing before allowing a \"health check\" request to execute._\n\n```js\nvar CircuitBreakerFactory = require( \"@bennadel/circuit-breaker\" ).CircuitBreakerFactory;\n\nvar circuitBreaker = CircuitBreakerFactory.create({\n    id: \"Remote API\",\n    requestTimeout: 5000,\n    volumeThreshold: 10,\n    failureThreshold: 10, // Percent (as in 1 failure in 10 responses trips the circuit).\n    activeThreshold: 50,\n    isFailure: function( error ) {\n        return( ! is404( error ) );\n    },\n    fallback: { /* Fallback value. */ },\n    monitor: function( eventType, eventData ) {\n        console.log( eventType, eventData );\n    },\n    bucketCount: 30,\n    bucketDuration: 1000\n});\n```\n\n## Fallback Values\n\nThe primary goal of the Circuit Breaker is to \"fail fast\" if the upstream resource \nappears to be unhealthy. However, the secondary goal of the Circuit Breaker is to provide\na better user experience. That means that if a meaningful fallback value can be provided\nin the case of error, the Circuit Breaker will facilitate this approach.\n\nThe fallback value can be a Function, a Promise, or any static value. If it's a Function,\nit should return either a Promise or a static value. Fallback values can be defined when\nthe Circuit Breaker is created:\n\n```js\nvar CircuitBreakerFactory = require( \"@bennadel/circuit-breaker\" ).CircuitBreakerFactory;\n\nvar circuitBreaker = CircuitBreakerFactory.create({\n    id: \"Remote API\",\n    fallback: { /* Fallback value. */ }\n});\n```\n\nBut, they can also be provided at the time of execution (regardless of whether or not a\nglobal fallback value was provided):\n\n```js\nvar CircuitBreakerFactory = require( \"@bennadel/circuit-breaker\" ).CircuitBreakerFactory;\n\nvar circuitBreaker = CircuitBreakerFactory.create({\n    id: \"Remote API\",\n    fallback: { /* Fallback value. */ }\n});\n\ncircuitBreaker\n    .execute(\n        function() {\n            throw( new Error( \"Network Error\" ) );\n        },\n        { /* Local fallback value. */ }\n    )\n    .then(\n        function( result ) {\n            console.log( result ); // Will be LOCAL fallback value.\n        }\n    )\n;\n```\n\nIf the fallback value is a Function and the execution was provided with a _context_ and\n_arguments_, the same _context_ and _arguments_ will be used to invoke the Fallback.\n\n## Circuit Breakers Are Scary -- What If I Get It Wrong?\n\nTo be honest, it can be scary - the idea of putting something into production that\nwill purposefully block calls to proxied systems. If you pick an error threshold that's\ntoo low, you may start blocking requests too quickly. If you pick an active threshold \nthat's too high, you may clobber the upstream resource.\n\nLuckily, you don't have to dive right into the deep-end. Instead, you can deploy a \n**passive Circuit Breaker** that will log all of the traffic; but, _will never fail open_,\nno matter how unhealthy the upstream resource becomes. This way, you can spend some time\npassively gathering metrics about your API usage (including counts, durations, and \nerrors) before switching over to an active Circuit Breaker with tailored settings.\n\nSince this is a passive Circuit Breaker (that never opens), there are fewer settings:\n\n* `id` - The unique identifier of the underlying state instance, which is used for \n  logging.\n* `isFailure` - The function that determines if the given failure is an error; or, if \n  it should be classified as a success (such as a 404 response).\n* `fallback` - The global fallback to be used for all executions in the Circuit Breaker \n  (which can be overridden locally with each execution).\n* `monitor` - The monitor -- Function or instance -- for external logging (ex, StatsD \n  logging).\n\n```js\nvar CircuitBreakerFactory = require( \"@bennadel/circuit-breaker\" ).CircuitBreakerFactory;\n\nvar circuitBreaker = CircuitBreakerFactory.createPassive({\n    id: \"Remote API\",\n    monitor: function logEvent( eventType, eventData ) {\n        // Log statsD metrics about count and duration.\n        // Log errors.\n    }\n});\n\n// This error will result in a rejected promise; but, the Circuit Breaker will always\n// remain closed, allowing requests to be executed.\ncircuitBreaker.execute(\n    function() {\n        throw( new Error( \"Network Error\" ) );\n    }\n);\n```\n\nOnce you've had a chance to monitor your Circuit Breakers, you can start switching your\n`.createPassive()` factory calls with `.create()` factory calls using settings that you\nknow correspond to the collected base-line of metrics. And, you can sleep well at night.\n\n## Logging And Monitoring\n\nBy default, the Circuit Breaker quietly discards all internal events. However, you will\nprobably want to log Errors and record StatsD metrics in your application. To do this, \nyou can provide a logging Function as the `monitor` argument:\n\n```js\nvar CircuitBreakerFactory = require( \"@bennadel/circuit-breaker\" ).CircuitBreakerFactory;\n\nvar circuitBreaker = CircuitBreakerFactory.create({\n    id: \"Remote API\",\n    monitor: function logEvent( eventType, eventData ) {\n        console.log( eventType, eventData );\n    }\n});\n```\n\nThis logging Function will be called with the following `eventType`values:\n\n* `closed` passing `eventData` properties `{ stateSnapshot }`\n* `execute` passing `eventData` properties `{ stateSnapshot }`\n* `emit` passing `eventData` properties `{ stateSnapshot }`\n* `failure` passing `eventData` properties `{ stateSnapshot, duration, error }`\n* `fallbackEmit` passing `eventData` properties `{ stateSnapshot }`\n* `fallbackFailure` passing `eventData` properties `{ stateSnapshot, error }`\n* `fallbackMissing` passing `eventData` properties `{ stateSnapshot }`\n* `fallbackSuccess` passing `eventData` properties `{ stateSnapshot }`\n* `opened` passing `eventData` properties `{ stateSnapshot }`\n* `shortCircuited` passing `eventData` properties `{ stateSnapshot, error }`\n* `success` passing `eventData` properties `{ stateSnapshot, duration }`\n* `timeout` passing `eventData` properties `{ stateSnapshot, duration, error }`\n\nUnder the hood, this is actually using your `logEvent()` Function to complete a concrete\nimplementation of the `AbstractLoggingMonitor`. If you don't provide a Function, you can \nprovide a Class that extends either the `Monitor` class or the `AbstractLoggingMonitor`\nclass. If you extend the `AbstractLoggingMonitor` base class, you only have to override \nthe `logEvent()` method:\n\n```js\nvar AbstractLoggingMonitor = require( \"@bennadel/circuit-breaker\" ).AbstractLoggingMonitor;\nvar CircuitBreakerFactory = require( \"@bennadel/circuit-breaker\" ).CircuitBreakerFactory;\n\nclass MyMonitor extends AbstractLoggingMonitor {\n\n    constructor( statsD ) {\n\n        super();\n        this._statsD = statsD;\n\n    }\n\n    logEvent( eventType, eventData ) {\n\n        stats.increment( `circuit-breaker.${ eventType }` );\n\n    }\n\n}\n\n// ....\n\nvar circuitBreaker = CircuitBreakerFactory.create({\n    id: \"Remote API\",\n    monitor: new MyMonitor( stats )\n});\n```\n\nHowever, if you extend the `Monitor` class, you can override any of the `log*` methods:\n\n```js\nvar CircuitBreakerFactory = require( \"@bennadel/circuit-breaker\" ).CircuitBreakerFactory;\nvar Monitor = require( \"@bennadel/circuit-breaker\" ).Monitor;\n\nclass MyMonitor extends Monitor {\n    \n    logClosed( stateSnapshot ) {\n        /* ... */\n    }\n\n    logOpened( stateSnapshot ) {\n        /* ... */\n    }\n\n}\n\n// ....\n\nvar circuitBreaker = CircuitBreakerFactory.create({\n    id: \"Remote API\",\n    monitor: new MyMonitor()\n});\n```\n\nThe `Monitor` class provides the following default, no-op (No Operation) methods, which\nmeans you only have to override the ones that are meaningful to your application:\n\n* `logClosed( stateSnapshot )` -- I log the point at which the Circuit Breaker state \n  moves from opened to closed.\n* `logExecute( stateSnapshot )` -- I log the point at which the execution is accepted by\n  the state of the Circuit Breaker and the underlying command is about to be invoked.\n* `logEmit( stateSnapshot )` -- I log the point at which the request has entered the \n  Circuit Breaker but has not yet been approved for execution.\n* `logFailure( stateSnapshot, duration, error )` -- I log the point at which the \n  execution has ended in error. This only accounts for non-Circuit Breaker errors \n  (see, logTimeout() and logShortCircuited() events).\n* `logFallbackEmit( stateSnapshot )` -- I log the point at which a non-successful \n  execution (due to error, timeout, or short-circuiting) is being evaluated for a\n  fallback response.\n* `logFallbackFailure( stateSnapshot, error )` -- I log the point at which an existing\n  fallback function resolved in error.\n* `logFallbackMissing( stateSnapshot )` -- I log the point at which a failed execution \n  has no fallback defined.\n* `logFallbackSuccess( stateSnapshot )` -- I log the point at which a fallback value has\n  successfully stood-in for a failed or bypassed execution.\n* `logOpened( stateSnapshot )` -- I log the point at which the Circuit Breaker state \n  moves from closed to opened.\n* `logShortCircuited( stateSnapshot, error )` -- I log the point at which an execution\n  is bypassed because the Circuit Breaker is currently in an opened state.\n* `logSuccess( stateSnapshot, duration )` -- I log the point at which an execution has\n  resolved successfully.\n* `logTimeout( stateSnapshot, duration, error )` -- I log the point at which a long-\n  running execution has been explicitly timed-out in error.\n\nThe `stateSnapshot` object passed to the `Monitor` methods (and to the \n`AbstractLoggingMonitor` `logEvent()` method) contains identification and metric \ninformation about the State being used to power the Circuit Breaker. Since one Circuit\nBreaker can share state with another Circuit Breaker, there's not too much sense in \nidentifying the Circuit Breakers themselves; as such, the State becomes the meaningful\ninformation for logging and monitoring. Each `stateSnapshot` provided by the default \nimplementation uses the following structure:\n\n```json\n{\n\t\"id\": \"Circuit Breaker for API\",\n\t\"closed\": true,\n\t\"settings\": {\n\t\t\"requestTimeout\": 0,\n\t\t\"volumeThreshold\": 0,\n\t\t\"failureThreshold\": 0,\n\t\t\"activeThreshold\": 0\n\t},\n\t\"metrics\": {\n\t\t\"emit\": 0,\n\t\t\"execute\": 0,\n\t\t\"success\": 0,\n\t\t\"failure\": 0,\n\t\t\"timeout\": 0\n\t},\n\t\"totalMetrics\": {\n\t\t\"emit\": 0,\n\t\t\"execute\": 0,\n\t\t\"success\": 0,\n\t\t\"failure\": 0,\n\t\t\"timeout\": 0\n\t},\n\t\"current\": {\n\t\t\"activeRequestCount\": 0\n\t}\n}\n```\n\n## Building Your Own `State` Implementation\n\nThe Circuit Breaker is designed to be a composite of several different classes all \nworking together to accomplish one goal. The reason for this composition was to allow\ncustom implementations to be designed if desired. Ideally, if you want a custom \nimplementation, the only class you should have to provide is the `State` class. The\n`CircuitBreaker` class manages the control-flow; but, it uses the `State` implementation\nto power that control-flow. If you want to provide your own `State` implementation, you \nhave to provide a class that exposes the following methods:\n\n* `canPerformHealthCheck()`\n* `getSnapshot()`\n* `isOpened()`\n* `isClosed()`\n* `getTimeout()`\n* `trackExecute()`\n* `trackEmit()`\n* `trackFailure( duration, error )`\n* `trackFallbackEmit()`\n* `trackFallbackFailure( error )`\n* `trackFallbackMissing()`\n* `trackFallbackSuccess()`\n* `trackShortCircuited( error )`\n* `trackSuccess( duration )`\n* `trackTimeout( duration, error )`\n\nWhat you do inside these methods is completely up to you. But, they have to exist since\nthe `CircuitBreaker` is going to call them. The general control-flow for the \n`CircuitBreaker` follows this plan:\n\n* Top-level `execute*()` method is called.\n* Call `trackEmit()`.\n* Check to see if `isOpened()`.\n* If opened:\n* * Check to see if `canPerformHealthCheck()`\n* * * If can perform health check, proceed to execution.\n* If can execute command:\n* * Call `trackExecute()`.\n* * Setup timeout timer using `getTimeout()`.\n* * Invoke underlying command.\n* On resolution:\n* * Call `trackSuccess()`.\n* On rejection:\n* * Check type of error:\n* * * If `OpenError` call `trackShortCircuited()`.\n* * * If `TimeoutError` call `trackTimeout()`.\n* * * Otherwise call `trackFailure()`.\n* * Call `trackFallbackEmit()`.\n* * Check to see if a fallback was provided (locally or globally).\n* * * If fallback was provided:\n* * * * Execute fallback.\n* * * * On resolution:\n* * * * * Call `trackFallbackSuccess()`.\n* * * * On rejection:\n* * * * * Call `trackFallbackFailure()`.\n* * * If no fallback was provided:\n* * * * Call `trackFallbackMissing()`.\n\nOnce you have a custom `State` implementation, you can construct a `CircuitBreaker`:\n\n```js\nvar CircuitBreaker = require( \"@bennadel/circuit-breaker\" ).CircuitBreaker;\n\nvar state = new CustomStateImplementation();\n\nvar circuitBreaker = new CircuitBreaker( state [, globalFallback] );\n```\n\n### Guarantees Around Synchronous State Tracking\n\nSince the Circuit Breaker generates and returns Promises around the execution of black-\nboxed commands, many of the methods on the `State` instance will be invoked \nasynchronously. However, the following series of methods are _guaranteed to be invoked\nsynchronously_ within the same tick of the Node.js event loop:\n\n* `trackEmit()`\n* `isOpened()`\n* `canPerformHealthCheck()` - called only if `isOpened()` returns `true`.\n* `trackExecute()`\n\nSince Node.js runs in a single process, you can assume that these four methods will be\ncalled without any race conditions.\n\n### All Metrics Should Be Stored In-Memory\n\nIf you are building your own `State` implementation, you may be tempted to share metrics\nacross different Node.js processes (or machines). For example, you may be tempted to \nstore metrics in a shared Redis instance that can be consumed be every instance of a \nCircuit Breaker that proxies a single resource. **DO NOT DO THIS**. Not only does the \nCircuit Breaker expect `State` methods to run synchronously; but, trying to share state \noffers no real value-add. Since the failure-tracking is based on _percentages_, sharing\nstate won't make the percentages more accurate. In fact, sharing state across processes \ncould lead to false-positives if a particular process or machine is having issues (such\nas configuration issues that don't affect other processes or machines).\n\n## Package Exports\n\nThis Circuit Breaker package exports the following public members:\n\n* `OpenError`\n* `StateError`\n* `TimeoutError`\n* `Metrics`\n* `AbstractLoggingMonitor`\n* `Monitor`\n* `State`\n* `CircuitBreaker`\n* `CircuitBreakerFactory`\n\n[bennadel]: http://www.bennadel.com\n[googleplus]: https://plus.google.com/108976367067760160494?rel=author\n[release-it]: https://www.bennadel.com/blog/3162-release-it-design-and-deploy-production-ready-software-by-michael-t-nygard.htm\n","maintainers":[{"name":"bennadel","email":"ben@bennadel.com"}],"time":{"modified":"2022-06-12T15:10:52.109Z","created":"2017-07-10T10:20:24.738Z","0.0.1":"2017-07-10T10:20:24.738Z"},"homepage":"https://github.com/bennadel/Node-Circuit-Breaker#readme","keywords":["circuit","breaker","circuit breaker","availability","reliability","fail fast"],"repository":{"type":"git","url":"git+https://github.com/bennadel/Node-Circuit-Breaker.git"},"author":{"name":"Ben Nadel"},"bugs":{"url":"https://github.com/bennadel/Node-Circuit-Breaker/issues"},"license":"ISC","readmeFilename":"README.md","users":{"cbetancourt":true}}