diff --git a/README.md b/README.md index c70715a6..f718469a 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ a network operation, or disk read, for example. Wrap those functions up in a `CircuitBreaker` and you have control over your destiny. ```javascript -const circuitBreaker = require('opossum'); +const CircuitBreaker = require('opossum'); function asyncFunctionThatCouldFail (x, y) { return new Promise((resolve, reject) => { @@ -46,7 +46,7 @@ const options = { errorThresholdPercentage: 50, // When 50% of requests fail, trip the circuit resetTimeout: 30000 // After 30 seconds, try again. }; -const breaker = circuitBreaker(asyncFunctionThatCouldFail, options); +const breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options); breaker.fire(params) .then(console.log) @@ -60,7 +60,7 @@ event of failure. To take some action when the fallback is performed, listen for the `fallback` event. ```javascript -const breaker = circuitBreaker(asyncFunctionThatCouldFail, options); +const breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options); // if asyncFunctionThatCouldFail starts to fail, firing the breaker // will trigger our fallback function breaker.fallback(() => 'Sorry, out of service right now'); @@ -124,7 +124,7 @@ server.route({ } }); ``` -In the browser's global scope will be a `circuitBreaker` function. Use it +In the browser's global scope will be a `CircuitBreaker` constructor. Use it to create circuit breakers, guarding against network failures in your REST API calls. @@ -137,7 +137,7 @@ const circuitBreakerOptions = { resetTimeout: 5000 }; -const circuit = circuitBreaker(() => $.get(route), circuitBreakerOptions); +const circuit = new CircuitBreaker(() => $.get(route), circuitBreakerOptions); circuit.fallback(() => `${route} unavailable right now. Try later.`)); circuit.on('success', (result) => $(element).append(JSON.stringify(result)})); @@ -167,7 +167,7 @@ Here are the events you can listen for. Handling events gives a greater level of control over your application behavior. ```js -const circuit = circuitBreaker(() => $.get(route), circuitBreakerOptions); +const circuit = new CircuitBreaker(() => $.get(route), circuitBreakerOptions); circuit.fallback(() => ({ body: `${route} unavailable right now. Try later.` })); @@ -211,10 +211,10 @@ Node core utility function `util.promisify()` . ```javascript const fs = require('fs'); const { promisify } = require('util'); -const circuitBreaker = require('opossum'); +const CircuitBreaker = require('opossum'); const readFile = promisify(fs.readFile); -const breaker = circuitBreaker(readFile, options); +const breaker = new CircuitBreaker(readFile, options); breaker.fire('./package.json', 'utf-8') .then(console.log) @@ -225,7 +225,7 @@ And just for fun, your circuit doesn't even really have to be a function. Not sure when you'd use this - but you could if you wanted to. ```javascript -const breaker = circuitBreaker('foo', options); +const breaker = new CircuitBreaker('foo', options); breaker.fire() .then(console.log) // logs 'foo' diff --git a/docs/index.html b/docs/index.html index f2de69ae..124e4c01 100644 --- a/docs/index.html +++ b/docs/index.html @@ -35,26 +35,6 @@

opossum

