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().
|