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 8Abstraction for exponential and custom retry strategies for failed operations. 9 10## Installation 11 12 npm install retry 13 14## Current Status 15 16This module has been tested and is ready to be used. 17 18## Tutorial 19 20The example below will retry a potentially failing `dns.resolve` operation 21`10` times using an exponential backoff strategy. With the default settings, this 22means the last attempt is made after `17 minutes and 3 seconds`. 23 24``` javascript 25var dns = require('dns'); 26var retry = require('retry'); 27 28function 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 42faultTolerantResolve('nodejs.org', function(err, addresses) { 43 console.log(err, addresses); 44}); 45``` 46 47Of course you can also configure the factors that go into the exponential 48backoff. See the API documentation below for all available settings. 49currentAttempt is an int representing the number of attempts so far. 50 51``` javascript 52var 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 65Creates 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 73Returns an array of timeouts. All time `options` and return values are in 74milliseconds. 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 84The formula used to calculate the individual timeouts is: 85 86``` 87Math.min(random * minTimeout * Math.pow(factor, attempt), maxTimeout) 88``` 89 90Have a look at [this article][article] for a better explanation of approach. 91 92If you want to tune your `factor` / `times` settings to attempt the last retry 93after a certain amount of time, you can use wolfram alpha. For example in order 94to 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 98Explaining 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 105To 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 113Returns 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 123Wrap all functions of the `obj` with retry. Optionally you can pass operation options and 124an array of method names which need to be wrapped. 125 126``` 127retry.wrap(obj) 128 129retry.wrap(obj, ['method1', 'method2']) 130 131retry.wrap(obj, {retries: 3}) 132 133retry.wrap(obj, {retries: 3}, ['method1', 'method2']) 134``` 135The `options` object can take any options that the usual call to `retry.operation` can take. 136 137### new RetryOperation(timeouts, [options]) 138 139Creates a new `RetryOperation` where `timeouts` is an array where each value is 140a timeout given in milliseconds. 141 142Available 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 146If `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 152Returns an array of all errors that have been passed to `retryOperation.retry()` so far. The 153returning 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 155at the last index. 156 157#### retryOperation.mainError() 158 159A reference to the error object that occured most frequently. Errors are 160compared using the `error.message` property. 161 162If multiple error messages occured the same amount of time, the last error 163object with that message is returned. 164 165If no errors occured so far, the value is `null`. 166 167#### retryOperation.attempt(fn, timeoutOps) 168 169Defines the function `fn` that is to be retried and executes it for the first 170time right away. The `fn` function can receive an optional `currentAttempt` callback that represents the number of attempts to execute `fn` so far. 171 172Optionally defines `timeoutOps` which is an object having a property `timeout` in miliseconds and a property `cb` callback function. 173Whenever your retry operation takes longer than `timeout` to execute, the timeout callback function `cb` is called. 174 175 176#### retryOperation.try(fn) 177 178This is an alias for `retryOperation.attempt(fn)`. This is deprecated. Please use `retryOperation.attempt(fn)` instead. 179 180#### retryOperation.start(fn) 181 182This is an alias for `retryOperation.attempt(fn)`. This is deprecated. Please use `retryOperation.attempt(fn)` instead. 183 184#### retryOperation.retry(error) 185 186Returns `false` when no `error` value is given, or the maximum amount of retries 187has been reached. 188 189Otherwise it returns `true`, and retries the operation after the timeout for 190the current attempt number. 191 192#### retryOperation.stop() 193 194Allows you to stop the operation being retried. Useful for aborting the operation on a fatal error etc. 195 196#### retryOperation.reset() 197 198Resets 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 202Returns an int representing the number of attempts it took to call `fn` before it was successful. 203 204## License 205 206retry is licensed under the MIT license. 207 208 209# Changelog 210 2110.10.0 Adding `stop` functionality, thanks to @maxnachlinger. 212 2130.9.0 Adding `unref` functionality, thanks to @satazor. 214 2150.8.0 Implementing retry.wrap. 216 2170.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 2190.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 2210.5.0 Some minor refactoring. 222 2230.4.0 Changed retryOperation.try() to retryOperation.attempt(). Deprecated the aliases start() and try() for it. 224 2250.3.0 Added retryOperation.start() which is an alias for retryOperation.try(). 226 2270.2.0 Added attempts() function and parameter to retryOperation.try() representing the number of attempts it took to call fn(). 228