-
  • - factory - - - -
  • - - -
  • - factory.circuits - - - -
  • - -
  • @@ -466,7 +446,7 @@

    Usage

    Let's say you've got an API that depends on something that might fail - a network operation, or disk read, for example. Wrap those functions up in a CircuitBreaker and you have control over your destiny.

    -
    const circuitBreaker = require('opossum');
    +
    const CircuitBreaker = require('opossum');
     
     function asyncFunctionThatCouldFail (x, y) {
       return new Promise((resolve, reject) => {
    @@ -479,7 +459,7 @@ 

    Usage

    errorThresholdPercentage: 50, // When 50% of requests fail, trip the circuit resetTimeout: 30000 // After 30 seconds, try again. }; -const breaker = circuitBreaker(asyncFunctionThatCouldFail, options); +const breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options); breaker.fire(params) .then(console.log) @@ -488,7 +468,7 @@

    Fallback

    You can also provide a fallback function that will be executed in the event of failure. To take some action when the fallback is performed, listen for the fallback event.

    -
    const breaker = circuitBreaker(asyncFunctionThatCouldFail, options);
    +
    const breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options);
     // if asyncFunctionThatCouldFail starts to fail, firing the breaker
     // will trigger our fallback function
     breaker.fallback(() => 'Sorry, out of service right now');
    @@ -655,328 +635,6 @@ 

    Troubleshooting

    -
    - - -
    - -

    - factory -

    - - -
    - - -

    Creates a CircuitBreaker instance capable of executing action.

    - -
    factory(action: Function, options: Object): CircuitBreaker
    - - - - - - - - - - - -
    Parameters
    -
    - -
    -
    - action (Function) - The action to fire for this -CircuitBreaker - -
    - -
    - -
    -
    - options (Object) - Options for the -CircuitBreaker - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    options.timeout Number - The time in milliseconds that action should -be allowed to execute before timing out. Default 10000 (10 seconds) -
    options.maxFailures Number - (Deprecated) The number of times the -circuit can fail before opening. Default 10. -
    options.resetTimeout Number - The time in milliseconds to wait before -setting the breaker to -halfOpen - state, and trying the action again. -Default: 30000 (30 seconds) -
    options.rollingCountTimeout Number - Sets the duration of the -statistical rolling window, in milliseconds. This is how long Opossum keeps -metrics for the circuit breaker to use and for publishing. Default: 10000 -
    options.rollingCountBuckets Number - Sets the number of buckets the -rolling statistical window is divided into. So, if -options.rollingCountTimeout is 10000, and options.rollingCountBuckets is 10, -then the statistical window will be 1000 1 second snapshots in the -statistical window. Default: 10 -
    options.name String - the circuit name to use when reporting stats. -Default: the name of the function this circuit controls. -
    options.rollingPercentilesEnabled boolean - This property indicates -whether execution latencies should be tracked and calculated as percentiles. -If they are disabled, all summary statistics (mean, percentiles) are -returned as -1. Default: false -
    options.capacity Number - the number of concurrent requests allowed. -If the number currently executing function calls is equal to -options.capacity, further calls to -fire() - are rejected until at least one -of the current requests completes. Default: -Number.MAX_SAFE_INTEGER -. -
    options.errorThresholdPercentage Number - the error percentage at -which to open the circuit and start short-circuiting requests to fallback. -Default: 50 -
    options.enabled boolean - whether this circuit is enabled upon -construction. Default: true -
    options.allowWarmUp boolean - determines whether to allow failures -without opening the circuit during a brief warmup period (this is the - -rollingCountDuration - property). Default: false -allow before enabling the circuit. This can help in situations where no -matter what your -errorThresholdPercentage - is, if the first execution -times out or fails, the circuit immediately opens. Default: 0 -
    options.volumeThreshold Number - the minimum number of requests within -the rolling statistical window that must exist before the circuit breaker -can open. This is similar to -options.allowWarmUp - in that no matter how many -failures there are, if the number of requests within the statistical window -does not exceed this threshold, the circuit will remain closed. Default: 0 -
    options.errorFilter Function - an optional function that will be -called when the circuit's function fails (returns a rejected Promise). If -this function returns truthy, the circuit's failure statistics will not be -incremented. This is useful, for example, when you don't want HTTP 404 to -trip the circuit, but still want to handle it as a failure case. -
    - -
    - -
    - - - - - - -
    Returns
    - CircuitBreaker: - a newly created -CircuitBreaker - instance - - - - - - - - - - - - - - - - -
    - - - - -
    - - -
    - -

    - factory.circuits -

    - - -
    - - -

    Get an Iterator object containing all -circuits that have been created but not subsequently shut down.

    - -
    factory.circuits(): Iterator
    - - - - - - - - - - - - - - - - -
    Returns
    - Iterator: - an - -Iterator - - of all available circuits - - - - - - - - - - - - - - - - -
    - - - -
    diff --git a/docs/opossum.md b/docs/opossum.md index 6dbb4f91..a6b46dcd 100644 --- a/docs/opossum.md +++ b/docs/opossum.md @@ -2,172 +2,105 @@ ### Table of Contents -- [factory][1] +- [CircuitBreaker][1] - [Parameters][2] -- [factory.circuits][3] -- [CircuitBreaker][4] - - [Parameters][5] - - [close][6] - - [open][7] - - [shutdown][8] - - [isShutdown][9] - - [name][10] - - [group][11] - - [pendingClose][12] - - [closed][13] - - [opened][14] - - [halfOpen][15] - - [status][16] - - [stats][17] - - [enabled][18] - - [warmUp][19] - - [volumeThreshold][20] - - [fallback][21] - - [Parameters][22] - - [fire][23] - - [clearCache][24] - - [healthCheck][25] - - [Parameters][26] - - [enable][27] - - [disable][28] - - [circuits][29] -- [CircuitBreaker#halfOpen][30] -- [CircuitBreaker#close][31] -- [CircuitBreaker#open][32] -- [CircuitBreaker#shutdown][33] -- [CircuitBreaker#fire][34] -- [CircuitBreaker#cacheHit][35] -- [CircuitBreaker#cacheMiss][36] -- [CircuitBreaker#reject][37] -- [CircuitBreaker#timeout][38] -- [CircuitBreaker#success][39] -- [CircuitBreaker#semaphoreLocked][40] -- [CircuitBreaker#healthCheckFailed][41] -- [CircuitBreaker#fallback][42] -- [CircuitBreaker#failure][43] -- [Status][44] - - [Parameters][45] - - [Examples][46] - - [stats][47] - - [window][48] -- [Status#snapshot][49] - -## factory - -Creates a [CircuitBreaker][4] instance capable of executing `action`. - -### Parameters - -- `action` **[Function][50]** The action to fire for this [CircuitBreaker][4] -- `options` **[Object][51]** Options for the [CircuitBreaker][4] - - `options.timeout` **[Number][52]** The time in milliseconds that action should - be allowed to execute before timing out. Default 10000 (10 seconds) - - `options.maxFailures` **[Number][52]** (Deprecated) The number of times the - circuit can fail before opening. Default 10. - - `options.resetTimeout` **[Number][52]** The time in milliseconds to wait before - setting the breaker to `halfOpen` state, and trying the action again. - Default: 30000 (30 seconds) - - `options.rollingCountTimeout` **[Number][52]** Sets the duration of the - statistical rolling window, in milliseconds. This is how long Opossum keeps - metrics for the circuit breaker to use and for publishing. Default: 10000 - - `options.rollingCountBuckets` **[Number][52]** Sets the number of buckets the - rolling statistical window is divided into. So, if - options.rollingCountTimeout is 10000, and options.rollingCountBuckets is 10, - then the statistical window will be 1000 1 second snapshots in the - statistical window. Default: 10 - - `options.name` **[String][53]** the circuit name to use when reporting stats. - Default: the name of the function this circuit controls. - - `options.rollingPercentilesEnabled` **[boolean][54]** This property indicates - whether execution latencies should be tracked and calculated as percentiles. - If they are disabled, all summary statistics (mean, percentiles) are - returned as -1. Default: false - - `options.capacity` **[Number][52]** the number of concurrent requests allowed. - If the number currently executing function calls is equal to - options.capacity, further calls to `fire()` are rejected until at least one - of the current requests completes. Default: `Number.MAX_SAFE_INTEGER`. - - `options.errorThresholdPercentage` **[Number][52]** the error percentage at - which to open the circuit and start short-circuiting requests to fallback. - Default: 50 - - `options.enabled` **[boolean][54]** whether this circuit is enabled upon - construction. Default: true - - `options.allowWarmUp` **[boolean][54]** determines whether to allow failures - without opening the circuit during a brief warmup period (this is the - `rollingCountDuration` property). Default: false - allow before enabling the circuit. This can help in situations where no - matter what your `errorThresholdPercentage` is, if the first execution - times out or fails, the circuit immediately opens. Default: 0 - - `options.volumeThreshold` **[Number][52]** the minimum number of requests within - the rolling statistical window that must exist before the circuit breaker - can open. This is similar to `options.allowWarmUp` in that no matter how many - failures there are, if the number of requests within the statistical window - does not exceed this threshold, the circuit will remain closed. Default: 0 - - `options.errorFilter` **[Function][50]** an optional function that will be - called when the circuit's function fails (returns a rejected Promise). If - this function returns truthy, the circuit's failure statistics will not be - incremented. This is useful, for example, when you don't want HTTP 404 to - trip the circuit, but still want to handle it as a failure case. - -Returns **[CircuitBreaker][55]** a newly created [CircuitBreaker][4] instance - -## factory.circuits - -Get an Iterator object containing all -circuits that have been created but not subsequently shut down. - -Returns **Iterator** an Iterator of all available circuits + - [close][3] + - [open][4] + - [shutdown][5] + - [isShutdown][6] + - [name][7] + - [group][8] + - [pendingClose][9] + - [closed][10] + - [opened][11] + - [halfOpen][12] + - [status][13] + - [stats][14] + - [enabled][15] + - [warmUp][16] + - [volumeThreshold][17] + - [fallback][18] + - [Parameters][19] + - [fire][20] + - [clearCache][21] + - [healthCheck][22] + - [Parameters][23] + - [enable][24] + - [disable][25] + - [circuits][26] +- [CircuitBreaker#halfOpen][27] +- [CircuitBreaker#close][28] +- [CircuitBreaker#open][29] +- [CircuitBreaker#shutdown][30] +- [CircuitBreaker#fire][31] +- [CircuitBreaker#cacheHit][32] +- [CircuitBreaker#cacheMiss][33] +- [CircuitBreaker#reject][34] +- [CircuitBreaker#timeout][35] +- [CircuitBreaker#success][36] +- [CircuitBreaker#semaphoreLocked][37] +- [CircuitBreaker#healthCheckFailed][38] +- [CircuitBreaker#fallback][39] +- [CircuitBreaker#failure][40] +- [Status][41] + - [Parameters][42] + - [Examples][43] + - [stats][44] + - [window][45] +- [Status#snapshot][46] ## CircuitBreaker **Extends EventEmitter** -Constructs a [CircuitBreaker][4]. +Constructs a [CircuitBreaker][1]. ### Parameters -- `action` **[Function][50]** The action to fire for this [CircuitBreaker][4] -- `options` **[Object][51]** Options for the [CircuitBreaker][4] - - `options.timeout` **[Number][52]** The time in milliseconds that action should +- `action` **[Function][47]** The action to fire for this [CircuitBreaker][1] +- `options` **[Object][48]** Options for the [CircuitBreaker][1] + - `options.timeout` **[Number][49]** The time in milliseconds that action should be allowed to execute before timing out. Default 10000 (10 seconds) - - `options.maxFailures` **[Number][52]** (Deprecated) The number of times the + - `options.maxFailures` **[Number][49]** (Deprecated) The number of times the circuit can fail before opening. Default 10. - - `options.resetTimeout` **[Number][52]** The time in milliseconds to wait before + - `options.resetTimeout` **[Number][49]** The time in milliseconds to wait before setting the breaker to `halfOpen` state, and trying the action again. Default: 30000 (30 seconds) - - `options.rollingCountTimeout` **[Number][52]** Sets the duration of the + - `options.rollingCountTimeout` **[Number][49]** Sets the duration of the statistical rolling window, in milliseconds. This is how long Opossum keeps metrics for the circuit breaker to use and for publishing. Default: 10000 - - `options.rollingCountBuckets` **[Number][52]** Sets the number of buckets the + - `options.rollingCountBuckets` **[Number][49]** Sets the number of buckets the rolling statistical window is divided into. So, if options.rollingCountTimeout is 10000, and options.rollingCountBuckets is 10, then the statistical window will be 1000 1 second snapshots in the statistical window. Default: 10 - - `options.name` **[String][53]** the circuit name to use when reporting stats. + - `options.name` **[String][50]** the circuit name to use when reporting stats. Default: the name of the function this circuit controls. - - `options.rollingPercentilesEnabled` **[boolean][54]** This property indicates + - `options.rollingPercentilesEnabled` **[boolean][51]** This property indicates whether execution latencies should be tracked and calculated as percentiles. If they are disabled, all summary statistics (mean, percentiles) are returned as -1. Default: false - - `options.capacity` **[Number][52]** the number of concurrent requests allowed. + - `options.capacity` **[Number][49]** the number of concurrent requests allowed. If the number currently executing function calls is equal to options.capacity, further calls to `fire()` are rejected until at least one of the current requests completes. Default: `Number.MAX_SAFE_INTEGER`. - - `options.errorThresholdPercentage` **[Number][52]** the error percentage at + - `options.errorThresholdPercentage` **[Number][49]** the error percentage at which to open the circuit and start short-circuiting requests to fallback. Default: 50 - - `options.enabled` **[boolean][54]** whether this circuit is enabled upon + - `options.enabled` **[boolean][51]** whether this circuit is enabled upon construction. Default: true - - `options.allowWarmUp` **[boolean][54]** determines whether to allow failures + - `options.allowWarmUp` **[boolean][51]** determines whether to allow failures without opening the circuit during a brief warmup period (this is the `rollingCountDuration` property). Default: false allow before enabling the circuit. This can help in situations where no matter what your `errorThresholdPercentage` is, if the first execution times out or fails, the circuit immediately opens. Default: 0 - - `options.volumeThreshold` **[Number][52]** the minimum number of requests within + - `options.volumeThreshold` **[Number][49]** the minimum number of requests within the rolling statistical window that must exist before the circuit breaker can open. This is similar to `options.allowWarmUp` in that no matter how many failures there are, if the number of requests within the statistical window does not exceed this threshold, the circuit will remain closed. Default: 0 - - `options.errorFilter` **[Function][50]** an optional function that will be + - `options.errorFilter` **[Function][47]** an optional function that will be called when the circuit's function fails (returns a rejected Promise). If this function returns truthy, the circuit's failPure statistics will not be incremented. This is useful, for example, when you don't want HTTP 404 to @@ -198,49 +131,49 @@ Returns **void** Determines if the circuit has been shutdown. -Type: [Boolean][54] +Type: [Boolean][51] ### name Gets the name of this circuit -Type: [String][53] +Type: [String][50] ### group Gets the name of this circuit group -Type: [String][53] +Type: [String][50] ### pendingClose Gets whether this cicruit is in the `pendingClosed` state -Type: [Boolean][54] +Type: [Boolean][51] ### closed True if the circuit is currently closed. False otherwise. -Type: [Boolean][54] +Type: [Boolean][51] ### opened True if the circuit is currently opened. False otherwise. -Type: [Boolean][54] +Type: [Boolean][51] ### halfOpen True if the circuit is currently half opened. False otherwise. -Type: [Boolean][54] +Type: [Boolean][51] ### status -The current [Status][16] of this [CircuitBreaker][4] +The current [Status][13] of this [CircuitBreaker][1] -Type: [Status][56] +Type: [Status][52] ### stats @@ -248,39 +181,39 @@ Type: [Status][56] Get the current stats for the circuit. -Type: [Object][51] +Type: [Object][48] ### enabled Gets whether the circuit is enabled or not -Type: [Boolean][54] +Type: [Boolean][51] ### warmUp Gets whether the circuit is currently in warm up phase -Type: [Boolean][54] +Type: [Boolean][51] ### volumeThreshold Gets the volume threshold for this circuit -Type: [Boolean][54] +Type: [Boolean][51] ### fallback -Provide a fallback function for this [CircuitBreaker][4]. This +Provide a fallback function for this [CircuitBreaker][1]. This function will be executed when the circuit is `fire`d and fails. It will always be preceded by a `failure` event, and `breaker.fire` returns a rejected Promise. #### Parameters -- `func` **([Function][50] \| [CircuitBreaker][55])** the fallback function to execute +- `func` **([Function][47] \| [CircuitBreaker][53])** the fallback function to execute when the breaker has opened or when a timeout or error occurs. -Returns **[CircuitBreaker][55]** this +Returns **[CircuitBreaker][53]** this ### fire @@ -289,12 +222,12 @@ returned promise will be rejected. If the action succeeds, the promise will resolve with the resolved value from action. If a fallback function was provided, it will be invoked in the event of any failure or timeout. -Returns **[Promise][57]<any>** promise resolves with the circuit function's return +Returns **[Promise][54]<any>** promise resolves with the circuit function's return value on success or is rejected on failure of the action. ### clearCache -Clears the cache of this [CircuitBreaker][4] +Clears the cache of this [CircuitBreaker][1] Returns **void** @@ -312,12 +245,12 @@ circuit breaker itself. #### Parameters -- `func` **[Function][50]** a health check function which returns a promise. -- `interval` **[Number][52]?** the amount of time between calls to the health +- `func` **[Function][47]** a health check function which returns a promise. +- `interval` **[Number][49]?** the amount of time between calls to the health check function. Default: 5000 (5 seconds) -- Throws **[TypeError][58]** if `interval` is supplied but not a number +- Throws **[TypeError][55]** if `interval` is supplied but not a number Returns **void** @@ -351,7 +284,7 @@ Emitted after `options.resetTimeout` has elapsed, allowing for a single attempt to call the service again. If that attempt is successful, the circuit will be closed. Otherwise it remains open. -Type: [Number][52] +Type: [Number][49] ## CircuitBreaker#close @@ -386,14 +319,14 @@ the cache, but the cache option is enabled. Emitted when the circuit breaker is open and failing fast -Type: [Error][59] +Type: [Error][56] ## CircuitBreaker#timeout Emitted when the circuit breaker action takes longer than `options.timeout` -Type: [Error][59] +Type: [Error][56] ## CircuitBreaker#success @@ -406,14 +339,14 @@ Type: any Emitted when the rate limit has been reached and there are no more locks to be obtained. -Type: [Error][59] +Type: [Error][56] ## CircuitBreaker#healthCheckFailed Emitted with the user-supplied health check function returns a rejected promise. -Type: [Error][59] +Type: [Error][56] ## CircuitBreaker#fallback @@ -425,7 +358,7 @@ Type: any Emitted when the circuit breaker action fails -Type: [Error][59] +Type: [Error][56] ## Status @@ -433,27 +366,27 @@ Type: [Error][59] - **See: CircuitBreaker#status** -Tracks execution status for a given [CircuitBreaker][4]. -A Status instance is created for every [CircuitBreaker][4] +Tracks execution status for a given [CircuitBreaker][1]. +A Status instance is created for every [CircuitBreaker][1] and does not typically need to be created by a user. -A Status instance will listen for all events on the [CircuitBreaker][4] +A Status instance will listen for all events on the [CircuitBreaker][1] and track them in a rolling statistical window. The window duration is determined by the `rollingCountTimeout` option provided to the -[CircuitBreaker][4]. The window consists of an array of Objects, -each representing the counts for a [CircuitBreaker][4]'s events. +[CircuitBreaker][1]. The window consists of an array of Objects, +each representing the counts for a [CircuitBreaker][1]'s events. -The array's length is determined by the [CircuitBreaker][4]'s +The array's length is determined by the [CircuitBreaker][1]'s `rollingCountBuckets` option. The duration of each slice of the window is determined by dividing the `rollingCountTimeout` by `rollingCountBuckets`. ### Parameters -- `options` **[Object][51]** for the status window - - `options.rollingCountBuckets` **[Number][52]** number of buckets in the window - - `options.rollingCountTimeout` **[Number][52]** the duration of the window - - `options.rollingPercentilesEnabled` **[Boolean][54]** whether to calculate +- `options` **[Object][48]** for the status window + - `options.rollingCountBuckets` **[Number][49]** number of buckets in the window + - `options.rollingCountTimeout` **[Number][49]** the duration of the window + - `options.rollingPercentilesEnabled` **[Boolean][51]** whether to calculate percentiles ### Examples @@ -475,137 +408,131 @@ circuit.status.window; Get the cumulative stats for the current window -Type: [Object][51] +Type: [Object][48] ### window Gets the stats window as an array of time-sliced objects. -Type: [Array][60] +Type: [Array][57] ## Status#snapshot Emitted at each time-slice. Listeners for this event will receive a cumulative snapshot of the current status window. -Type: [Object][51] +Type: [Object][48] -[1]: #factory +[1]: #circuitbreaker [2]: #parameters -[3]: #factorycircuits - -[4]: #circuitbreaker - -[5]: #parameters-1 - -[6]: #close +[3]: #close -[7]: #open +[4]: #open -[8]: #shutdown +[5]: #shutdown -[9]: #isshutdown +[6]: #isshutdown -[10]: #name +[7]: #name -[11]: #group +[8]: #group -[12]: #pendingclose +[9]: #pendingclose -[13]: #closed +[10]: #closed -[14]: #opened +[11]: #opened -[15]: #halfopen +[12]: #halfopen -[16]: #status +[13]: #status -[17]: #stats +[14]: #stats -[18]: #enabled +[15]: #enabled -[19]: #warmup +[16]: #warmup -[20]: #volumethreshold +[17]: #volumethreshold -[21]: #fallback +[18]: #fallback -[22]: #parameters-2 +[19]: #parameters-1 -[23]: #fire +[20]: #fire -[24]: #clearcache +[21]: #clearcache -[25]: #healthcheck +[22]: #healthcheck -[26]: #parameters-3 +[23]: #parameters-2 -[27]: #enable +[24]: #enable -[28]: #disable +[25]: #disable -[29]: #circuits +[26]: #circuits -[30]: #circuitbreakerhalfopen +[27]: #circuitbreakerhalfopen -[31]: #circuitbreakerclose +[28]: #circuitbreakerclose -[32]: #circuitbreakeropen +[29]: #circuitbreakeropen -[33]: #circuitbreakershutdown +[30]: #circuitbreakershutdown -[34]: #circuitbreakerfire +[31]: #circuitbreakerfire -[35]: #circuitbreakercachehit +[32]: #circuitbreakercachehit -[36]: #circuitbreakercachemiss +[33]: #circuitbreakercachemiss -[37]: #circuitbreakerreject +[34]: #circuitbreakerreject -[38]: #circuitbreakertimeout +[35]: #circuitbreakertimeout -[39]: #circuitbreakersuccess +[36]: #circuitbreakersuccess -[40]: #circuitbreakersemaphorelocked +[37]: #circuitbreakersemaphorelocked -[41]: #circuitbreakerhealthcheckfailed +[38]: #circuitbreakerhealthcheckfailed -[42]: #circuitbreakerfallback +[39]: #circuitbreakerfallback -[43]: #circuitbreakerfailure +[40]: #circuitbreakerfailure -[44]: #status-1 +[41]: #status-1 -[45]: #parameters-4 +[42]: #parameters-3 -[46]: #examples +[43]: #examples -[47]: #stats-1 +[44]: #stats-1 -[48]: #window +[45]: #window -[49]: #statussnapshot +[46]: #statussnapshot -[50]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function +[47]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function -[51]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object +[48]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object -[52]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number +[49]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number -[53]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String +[50]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String -[54]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean +[51]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean -[55]: #circuitbreaker +[52]: #status -[56]: #status +[53]: #circuitbreaker -[57]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise +[54]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise -[58]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypeError +[55]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypeError -[59]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error +[56]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error -[60]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array +[57]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array diff --git a/index.js b/index.js index cbce0884..cae60bd9 100644 --- a/index.js +++ b/index.js @@ -1,79 +1,3 @@ 'use strict'; -const CircuitBreaker = require('./lib/circuit'); - -const defaults = { - timeout: 10000, // 10 seconds - errorThresholdPercentage: 50, - resetTimeout: 30000 // 30 seconds -}; - -/** - * Creates a {@link CircuitBreaker} instance capable of executing `action`. - * - * @param {Function} action The action to fire for this {@link CircuitBreaker} - * @param {Object} options Options for the {@link CircuitBreaker} - * @param {Number} options.timeout The time in milliseconds that action should - * be allowed to execute before timing out. Default 10000 (10 seconds) - * @param {Number} options.maxFailures (Deprecated) The number of times the - * circuit can fail before opening. Default 10. - * @param {Number} options.resetTimeout The time in milliseconds to wait before - * setting the breaker to `halfOpen` state, and trying the action again. - * Default: 30000 (30 seconds) - * @param {Number} options.rollingCountTimeout Sets the duration of the - * statistical rolling window, in milliseconds. This is how long Opossum keeps - * metrics for the circuit breaker to use and for publishing. Default: 10000 - * @param {Number} options.rollingCountBuckets Sets the number of buckets the - * rolling statistical window is divided into. So, if - * options.rollingCountTimeout is 10000, and options.rollingCountBuckets is 10, - * then the statistical window will be 1000 1 second snapshots in the - * statistical window. Default: 10 - * @param {String} options.name the circuit name to use when reporting stats. - * Default: the name of the function this circuit controls. - * @param {boolean} options.rollingPercentilesEnabled This property indicates - * whether execution latencies should be tracked and calculated as percentiles. - * If they are disabled, all summary statistics (mean, percentiles) are - * returned as -1. Default: false - * @param {Number} options.capacity the number of concurrent requests allowed. - * If the number currently executing function calls is equal to - * options.capacity, further calls to `fire()` are rejected until at least one - * of the current requests completes. Default: `Number.MAX_SAFE_INTEGER`. - * @param {Number} options.errorThresholdPercentage the error percentage at - * which to open the circuit and start short-circuiting requests to fallback. - * Default: 50 - * @param {boolean} options.enabled whether this circuit is enabled upon - * construction. Default: true - * @param {boolean} options.allowWarmUp determines whether to allow failures - * without opening the circuit during a brief warmup period (this is the - * `rollingCountDuration` property). Default: false - * allow before enabling the circuit. This can help in situations where no - * matter what your `errorThresholdPercentage` is, if the first execution - * times out or fails, the circuit immediately opens. Default: 0 - * @param {Number} options.volumeThreshold the minimum number of requests within - * the rolling statistical window that must exist before the circuit breaker - * can open. This is similar to `options.allowWarmUp` in that no matter how many - * failures there are, if the number of requests within the statistical window - * does not exceed this threshold, the circuit will remain closed. Default: 0 - * @param {Function} options.errorFilter an optional function that will be - * called when the circuit's function fails (returns a rejected Promise). If - * this function returns truthy, the circuit's failure statistics will not be - * incremented. This is useful, for example, when you don't want HTTP 404 to - * trip the circuit, but still want to handle it as a failure case. - * @return {CircuitBreaker} a newly created {@link CircuitBreaker} instance - */ -function factory(action, options) { - return new CircuitBreaker(action, - Object.assign({}, defaults, options)); -} - -/** - * Get an Iterator object containing all - * circuits that have been created but not subsequently shut down. - * @function factory.circuits - * @return {Iterator} an Iterator of all available circuits - */ -factory.circuits = CircuitBreaker.circuits; - -module.exports = exports = factory; -// Allow use of default import syntax in TypeScript -module.exports.default = factory; +module.exports = exports = require('./lib/circuit'); diff --git a/lib/circuit.js b/lib/circuit.js index 1f712fb3..9cda2e1f 100644 --- a/lib/circuit.js +++ b/lib/circuit.js @@ -91,9 +91,13 @@ const CIRCUITS = new Set(); * @fires CircuitBreaker#failure */ class CircuitBreaker extends EventEmitter { - constructor (action, options) { + constructor(action, options = {}) { super(); this.options = options; + this.options.timeout = options.timeout || 10000; + this.options.resetTimeout = options.resetTimeout || 30000; + this.options.errorThresholdPercentage = + options.errorThresholdPercentage || 50; this.options.rollingCountTimeout = options.rollingCountTimeout || 10000; this.options.rollingCountBuckets = options.rollingCountBuckets || 10; this.options.rollingPercentilesEnabled = diff --git a/test/circuit-shutdown-test.js b/test/circuit-shutdown-test.js index 23f57bb5..d55bf4f0 100644 --- a/test/circuit-shutdown-test.js +++ b/test/circuit-shutdown-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tape'); -const circuit = require('..'); +const CircuitBreaker = require('..'); const passFail = require('./common').passFail; // tests that we are not leaving listeners open to @@ -9,7 +9,7 @@ const passFail = require('./common').passFail; test('EventEmitter max listeners', t => { let i = 100; while (--i >= 0) { - const breaker = circuit(passFail, { name: `passFail${i}` }); + const breaker = new CircuitBreaker(passFail, { name: `passFail${i}` }); breaker.fire(1); breaker.shutdown(); // required for cleanup } @@ -18,7 +18,7 @@ test('EventEmitter max listeners', t => { test('Circuit shuts down properly', t => { t.plan(5); - const breaker = circuit(passFail); + const breaker = new CircuitBreaker(passFail); t.ok(breaker.fire(1), 'breaker is active'); breaker.shutdown(); t.ok(breaker.isShutdown, 'breaker is shutdown'); @@ -39,18 +39,18 @@ test('A list of non-shutdown circuits is maintained', t => { return count === 0; } - t.ok(expectCount(circuit.circuits(), 0)); + t.ok(expectCount(CircuitBreaker.circuits(), 0)); - const c1 = circuit(passFail); - const c2 = circuit(passFail); + const c1 = new CircuitBreaker(passFail); + const c2 = new CircuitBreaker(passFail); - t.ok(expectCount(circuit.circuits(), 2)); + t.ok(expectCount(CircuitBreaker.circuits(), 2)); c2.shutdown(); - t.ok(expectCount(circuit.circuits(), 1)); + t.ok(expectCount(CircuitBreaker.circuits(), 1)); c1.shutdown(); - t.ok(expectCount(circuit.circuits(), 0)); + t.ok(expectCount(CircuitBreaker.circuits(), 0)); t.end(); }); diff --git a/test/enable-disable-test.js b/test/enable-disable-test.js index c0751608..132c4750 100644 --- a/test/enable-disable-test.js +++ b/test/enable-disable-test.js @@ -1,12 +1,12 @@ 'use strict'; const test = require('tape'); -const opossum = require('../'); +const CircuitBreaker = require('../'); const { passFail } = require('./common'); test('Defaults to enabled', t => { t.plan(1); - const breaker = opossum(passFail); + const breaker = new CircuitBreaker(passFail); t.equals(breaker.enabled, true); breaker.shutdown(); t.end(); @@ -14,7 +14,7 @@ test('Defaults to enabled', t => { test('Accepts options.enabled', t => { t.plan(1); - const breaker = opossum(passFail, { enabled: false }); + const breaker = new CircuitBreaker(passFail, { enabled: false }); t.equals(breaker.enabled, false); breaker.shutdown(); t.end(); @@ -27,7 +27,7 @@ test('When disabled the circuit should always be closed', t => { resetTimeout: 1000 }; - const breaker = opossum(passFail, options); + const breaker = new CircuitBreaker(passFail, options); breaker.disable(); breaker.fire(-1) .catch(e => t.equals(e, 'Error: -1 is < 0')) diff --git a/test/error-filter-test.js b/test/error-filter-test.js index f5ec1c1f..6cce9a88 100644 --- a/test/error-filter-test.js +++ b/test/error-filter-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tape'); -const opossum = require('../'); +const CircuitBreaker = require('../'); function mightFail (errorCode) { const err = new Error('Something went wrong'); @@ -20,7 +20,7 @@ const options = { test('Bypasses failure stats if errorFilter returns true', t => { t.plan(3); - const breaker = opossum(mightFail, options); + const breaker = new CircuitBreaker(mightFail, options); breaker.fire(400) .then(t.fail) .catch(err => { @@ -34,7 +34,7 @@ test('Bypasses failure stats if errorFilter returns true', t => { test('Increments failure stats if errorFilter returns false', t => { t.plan(3); - const breaker = opossum(mightFail, options); + const breaker = new CircuitBreaker(mightFail, options); breaker.fire(500) .then(t.fail) .catch(err => { @@ -47,7 +47,8 @@ test('Increments failure stats if errorFilter returns false', t => { test('Increments failure stats if no filter is provided', t => { t.plan(3); - const breaker = opossum(mightFail, { errorThresholdPercentage: 1 }); + const breaker = new CircuitBreaker(mightFail, + { errorThresholdPercentage: 1 }); breaker.fire(500) .then(t.fail) .catch(err => { diff --git a/test/half-open-test.js b/test/half-open-test.js index 668502f7..8b37ce5e 100644 --- a/test/half-open-test.js +++ b/test/half-open-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tape'); -const opossum = require('../'); +const CircuitBreaker = require('../'); const { timedFailingFunction } = require('./common'); test('When half-open, the circuit only allows one request through', t => { @@ -11,8 +11,9 @@ test('When half-open, the circuit only allows one request through', t => { resetTimeout: 100 }; - const breaker = opossum(timedFailingFunction, options); + const breaker = new CircuitBreaker(timedFailingFunction, options); breaker.fire(1) + .then(t.fail) .catch(e => t.equals(e, 'Failed after 1')) .then(() => { t.ok(breaker.opened, 'should be open after initial fire'); diff --git a/test/health-check-test.js b/test/health-check-test.js index 2a3f5bd7..7bf0b9c3 100644 --- a/test/health-check-test.js +++ b/test/health-check-test.js @@ -1,12 +1,12 @@ 'use strict'; const test = require('tape'); -const opossum = require('../'); +const CircuitBreaker = require('../'); const common = require('./common'); test('Circuits accept a health check function', t => { t.plan(1); - const circuit = opossum(common.passFail); + const circuit = new CircuitBreaker(common.passFail); circuit.healthCheck(healthChecker(_ => { t.ok(true, 'function called'); circuit.shutdown(); @@ -17,7 +17,7 @@ test('Circuits accept a health check function', t => { test('healthCheckFailed is emitted on failure', t => { t.plan(1); - const circuit = opossum(common.passFail); + const circuit = new CircuitBreaker(common.passFail); circuit.on('healthCheckFailed', e => { t.equals(e.message, 'Too many tacos', 'healthCheckFailed emitted'); circuit.shutdown(); @@ -29,7 +29,7 @@ test('healthCheckFailed is emitted on failure', t => { test('circuit opens on health check failure', t => { t.plan(1); - const circuit = opossum(common.passFail); + const circuit = new CircuitBreaker(common.passFail); circuit.on('open', e => { t.ok(circuit.opened, 'circuit opened'); circuit.shutdown(); @@ -42,7 +42,7 @@ test('circuit opens on health check failure', t => { test('Health check function executes in the circuit breaker context', t => { t.plan(1); let called = false; - const circuit = opossum(common.passFail); + const circuit = new CircuitBreaker(common.passFail); circuit.healthCheck(function healthCheck () { if (!called) { t.equal(this, circuit, 'health check executes in circuit context'); @@ -56,7 +56,7 @@ test('Health check function executes in the circuit breaker context', t => { test('healthCheck() throws TypeError if interval duration is NaN', t => { t.plan(2); - const circuit = opossum(common.passFail); + const circuit = new CircuitBreaker(common.passFail); try { circuit.healthCheck(_ => {}, 'Biscuits and gravy'); t.fail('Circuit breaker did not throw TypeError'); @@ -71,7 +71,7 @@ test('healthCheck() throws TypeError if interval duration is NaN', t => { test('healthCheck() throws TypeError if parameter is not a function', t => { t.plan(2); - const circuit = opossum(common.passFail); + const circuit = new CircuitBreaker(common.passFail); try { circuit.healthCheck('Biscuits and gravy'); t.fail('Circuit breaker did not throw TypeError'); diff --git a/test/test.js b/test/test.js index efaf4355..0d1983f2 100644 --- a/test/test.js +++ b/test/test.js @@ -3,12 +3,12 @@ const browser = require('./browser/browser-tap'); const test = require('tape'); const {promisify} = require('util'); -const circuit = require('../'); +const CircuitBreaker = require('../'); browser.enable(); test('api', t => { - const breaker = circuit(passFail); + const breaker = new CircuitBreaker(passFail); t.ok(breaker, 'CircuitBreaker'); t.ok(breaker.fire, 'CircuitBreaker.fire'); t.notOk(breaker.opened, 'CircuitBreaker.opened'); @@ -22,14 +22,14 @@ test('api', t => { }); test('has a name based on the function name', t => { - const breaker = circuit(passFail); + const breaker = new CircuitBreaker(passFail); t.equals(breaker.name, passFail.name); breaker.shutdown(); t.end(); }); test('accepts a name as an option', t => { - const breaker = circuit(passFail, { name: 'tacoMachine' }); + const breaker = new CircuitBreaker(passFail, { name: 'tacoMachine' }); t.equals(breaker.name, 'tacoMachine'); breaker.shutdown(); t.end(); @@ -37,21 +37,21 @@ test('accepts a name as an option', t => { test('uses UUID as a name when none is provided and the function is anonymoys', t => { - const breaker = circuit(identity); + const breaker = new CircuitBreaker(identity); t.ok(breaker.name); breaker.shutdown(); t.end(); }); test('accepts a group as an option', t => { - const breaker = circuit(passFail, { group: 'tacoMachine' }); + const breaker = new CircuitBreaker(passFail, { group: 'tacoMachine' }); t.equals(breaker.group, 'tacoMachine'); breaker.shutdown(); t.end(); }); test('uses name as a group when no group is provided', t => { - const breaker = circuit(passFail, { name: 'tacoMachine' }); + const breaker = new CircuitBreaker(passFail, { name: 'tacoMachine' }); t.equals(breaker.group, 'tacoMachine'); breaker.shutdown(); t.end(); @@ -60,7 +60,7 @@ test('uses name as a group when no group is provided', t => { test('Passes parameters to the circuit function', t => { t.plan(1); const expected = 34; - const breaker = circuit(passFail); + const breaker = new CircuitBreaker(passFail); breaker.fire(expected) .then(arg => t.equals(arg, expected, 'function parameters provided')) @@ -75,7 +75,7 @@ test('Using cache', t => { const options = { cache: true }; - const breaker = circuit(passFail, options); + const breaker = new CircuitBreaker(passFail, options); breaker.fire(expected) .then(arg => { @@ -115,7 +115,7 @@ test('Using cache', t => { test('Fails when the circuit function fails', t => { t.plan(1); - const breaker = circuit(passFail); + const breaker = new CircuitBreaker(passFail); breaker.fire(-1) .then(() => t.fail) @@ -130,7 +130,7 @@ test('Fails when the circuit function times out', t => { t.plan(2); const expected = 'Timed out after 10ms'; const expectedCode = 'ETIMEDOUT'; - const breaker = circuit(slowFunction, { timeout: 10 }); + const breaker = new CircuitBreaker(slowFunction, { timeout: 10 }); breaker.fire() .then(t.fail) @@ -144,7 +144,7 @@ test('Fails when the circuit function times out', t => { test('Works with functions that do not return a promise', t => { t.plan(1); - const breaker = circuit(nonPromise); + const breaker = new CircuitBreaker(nonPromise); breaker.fire() .then(arg => t.equals(arg, 'foo', 'non-Promise returns expected value')) @@ -155,7 +155,7 @@ test('Works with functions that do not return a promise', t => { test('Works with non-functions', t => { t.plan(1); - const breaker = circuit('foobar'); + const breaker = new CircuitBreaker('foobar'); breaker.fire() .then(arg => t.equals(arg, 'foobar', 'expected raw value returns')) @@ -167,7 +167,7 @@ test('Works with non-functions', t => { test('Works with callback functions', t => { t.plan(1); const promisified = promisify(callbackFunction); - const breaker = circuit(promisified); + const breaker = new CircuitBreaker(promisified); breaker.fire(3, 4) .then(arg => t.equals(arg, 7, 'Works with a Promisified Callback')) @@ -179,7 +179,7 @@ test('Works with callback functions', t => { test('Works with callback functions that fail', t => { t.plan(1); const promisified = promisify(failedCallbackFunction); - const breaker = circuit(promisified); + const breaker = new CircuitBreaker(promisified); breaker.fire(3, 4) .then(t.fail) @@ -190,7 +190,8 @@ test('Works with callback functions that fail', t => { test('Breaker opens after a configurable number of failures', t => { t.plan(2); - const breaker = circuit(passFail, { errorThresholdPercentage: 10 }); + const breaker = new CircuitBreaker(passFail, + { errorThresholdPercentage: 10 }); breaker.fire(-1) .then(t.fail) @@ -211,7 +212,7 @@ test('Breaker resets after a configurable amount of time', t => { t.plan(1); const fails = -1; const resetTimeout = 100; - const breaker = circuit(passFail, + const breaker = new CircuitBreaker(passFail, { errorThresholdPercentage: 1, resetTimeout }); breaker.fire(fails) @@ -229,7 +230,7 @@ test('Breaker resets after a configurable amount of time', t => { test('Breaker status reflects open state', t => { t.plan(1); - const breaker = circuit(passFail, + const breaker = new CircuitBreaker(passFail, { errorThresholdPercentage: 0, resetTimeout: 100 }); breaker.fire(-1) .then(t.fail) @@ -242,7 +243,7 @@ test('Breaker resets for circuits with a fallback function', t => { t.plan(2); const fails = -1; const resetTimeout = 100; - const breaker = circuit(passFail, + const breaker = new CircuitBreaker(passFail, { errorThresholdPercentage: 1, resetTimeout }); breaker.fallback(x => x * 2); @@ -268,7 +269,7 @@ test('Executes fallback action, if one exists, when breaker is open', t => { t.plan(1); const fails = -1; const expected = 100; - const breaker = circuit(passFail, { errorThresholdPercentage: 1 }); + const breaker = new CircuitBreaker(passFail, { errorThresholdPercentage: 1 }); breaker.fallback(() => expected); breaker.fire(fails) .then(() => { @@ -283,7 +284,7 @@ test('Executes fallback action, if one exists, when breaker is open', t => { test('Passes error as last argument to the fallback function', t => { t.plan(1); const fails = -1; - const breaker = circuit(passFail, { errorThresholdPercentage: 1 }); + const breaker = new CircuitBreaker(passFail, { errorThresholdPercentage: 1 }); breaker.on('fallback', result => { t.equals(result, `Error: ${fails} is < 0`, 'fallback received error as last parameter'); @@ -298,7 +299,7 @@ test('Fallback called once for the same execution when timing out', t => { t.plan(1); const actionDuration = 200; - const breaker = circuit(timedFailingFunction, + const breaker = new CircuitBreaker(timedFailingFunction, { timeout: actionDuration / 2 }); breaker.fallback((ms, err) => { @@ -317,7 +318,7 @@ test('Fallback called once for the same execution when timing out', t => { test('Passes arguments to the fallback function', t => { t.plan(1); const fails = -1; - const breaker = circuit(passFail, { errorThresholdPercentage: 1 }); + const breaker = new CircuitBreaker(passFail, { errorThresholdPercentage: 1 }); breaker.on('fallback', result => { t.equals(result, fails, 'fallback received expected parameters'); breaker.shutdown(); @@ -328,7 +329,7 @@ test('Passes arguments to the fallback function', t => { test('Returns self from fallback()', t => { t.plan(1); - const breaker = circuit(passFail, { errorThresholdPercentage: 1 }); + const breaker = new CircuitBreaker(passFail, { errorThresholdPercentage: 1 }); breaker.fallback(noop); breaker.fire(1) .then(result => { @@ -341,7 +342,8 @@ test('Returns self from fallback()', t => { test('CircuitBreaker emits failure when action throws', t => { t.plan(2); - const breaker = circuit(() => { throw new Error('E_TOOMANYCHICKENTACOS'); }); + const breaker = new CircuitBreaker( + () => { throw new Error('E_TOOMANYCHICKENTACOS'); }); breaker.fire() .then(t.fail) .catch(e => { @@ -354,7 +356,8 @@ test('CircuitBreaker emits failure when action throws', t => { test('CircuitBreaker executes fallback when an action throws', t => { t.plan(3); - const breaker = circuit(() => { throw new Error('E_TOOMANYCHICKENTACOS'); }) + const breaker = new CircuitBreaker( + () => { throw new Error('E_TOOMANYCHICKENTACOS'); }) .fallback(() => 'Fallback executed'); breaker.fire() .then(result => { @@ -370,7 +373,7 @@ test('CircuitBreaker executes fallback when an action throws', t => { test('CircuitBreaker emits failure when falling back', t => { t.plan(2); - const breaker = circuit(passFail).fallback(() => 'fallback value'); + const breaker = new CircuitBreaker(passFail).fallback(() => 'fallback value'); breaker.on('failure', err => { t.equals('Error: -1 is < 0', err, 'Expected failure'); @@ -386,7 +389,7 @@ test('CircuitBreaker emits failure when falling back', t => { test('CircuitBreaker status', t => { t.plan(12); - const breaker = circuit(passFail, { errorThresholdPercentage: 1 }); + const breaker = new CircuitBreaker(passFail, { errorThresholdPercentage: 1 }); const deepEqual = (t, expected) => actual => t.deepEqual(actual, expected, 'expected status values'); @@ -429,7 +432,7 @@ test('CircuitBreaker status', t => { test('CircuitBreaker rolling counts', t => { const opts = { rollingCountTimeout: 200, rollingCountBuckets: 2 }; - const breaker = circuit(passFail, opts); + const breaker = new CircuitBreaker(passFail, opts); const deepEqual = (t, expected) => actual => t.deepEqual(actual, expected, 'expected status values'); Promise.all([ @@ -454,7 +457,7 @@ test('CircuitBreaker rolling counts', t => { test('CircuitBreaker status listeners', t => { const opts = { rollingCountTimeout: 2500, rollingCountBuckets: 25 }; - const breaker = circuit(passFail, opts); + const breaker = new CircuitBreaker(passFail, opts); breaker.status.on('snapshot', snapshot => { t.ok(snapshot.successes !== undefined, 'has successes stat'); @@ -473,7 +476,7 @@ test('CircuitBreaker status listeners', t => { test('CircuitBreaker fallback event', t => { t.plan(1); - const breaker = circuit(passFail, { errorThresholdPercentage: 0 }); + const breaker = new CircuitBreaker(passFail, { errorThresholdPercentage: 0 }); breaker.fallback(x => x); breaker.on('fallback', value => { t.equal(value, -1, 'fallback value received'); @@ -491,7 +494,7 @@ test('CircuitBreaker events', t => { resetTimeout: 500 }; - const breaker = circuit(passFail, options); + const breaker = new CircuitBreaker(passFail, options); let fired = 0; let failures = 0; let success = 0; @@ -575,7 +578,8 @@ test('CircuitBreaker events', t => { t.equals(timeout, 0, 'timeout event did not fire'); }) .then(() => { - const timeoutBreaker = circuit(slowFunction, options); + const timeoutBreaker = new CircuitBreaker(slowFunction, + options); let timedOut = false; timeoutBreaker.on('timeout', () => timedOut++); timeoutBreaker.fire().then(t.fail).catch(noop); @@ -596,7 +600,7 @@ test('circuit halfOpen', t => { resetTimeout: 100 }; - const breaker = circuit(passFail, options); + const breaker = new CircuitBreaker(passFail, options); breaker.fire(-1) .catch(e => t.equals(e, 'Error: -1 is < 0', 'function should fail')) .then(() => { @@ -647,7 +651,7 @@ test('CircuitBreaker fallback as a rejected promise', t => { resetTimeout: 100 }; const input = -1; - const breaker = circuit(passFail, options); + const breaker = new CircuitBreaker(passFail, options); breaker.fallback(() => Promise.reject(new Error('nope'))); breaker.fire(input).then(t.fail).catch(e => { @@ -662,7 +666,7 @@ test('CircuitBreaker fallback event as a rejected promise', t => { resetTimeout: 100 }; const input = -1; - const breaker = circuit(passFail, options); + const breaker = new CircuitBreaker(passFail, options); breaker.fallback(() => Promise.reject(new Error('nope'))); breaker.on('fallback', result => { @@ -684,8 +688,8 @@ test('CircuitBreaker fallback as a CircuitBreaker', t => { }; const input = -1; - const breaker = circuit(passFail, options); - breaker.fallback(circuit(x => x, options)); + const breaker = new CircuitBreaker(passFail, options); + breaker.fallback(new CircuitBreaker(x => x, options)); breaker.fire(input) .then(v => t.equals(v, input, 'Fallback value equals input')) @@ -701,8 +705,8 @@ test('CircuitBreaker fallback as a CircuitBreaker that fails', t => { }; const input = -1; - const breaker = circuit(passFail, options); - breaker.fallback(circuit(passFail, options)); + const breaker = new CircuitBreaker(passFail, options); + breaker.fallback(new CircuitBreaker(passFail, options)); breaker.fire(input) .catch(e => t.equals(e, 'Error: -1 is < 0', 'Breaker should fail')) @@ -718,8 +722,8 @@ test('CircuitBreaker fallback as a CircuitBreaker', t => { }; const input = -1; - const breaker = circuit(passFail, options); - breaker.fallback(circuit(x => x, options)); + const breaker = new CircuitBreaker(passFail, options); + breaker.fallback(new CircuitBreaker(x => x, options)); breaker.fire(input) .then(v => t.equals(v, input, 'Fallback value equals input')) @@ -738,11 +742,11 @@ Please use options.errorThresholdPercentage`; console.error = originalLog; t.end(); }; - circuit(passFail, options); + new CircuitBreaker(passFail, options); }); test('rolling percentile enabled option defaults to true', t => { - const breaker = circuit(passFail); + const breaker = new CircuitBreaker(passFail); t.equals(breaker.status.rollingPercentilesEnabled, true, 'rollingPercentilesEnabled should default to true'); t.equals(breaker.status.stats.latencyMean, 0, @@ -757,7 +761,7 @@ test('rolling percentile enabled option defaults to true', t => { test('rolling percentile enabled option set to false', t => { const options = { rollingPercentilesEnabled: false }; - const breaker = circuit(passFail, options); + const breaker = new CircuitBreaker(passFail, options); t.equals(breaker.status.rollingPercentilesEnabled, false, 'rollingPercentilesEnabled set to false'); t.equals(breaker.status.stats.latencyMean, -1, @@ -772,7 +776,7 @@ test('rolling percentile enabled option set to false', t => { test('Circuit Breaker success event emits latency', t => { t.plan(1); - const breaker = circuit(passFail); + const breaker = new CircuitBreaker(passFail); breaker.on('success', (result, latencyTime) => { t.ok(latencyTime, 'second argument is the latency'); breaker.shutdown(); @@ -784,7 +788,7 @@ test('Circuit Breaker success event emits latency', t => { test('Circuit Breaker failure event emits latency', t => { t.plan(1); - const breaker = circuit(passFail); + const breaker = new CircuitBreaker(passFail); breaker.on('failure', (result, latencyTime) => { t.ok(latencyTime, 'second argument is the latency'); breaker.shutdown(); @@ -796,7 +800,7 @@ test('Circuit Breaker failure event emits latency', t => { test('Circuit Breaker failure event emits function parameters', t => { t.plan(6); - const breaker = circuit(passFail); + const breaker = new CircuitBreaker(passFail); breaker.on('failure', (result, latencyTime, args) => { t.ok(args, 'third argument is the function args'); t.equal(Array.isArray(args), true, 'The args parameter is an array'); @@ -813,7 +817,7 @@ test('Circuit Breaker failure event emits function parameters', t => { test('Circuit Breaker timeout event emits latency', t => { t.plan(1); - const breaker = circuit(slowFunction, { timeout: 10 }); + const breaker = new CircuitBreaker(slowFunction, { timeout: 10 }); breaker.on('timeout', (result, latencyTime) => { t.ok(latencyTime, 'second argument is the latency'); @@ -826,7 +830,7 @@ test('Circuit Breaker timeout event emits latency', t => { test('Circuit Breaker timeout event emits function parameters', t => { t.plan(6); - const breaker = circuit(slowFunction, { timeout: 10 }); + const breaker = new CircuitBreaker(slowFunction, { timeout: 10 }); breaker.on('timeout', (result, latencyTime, args) => { t.ok(args, 'third argument is the function args'); @@ -844,7 +848,8 @@ test('Circuit Breaker timeout event emits function parameters', t => { test('Circuit Breaker timeout with semaphore released', t => { t.plan(1); - const breaker = circuit(slowFunction, { timeout: 10, capacity: 2 }); + const breaker = new CircuitBreaker(slowFunction, + { timeout: 10, capacity: 2 }); breaker.on('timeout', _ => { t.equal(breaker.semaphore.count, breaker.options.capacity); @@ -857,7 +862,8 @@ test('Circuit Breaker timeout with semaphore released', t => { test('CircuitBreaker semaphore rate limiting', t => { t.plan(2); - const breaker = circuit(timedFunction, { timeout: 300, capacity: 1 }); + const breaker = new CircuitBreaker(timedFunction, + { timeout: 300, capacity: 1 }); // fire once to acquire the semaphore and hold it for a long time breaker.fire(1000).catch(noop); @@ -872,7 +878,7 @@ test('CircuitBreaker semaphore rate limiting', t => { }); test('CircuitBreaker default capacity', t => { - const breaker = circuit(passFail); + const breaker = new CircuitBreaker(passFail); t.equals(breaker.options.capacity, Number.MAX_SAFE_INTEGER); breaker.shutdown(); t.end(); diff --git a/test/volume-threshold-test.js b/test/volume-threshold-test.js index 4f74c2d6..ca36cfd8 100644 --- a/test/volume-threshold-test.js +++ b/test/volume-threshold-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tape'); -const opossum = require('../'); +const CircuitBreaker = require('../'); const { passFail } = require('./common'); test('By default does not have a volume threshold', t => { @@ -11,7 +11,7 @@ test('By default does not have a volume threshold', t => { resetTimeout: 100 }; - const breaker = opossum(passFail, options); + const breaker = new CircuitBreaker(passFail, options); breaker.fire(-1) .catch(e => t.equals(e, 'Error: -1 is < 0')) .then(() => { @@ -31,7 +31,7 @@ test('Has a volume threshold before tripping when option is provided', t => { volumeThreshold: 3 }; - const breaker = opossum(passFail, options); + const breaker = new CircuitBreaker(passFail, options); breaker.fire(-1) .then(t.fail) .catch(e => { @@ -73,7 +73,7 @@ test('volume threshold does not affect halfOpen state', t => { volumeThreshold: 2 }; - const breaker = opossum(passFail, options); + const breaker = new CircuitBreaker(passFail, options); breaker.fire(-1) .then(t.fail) .catch(_ => { diff --git a/test/warmup-test.js b/test/warmup-test.js index 66906967..0c5900e1 100644 --- a/test/warmup-test.js +++ b/test/warmup-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tape'); -const opossum = require('../'); +const CircuitBreaker = require('../'); const { passFail } = require('./common'); test('By default does not allow for warmup', t => { @@ -11,7 +11,7 @@ test('By default does not allow for warmup', t => { resetTimeout: 100 }; - const breaker = opossum(passFail, options); + const breaker = new CircuitBreaker(passFail, options); breaker.fire(-1) .catch(e => t.equals(e, 'Error: -1 is < 0')) .then(() => { @@ -31,7 +31,7 @@ test('Allows for warmup when option is provided', t => { allowWarmUp: true }; - const breaker = opossum(passFail, options); + const breaker = new CircuitBreaker(passFail, options); breaker.fire(-1) .catch(e => t.equals(e, 'Error: -1 is < 0')) .then(() => { @@ -52,7 +52,7 @@ test('Only warms up for rollingCountTimeout', t => { rollingCountTimeout: 500 }; - const breaker = opossum(passFail, options); + const breaker = new CircuitBreaker(passFail, options); breaker.fire(-1) .catch(e => t.equals(e, 'Error: -1 is < 0')) .then(() => {