[6a3a178] | 1 | <!-- badges/ -->
|
---|
| 2 | [![Build Status](https://secure.travis-ci.org/tim-kos/node-retry.png?branch=master)](http://travis-ci.org/tim-kos/node-retry "Check this project's build status on TravisCI")
|
---|
| 3 | [![codecov](https://codecov.io/gh/tim-kos/node-retry/branch/master/graph/badge.svg)](https://codecov.io/gh/tim-kos/node-retry)
|
---|
| 4 | <!-- /badges -->
|
---|
| 5 |
|
---|
| 6 | # retry
|
---|
| 7 |
|
---|
| 8 | Abstraction for exponential and custom retry strategies for failed operations.
|
---|
| 9 |
|
---|
| 10 | ## Installation
|
---|
| 11 |
|
---|
| 12 | npm install retry
|
---|
| 13 |
|
---|
| 14 | ## Current Status
|
---|
| 15 |
|
---|
| 16 | This module has been tested and is ready to be used.
|
---|
| 17 |
|
---|
| 18 | ## Tutorial
|
---|
| 19 |
|
---|
| 20 | The example below will retry a potentially failing `dns.resolve` operation
|
---|
| 21 | `10` times using an exponential backoff strategy. With the default settings, this
|
---|
| 22 | means the last attempt is made after `17 minutes and 3 seconds`.
|
---|
| 23 |
|
---|
| 24 | ``` javascript
|
---|
| 25 | var dns = require('dns');
|
---|
| 26 | var retry = require('retry');
|
---|
| 27 |
|
---|
| 28 | function faultTolerantResolve(address, cb) {
|
---|
| 29 | var operation = retry.operation();
|
---|
| 30 |
|
---|
| 31 | operation.attempt(function(currentAttempt) {
|
---|
| 32 | dns.resolve(address, function(err, addresses) {
|
---|
| 33 | if (operation.retry(err)) {
|
---|
| 34 | return;
|
---|
| 35 | }
|
---|
| 36 |
|
---|
| 37 | cb(err ? operation.mainError() : null, addresses);
|
---|
| 38 | });
|
---|
| 39 | });
|
---|
| 40 | }
|
---|
| 41 |
|
---|
| 42 | faultTolerantResolve('nodejs.org', function(err, addresses) {
|
---|
| 43 | console.log(err, addresses);
|
---|
| 44 | });
|
---|
| 45 | ```
|
---|
| 46 |
|
---|
| 47 | Of course you can also configure the factors that go into the exponential
|
---|
| 48 | backoff. See the API documentation below for all available settings.
|
---|
| 49 | currentAttempt is an int representing the number of attempts so far.
|
---|
| 50 |
|
---|
| 51 | ``` javascript
|
---|
| 52 | var operation = retry.operation({
|
---|
| 53 | retries: 5,
|
---|
| 54 | factor: 3,
|
---|
| 55 | minTimeout: 1 * 1000,
|
---|
| 56 | maxTimeout: 60 * 1000,
|
---|
| 57 | randomize: true,
|
---|
| 58 | });
|
---|
| 59 | ```
|
---|
| 60 |
|
---|
| 61 | ## API
|
---|
| 62 |
|
---|
| 63 | ### retry.operation([options])
|
---|
| 64 |
|
---|
| 65 | Creates a new `RetryOperation` object. `options` is the same as `retry.timeouts()`'s `options`, with two additions:
|
---|
| 66 |
|
---|
| 67 | * `forever`: Whether to retry forever, defaults to `false`.
|
---|
| 68 | * `unref`: Whether to [unref](https://nodejs.org/api/timers.html#timers_unref) the setTimeout's, defaults to `false`.
|
---|
| 69 | * `maxRetryTime`: The maximum time (in milliseconds) that the retried operation is allowed to run. Default is `Infinity`.
|
---|
| 70 |
|
---|
| 71 | ### retry.timeouts([options])
|
---|
| 72 |
|
---|
| 73 | Returns an array of timeouts. All time `options` and return values are in
|
---|
| 74 | milliseconds. If `options` is an array, a copy of that array is returned.
|
---|
| 75 |
|
---|
| 76 | `options` is a JS object that can contain any of the following keys:
|
---|
| 77 |
|
---|
| 78 | * `retries`: The maximum amount of times to retry the operation. Default is `10`. Seting this to `1` means `do it once, then retry it once`.
|
---|
| 79 | * `factor`: The exponential factor to use. Default is `2`.
|
---|
| 80 | * `minTimeout`: The number of milliseconds before starting the first retry. Default is `1000`.
|
---|
| 81 | * `maxTimeout`: The maximum number of milliseconds between two retries. Default is `Infinity`.
|
---|
| 82 | * `randomize`: Randomizes the timeouts by multiplying with a factor between `1` to `2`. Default is `false`.
|
---|
| 83 |
|
---|
| 84 | The formula used to calculate the individual timeouts is:
|
---|
| 85 |
|
---|
| 86 | ```
|
---|
| 87 | Math.min(random * minTimeout * Math.pow(factor, attempt), maxTimeout)
|
---|
| 88 | ```
|
---|
| 89 |
|
---|
| 90 | Have a look at [this article][article] for a better explanation of approach.
|
---|
| 91 |
|
---|
| 92 | If you want to tune your `factor` / `times` settings to attempt the last retry
|
---|
| 93 | after a certain amount of time, you can use wolfram alpha. For example in order
|
---|
| 94 | to tune for `10` attempts in `5 minutes`, you can use this equation:
|
---|
| 95 |
|
---|
| 96 | ![screenshot](https://github.com/tim-kos/node-retry/raw/master/equation.gif)
|
---|
| 97 |
|
---|
| 98 | Explaining the various values from left to right:
|
---|
| 99 |
|
---|
| 100 | * `k = 0 ... 9`: The `retries` value (10)
|
---|
| 101 | * `1000`: The `minTimeout` value in ms (1000)
|
---|
| 102 | * `x^k`: No need to change this, `x` will be your resulting factor
|
---|
| 103 | * `5 * 60 * 1000`: The desired total amount of time for retrying in ms (5 minutes)
|
---|
| 104 |
|
---|
| 105 | To make this a little easier for you, use wolfram alpha to do the calculations:
|
---|
| 106 |
|
---|
| 107 | <http://www.wolframalpha.com/input/?i=Sum%5B1000*x^k%2C+{k%2C+0%2C+9}%5D+%3D+5+*+60+*+1000>
|
---|
| 108 |
|
---|
| 109 | [article]: http://dthain.blogspot.com/2009/02/exponential-backoff-in-distributed.html
|
---|
| 110 |
|
---|
| 111 | ### retry.createTimeout(attempt, opts)
|
---|
| 112 |
|
---|
| 113 | Returns a new `timeout` (integer in milliseconds) based on the given parameters.
|
---|
| 114 |
|
---|
| 115 | `attempt` is an integer representing for which retry the timeout should be calculated. If your retry operation was executed 4 times you had one attempt and 3 retries. If you then want to calculate a new timeout, you should set `attempt` to 4 (attempts are zero-indexed).
|
---|
| 116 |
|
---|
| 117 | `opts` can include `factor`, `minTimeout`, `randomize` (boolean) and `maxTimeout`. They are documented above.
|
---|
| 118 |
|
---|
| 119 | `retry.createTimeout()` is used internally by `retry.timeouts()` and is public for you to be able to create your own timeouts for reinserting an item, see [issue #13](https://github.com/tim-kos/node-retry/issues/13).
|
---|
| 120 |
|
---|
| 121 | ### retry.wrap(obj, [options], [methodNames])
|
---|
| 122 |
|
---|
| 123 | Wrap all functions of the `obj` with retry. Optionally you can pass operation options and
|
---|
| 124 | an array of method names which need to be wrapped.
|
---|
| 125 |
|
---|
| 126 | ```
|
---|
| 127 | retry.wrap(obj)
|
---|
| 128 |
|
---|
| 129 | retry.wrap(obj, ['method1', 'method2'])
|
---|
| 130 |
|
---|
| 131 | retry.wrap(obj, {retries: 3})
|
---|
| 132 |
|
---|
| 133 | retry.wrap(obj, {retries: 3}, ['method1', 'method2'])
|
---|
| 134 | ```
|
---|
| 135 | The `options` object can take any options that the usual call to `retry.operation` can take.
|
---|
| 136 |
|
---|
| 137 | ### new RetryOperation(timeouts, [options])
|
---|
| 138 |
|
---|
| 139 | Creates a new `RetryOperation` where `timeouts` is an array where each value is
|
---|
| 140 | a timeout given in milliseconds.
|
---|
| 141 |
|
---|
| 142 | Available options:
|
---|
| 143 | * `forever`: Whether to retry forever, defaults to `false`.
|
---|
| 144 | * `unref`: Wether to [unref](https://nodejs.org/api/timers.html#timers_unref) the setTimeout's, defaults to `false`.
|
---|
| 145 |
|
---|
| 146 | If `forever` is true, the following changes happen:
|
---|
| 147 | * `RetryOperation.errors()` will only output an array of one item: the last error.
|
---|
| 148 | * `RetryOperation` will repeatedly use the `timeouts` array. Once all of its timeouts have been used up, it restarts with the first timeout, then uses the second and so on.
|
---|
| 149 |
|
---|
| 150 | #### retryOperation.errors()
|
---|
| 151 |
|
---|
| 152 | Returns an array of all errors that have been passed to `retryOperation.retry()` so far. The
|
---|
| 153 | returning array has the errors ordered chronologically based on when they were passed to
|
---|
| 154 | `retryOperation.retry()`, which means the first passed error is at index zero and the last is
|
---|
| 155 | at the last index.
|
---|
| 156 |
|
---|
| 157 | #### retryOperation.mainError()
|
---|
| 158 |
|
---|
| 159 | A reference to the error object that occured most frequently. Errors are
|
---|
| 160 | compared using the `error.message` property.
|
---|
| 161 |
|
---|
| 162 | If multiple error messages occured the same amount of time, the last error
|
---|
| 163 | object with that message is returned.
|
---|
| 164 |
|
---|
| 165 | If no errors occured so far, the value is `null`.
|
---|
| 166 |
|
---|
| 167 | #### retryOperation.attempt(fn, timeoutOps)
|
---|
| 168 |
|
---|
| 169 | Defines the function `fn` that is to be retried and executes it for the first
|
---|
| 170 | time right away. The `fn` function can receive an optional `currentAttempt` callback that represents the number of attempts to execute `fn` so far.
|
---|
| 171 |
|
---|
| 172 | Optionally defines `timeoutOps` which is an object having a property `timeout` in miliseconds and a property `cb` callback function.
|
---|
| 173 | Whenever your retry operation takes longer than `timeout` to execute, the timeout callback function `cb` is called.
|
---|
| 174 |
|
---|
| 175 |
|
---|
| 176 | #### retryOperation.try(fn)
|
---|
| 177 |
|
---|
| 178 | This is an alias for `retryOperation.attempt(fn)`. This is deprecated. Please use `retryOperation.attempt(fn)` instead.
|
---|
| 179 |
|
---|
| 180 | #### retryOperation.start(fn)
|
---|
| 181 |
|
---|
| 182 | This is an alias for `retryOperation.attempt(fn)`. This is deprecated. Please use `retryOperation.attempt(fn)` instead.
|
---|
| 183 |
|
---|
| 184 | #### retryOperation.retry(error)
|
---|
| 185 |
|
---|
| 186 | Returns `false` when no `error` value is given, or the maximum amount of retries
|
---|
| 187 | has been reached.
|
---|
| 188 |
|
---|
| 189 | Otherwise it returns `true`, and retries the operation after the timeout for
|
---|
| 190 | the current attempt number.
|
---|
| 191 |
|
---|
| 192 | #### retryOperation.stop()
|
---|
| 193 |
|
---|
| 194 | Allows you to stop the operation being retried. Useful for aborting the operation on a fatal error etc.
|
---|
| 195 |
|
---|
| 196 | #### retryOperation.reset()
|
---|
| 197 |
|
---|
| 198 | Resets the internal state of the operation object, so that you can call `attempt()` again as if this was a new operation object.
|
---|
| 199 |
|
---|
| 200 | #### retryOperation.attempts()
|
---|
| 201 |
|
---|
| 202 | Returns an int representing the number of attempts it took to call `fn` before it was successful.
|
---|
| 203 |
|
---|
| 204 | ## License
|
---|
| 205 |
|
---|
| 206 | retry is licensed under the MIT license.
|
---|
| 207 |
|
---|
| 208 |
|
---|
| 209 | # Changelog
|
---|
| 210 |
|
---|
| 211 | 0.10.0 Adding `stop` functionality, thanks to @maxnachlinger.
|
---|
| 212 |
|
---|
| 213 | 0.9.0 Adding `unref` functionality, thanks to @satazor.
|
---|
| 214 |
|
---|
| 215 | 0.8.0 Implementing retry.wrap.
|
---|
| 216 |
|
---|
| 217 | 0.7.0 Some bug fixes and made retry.createTimeout() public. Fixed issues [#10](https://github.com/tim-kos/node-retry/issues/10), [#12](https://github.com/tim-kos/node-retry/issues/12), and [#13](https://github.com/tim-kos/node-retry/issues/13).
|
---|
| 218 |
|
---|
| 219 | 0.6.0 Introduced optional timeOps parameter for the attempt() function which is an object having a property timeout in milliseconds and a property cb callback function. Whenever your retry operation takes longer than timeout to execute, the timeout callback function cb is called.
|
---|
| 220 |
|
---|
| 221 | 0.5.0 Some minor refactoring.
|
---|
| 222 |
|
---|
| 223 | 0.4.0 Changed retryOperation.try() to retryOperation.attempt(). Deprecated the aliases start() and try() for it.
|
---|
| 224 |
|
---|
| 225 | 0.3.0 Added retryOperation.start() which is an alias for retryOperation.try().
|
---|
| 226 |
|
---|
| 227 | 0.2.0 Added attempts() function and parameter to retryOperation.try() representing the number of attempts it took to call fn().
|
---|