98 lines
6.0 KiB
JavaScript
98 lines
6.0 KiB
JavaScript
|
(function () {
|
||
|
|
||
|
/* Imports */
|
||
|
var Meteor = Package.meteor.Meteor;
|
||
|
var _ = Package.underscore._;
|
||
|
var Random = Package.random.Random;
|
||
|
|
||
|
/* Package-scope variables */
|
||
|
var Retry;
|
||
|
|
||
|
(function () {
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// packages/retry/retry.js //
|
||
|
// //
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Retry logic with an exponential backoff. // 1
|
||
|
// // 2
|
||
|
// options: // 3
|
||
|
// baseTimeout: time for initial reconnect attempt (ms). // 4
|
||
|
// exponent: exponential factor to increase timeout each attempt. // 5
|
||
|
// maxTimeout: maximum time between retries (ms). // 6
|
||
|
// minCount: how many times to reconnect "instantly". // 7
|
||
|
// minTimeout: time to wait for the first `minCount` retries (ms). // 8
|
||
|
// fuzz: factor to randomize retry times by (to avoid retry storms). // 9
|
||
|
// 10
|
||
|
Retry = function (options) { // 11
|
||
|
var self = this; // 12
|
||
|
_.extend(self, _.defaults(_.clone(options || {}), { // 13
|
||
|
baseTimeout: 1000, // 1 second // 14
|
||
|
exponent: 2.2, // 15
|
||
|
// The default is high-ish to ensure a server can recover from a // 16
|
||
|
// failure caused by load. // 17
|
||
|
maxTimeout: 5 * 60000, // 5 minutes // 18
|
||
|
minTimeout: 10, // 19
|
||
|
minCount: 2, // 20
|
||
|
fuzz: 0.5 // +- 25% // 21
|
||
|
})); // 22
|
||
|
self.retryTimer = null; // 23
|
||
|
}; // 24
|
||
|
// 25
|
||
|
_.extend(Retry.prototype, { // 26
|
||
|
// 27
|
||
|
// Reset a pending retry, if any. // 28
|
||
|
clear: function () { // 29
|
||
|
var self = this; // 30
|
||
|
if (self.retryTimer) // 31
|
||
|
clearTimeout(self.retryTimer); // 32
|
||
|
self.retryTimer = null; // 33
|
||
|
}, // 34
|
||
|
// 35
|
||
|
// Calculate how long to wait in milliseconds to retry, based on the // 36
|
||
|
// `count` of which retry this is. // 37
|
||
|
_timeout: function (count) { // 38
|
||
|
var self = this; // 39
|
||
|
// 40
|
||
|
if (count < self.minCount) // 41
|
||
|
return self.minTimeout; // 42
|
||
|
// 43
|
||
|
var timeout = Math.min( // 44
|
||
|
self.maxTimeout, // 45
|
||
|
self.baseTimeout * Math.pow(self.exponent, count)); // 46
|
||
|
// fuzz the timeout randomly, to avoid reconnect storms when a // 47
|
||
|
// server goes down. // 48
|
||
|
timeout = timeout * ((Random.fraction() * self.fuzz) + // 49
|
||
|
(1 - self.fuzz/2)); // 50
|
||
|
return timeout; // 51
|
||
|
}, // 52
|
||
|
// 53
|
||
|
// Call `fn` after a delay, based on the `count` of which retry this is. // 54
|
||
|
retryLater: function (count, fn) { // 55
|
||
|
var self = this; // 56
|
||
|
var timeout = self._timeout(count); // 57
|
||
|
if (self.retryTimer) // 58
|
||
|
clearTimeout(self.retryTimer); // 59
|
||
|
self.retryTimer = Meteor.setTimeout(fn, timeout); // 60
|
||
|
return timeout; // 61
|
||
|
} // 62
|
||
|
// 63
|
||
|
}); // 64
|
||
|
// 65
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
}).call(this);
|
||
|
|
||
|
|
||
|
/* Exports */
|
||
|
if (typeof Package === 'undefined') Package = {};
|
||
|
Package.retry = {
|
||
|
Retry: Retry
|
||
|
};
|
||
|
|
||
|
})();
|
||
|
|
||
|
//# sourceMappingURL=retry.js.map
|