0
# Retry
1
2
Retry provides abstraction for exponential and custom retry strategies for failed operations. It offers both operation-based retry patterns through RetryOperation objects and functional wrapper patterns that can automatically add retry logic to existing functions.
3
4
## Package Information
5
6
- **Package Name**: retry
7
- **Package Type**: npm
8
- **Language**: JavaScript
9
- **Installation**: `npm install retry`
10
- **Engine Requirements**: Node.js >= 4
11
12
## Core Imports
13
14
```javascript
15
const retry = require("retry");
16
```
17
18
Note: This package uses CommonJS exports only. ES module syntax can be used with bundlers or transpilers:
19
20
```javascript
21
import retry from "retry";
22
```
23
24
Individual function imports:
25
26
```javascript
27
const { operation, timeouts, createTimeout, wrap } = require("retry");
28
```
29
30
## Basic Usage
31
32
```javascript
33
const retry = require("retry");
34
35
function faultTolerantResolve(address, cb) {
36
const operation = retry.operation();
37
38
operation.attempt(function(currentAttempt) {
39
dns.resolve(address, function(err, addresses) {
40
if (operation.retry(err)) {
41
return;
42
}
43
44
cb(err ? operation.mainError() : null, addresses);
45
});
46
});
47
}
48
```
49
50
## Architecture
51
52
Retry is built around several key components:
53
54
- **RetryOperation Class**: Manages individual retry operation lifecycle with error collection and timer management
55
- **Exponential Backoff**: Configurable timeout calculation using exponential growth with optional randomization
56
- **Error Aggregation**: Collects and analyzes errors across retry attempts to identify most common failures
57
- **Method Wrapping**: Automatic retry logic injection into existing object methods
58
- **Timer Management**: Precise timeout control with optional unreferencing for process exit handling
59
60
## Capabilities
61
62
### Retry Operations
63
64
Create and manage retry operations with exponential backoff strategies. The core functionality for handling failed operations with configurable retry policies.
65
66
```javascript { .api }
67
/**
68
* Creates a new RetryOperation instance with computed timeouts
69
* @param {Object} [options] - Configuration options for retry behavior
70
* @param {number} [options.retries=10] - Maximum number of retry attempts
71
* @param {number} [options.factor=2] - Exponential backoff factor
72
* @param {number} [options.minTimeout=1000] - Minimum timeout in milliseconds
73
* @param {number} [options.maxTimeout=Infinity] - Maximum timeout in milliseconds
74
* @param {boolean} [options.randomize=false] - Add randomization to timeouts
75
* @param {boolean} [options.forever=false] - Whether to retry forever
76
* @param {boolean} [options.unref=false] - Whether to unref setTimeout calls
77
* @param {number} [options.maxRetryTime=Infinity] - Maximum time allowed for retries in milliseconds
78
* @returns {RetryOperation} RetryOperation instance
79
*/
80
function operation(options);
81
```
82
83
**Usage Examples:**
84
85
```javascript
86
const retry = require("retry");
87
88
// Basic retry with default settings (10 retries, exponential backoff)
89
const operation = retry.operation();
90
91
// Custom retry configuration
92
const operation = retry.operation({
93
retries: 5,
94
factor: 3,
95
minTimeout: 1 * 1000,
96
maxTimeout: 60 * 1000,
97
randomize: true,
98
});
99
100
// Retry forever with maximum retry time limit
101
const operation = retry.operation({
102
forever: true,
103
maxRetryTime: 5 * 60 * 1000, // 5 minutes max
104
});
105
```
106
107
### Timeout Generation
108
109
Generate arrays of timeout values for exponential backoff strategies. Used internally by operations but also available for custom timeout management.
110
111
```javascript { .api }
112
/**
113
* Generates array of timeout values for exponential backoff
114
* @param {Object|Array} [options] - Timeout configuration or array of timeout values
115
* @param {number} [options.retries=10] - Maximum number of retries
116
* @param {number} [options.factor=2] - Exponential backoff factor
117
* @param {number} [options.minTimeout=1000] - Minimum timeout in milliseconds
118
* @param {number} [options.maxTimeout=Infinity] - Maximum timeout in milliseconds
119
* @param {boolean} [options.randomize=false] - Add randomization to timeouts
120
* @param {boolean} [options.forever=false] - Enable infinite retries
121
* @returns {number[]} Array of timeout values in milliseconds
122
*/
123
function timeouts(options);
124
125
/**
126
* Calculates timeout value for specific attempt number
127
* @param {number} attempt - Zero-indexed attempt number
128
* @param {Object} opts - Options containing factor, minTimeout, maxTimeout, randomize
129
* @param {number} opts.factor - Exponential backoff factor
130
* @param {number} opts.minTimeout - Minimum timeout in milliseconds
131
* @param {number} opts.maxTimeout - Maximum timeout in milliseconds
132
* @param {boolean} [opts.randomize=false] - Add randomization to timeout
133
* @returns {number} Timeout value in milliseconds
134
*/
135
function createTimeout(attempt, opts);
136
```
137
138
**Usage Examples:**
139
140
```javascript
141
const retry = require("retry");
142
143
// Generate timeout array
144
const timeouts = retry.timeouts({
145
retries: 3,
146
factor: 2,
147
minTimeout: 1000,
148
maxTimeout: 5000,
149
randomize: true
150
});
151
// Result: [1000-2000, 2000-4000, 4000-5000] (randomized)
152
153
// Calculate specific timeout
154
const timeout = retry.createTimeout(2, {
155
factor: 2,
156
minTimeout: 1000,
157
maxTimeout: 10000
158
});
159
// Result: 4000 (1000 * 2^2)
160
161
// Use array of custom timeouts
162
const customTimeouts = retry.timeouts([100, 500, 1000, 2000]);
163
```
164
165
### Method Wrapping
166
167
Automatically add retry logic to existing object methods. Useful for wrapping APIs, database clients, or any object with methods that may fail.
168
169
```javascript { .api }
170
/**
171
* Wraps object methods with retry logic
172
* @param {Object} obj - Object containing methods to wrap
173
* @param {Object|Array} [options] - Retry options or method names array
174
* @param {Array} [methods] - Array of method names to wrap
175
* @returns {void} Modifies obj in place
176
*/
177
function wrap(obj, options, methods);
178
```
179
180
**Usage Examples:**
181
182
```javascript
183
const retry = require("retry");
184
const dns = require("dns");
185
186
// Wrap all functions in an object
187
retry.wrap(dns);
188
189
// Wrap specific methods
190
retry.wrap(dns, ["resolve", "lookup"]);
191
192
// Wrap with custom retry options
193
retry.wrap(dns, {
194
retries: 3,
195
factor: 2,
196
minTimeout: 1000
197
});
198
199
// Wrap specific methods with options
200
retry.wrap(dns, {
201
retries: 5,
202
maxTimeout: 10000
203
}, ["resolve"]);
204
205
// The wrapped methods now automatically retry on errors
206
dns.resolve("example.com", function(err, addresses) {
207
// This will retry up to the configured number of times
208
console.log(err, addresses);
209
});
210
```
211
212
### RetryOperation Management
213
214
Direct management of RetryOperation instances for fine-grained control over retry behavior and error handling.
215
216
```javascript { .api }
217
/**
218
* RetryOperation constructor - manages individual retry operation lifecycle
219
* @param {number[]} timeouts - Array of timeout values in milliseconds
220
* @param {Object|boolean} [options] - Configuration options
221
* @param {boolean} [options.forever=false] - Whether to retry forever
222
* @param {boolean} [options.unref=false] - Whether to unref setTimeout calls
223
* @param {number} [options.maxRetryTime=Infinity] - Maximum time allowed for retries
224
*/
225
function RetryOperation(timeouts, options);
226
227
/**
228
* Executes function with retry capability
229
* @param {Function} fn - Function to execute, receives currentAttempt as parameter
230
* @param {Object} [timeoutOps] - Timeout configuration
231
* @param {number} [timeoutOps.timeout] - Operation timeout in milliseconds
232
* @param {Function} [timeoutOps.cb] - Timeout callback function
233
* @returns {void}
234
*/
235
RetryOperation.prototype.attempt = function(fn, timeoutOps);
236
237
/**
238
* Determines if operation should be retried based on error
239
* @param {Error|null} err - Error from operation attempt
240
* @returns {boolean} true if operation will be retried, false otherwise
241
*/
242
RetryOperation.prototype.retry = function(err);
243
244
/**
245
* Stops retry operation and clears timers
246
* @returns {void}
247
*/
248
RetryOperation.prototype.stop = function();
249
250
/**
251
* Resets operation to initial state for reuse
252
* @returns {void}
253
*/
254
RetryOperation.prototype.reset = function();
255
256
/**
257
* Returns array of all errors encountered during retries
258
* @returns {Error[]} Array of Error objects in chronological order
259
*/
260
RetryOperation.prototype.errors = function();
261
262
/**
263
* Returns current attempt count
264
* @returns {number} Number representing attempt count
265
*/
266
RetryOperation.prototype.attempts = function();
267
268
/**
269
* Returns most frequently occurring error
270
* @returns {Error|null} Error object or null if no errors
271
*/
272
RetryOperation.prototype.mainError = function();
273
274
/**
275
* @deprecated Use attempt() instead
276
* Deprecated alias for attempt()
277
* @param {Function} fn - Function to execute
278
* @returns {void}
279
*/
280
RetryOperation.prototype.try = function(fn);
281
282
/**
283
* @deprecated Use attempt() instead
284
* Deprecated alias for attempt()
285
* @param {Function} fn - Function to execute
286
* @returns {void}
287
*/
288
RetryOperation.prototype.start = function(fn);
289
```
290
291
**Usage Examples:**
292
293
```javascript
294
const retry = require("retry");
295
296
// Direct RetryOperation usage
297
const timeouts = retry.timeouts({ retries: 3 });
298
const operation = new retry.RetryOperation(timeouts);
299
300
operation.attempt(function(currentAttempt) {
301
console.log(`Attempt ${currentAttempt}`);
302
303
asyncOperation(function(err, result) {
304
if (err && err.message === 'FATAL_ERROR') {
305
// Stop retrying on fatal errors
306
operation.stop();
307
return callback(err);
308
}
309
310
if (operation.retry(err)) {
311
return; // Will retry
312
}
313
314
// Final result (success or max retries reached)
315
if (err) {
316
console.log('All errors:', operation.errors());
317
console.log('Main error:', operation.mainError());
318
console.log('Total attempts:', operation.attempts());
319
}
320
321
callback(operation.mainError(), result);
322
});
323
});
324
325
// Reset and reuse operation
326
operation.reset();
327
operation.attempt(/* ... */);
328
```
329
330
## Types
331
332
```javascript { .api }
333
/**
334
* Configuration options for retry operations
335
* @typedef {Object} RetryOptions
336
* @property {number} [retries=10] - Maximum number of retry attempts
337
* @property {number} [factor=2] - Exponential backoff factor
338
* @property {number} [minTimeout=1000] - Minimum timeout in milliseconds
339
* @property {number} [maxTimeout=Infinity] - Maximum timeout in milliseconds
340
* @property {boolean} [randomize=false] - Add randomization to timeouts
341
* @property {boolean} [forever=false] - Whether to retry forever
342
* @property {boolean} [unref=false] - Whether to unref setTimeout calls
343
* @property {number} [maxRetryTime=Infinity] - Maximum time allowed for retries in milliseconds
344
*/
345
346
/**
347
* Timeout operation configuration
348
* @typedef {Object} TimeoutOps
349
* @property {number} timeout - Operation timeout in milliseconds
350
* @property {Function} cb - Timeout callback function
351
*/
352
```
353
354
## Error Handling
355
356
The retry library provides comprehensive error handling and analysis:
357
358
### Error Collection
359
- All retry attempts collect errors in chronological order via `errors()`
360
- `mainError()` returns the most frequently occurring error based on error message
361
- Error aggregation supports detailed failure analysis
362
363
### Special Error Behaviors
364
- **Timeout errors**: Automatically added when `maxRetryTime` is exceeded
365
- **Fatal error handling**: Use `operation.stop()` to abort retries for unrecoverable errors
366
- **Forever mode**: When `forever: true`, only keeps the last error to prevent memory leaks
367
368
### Timeout Formula
369
The timeout calculation uses the following formula:
370
```
371
timeout = Math.min(random * Math.max(minTimeout, 1) * Math.pow(factor, attempt), maxTimeout)
372
```
373
Where `random` is 1 or a value between 1-2 if `randomize` is enabled. The `Math.max(minTimeout, 1)` ensures a minimum timeout of at least 1 millisecond.
374
375
## Common Patterns
376
377
### DNS Resolution with Retry
378
```javascript
379
const dns = require("dns");
380
const retry = require("retry");
381
382
function faultTolerantResolve(address, cb) {
383
const operation = retry.operation({
384
retries: 2,
385
factor: 2,
386
minTimeout: 1 * 1000,
387
maxTimeout: 2 * 1000,
388
randomize: true
389
});
390
391
operation.attempt(function(currentAttempt) {
392
dns.resolve(address, function(err, addresses) {
393
if (operation.retry(err)) {
394
return;
395
}
396
397
cb(operation.mainError(), operation.errors(), addresses);
398
});
399
});
400
}
401
```
402
403
### API Client with Fatal Error Handling
404
```javascript
405
function apiCall(endpoint, data, callback) {
406
const operation = retry.operation({ retries: 3 });
407
408
operation.attempt(function(currentAttempt) {
409
httpRequest(endpoint, data, function(err, response) {
410
if (err && err.statusCode === 401) {
411
// Don't retry authentication errors
412
operation.stop();
413
return callback(err);
414
}
415
416
if (operation.retry(err)) {
417
return;
418
}
419
420
callback(operation.mainError(), response);
421
});
422
});
423
}
424
```
425
426
### Database Connection with Wrapping
427
```javascript
428
const retry = require("retry");
429
430
// Wrap database client methods
431
retry.wrap(dbClient, {
432
retries: 5,
433
factor: 1.5,
434
minTimeout: 500,
435
maxTimeout: 10000
436
}, ["query", "connect"]);
437
438
// Now database operations automatically retry
439
dbClient.query("SELECT * FROM users", function(err, results) {
440
// This query will retry up to 5 times on failure
441
console.log(err, results);
442
});
443
```