datum-focus
Version:
Data shape, model, metadata, JSON, JSON Schema, GraphQL, MongoDB query and aggregations, iterator generators
116 lines (99 loc) • 2.83 kB
text/typescript
import { RetryOperation } from './operation';
interface RetryOptions {
retries: number;
factor: number;
forever: boolean;
minTimeout: number;
maxTimeout: number;
randomize: boolean;
maxRetryTime: number;
unref: any;
}
export function operation(options: Partial<RetryOptions>) {
const timeouts = exports.timeouts(options);
return new RetryOperation(timeouts, {
forever: options && (options.forever || options.retries === Infinity),
unref: options && options.unref,
maxRetryTime: options && options.maxRetryTime
});
};
export function timeouts(options: RetryOptions) {
if (options instanceof Array) {
return Object.assign([], options);
}
const opts: Pick<
RetryOptions,
'retries' | 'factor' | 'minTimeout' | 'maxTimeout' | 'randomize'
> & Partial<Pick<RetryOptions, 'unref'>> = {
retries: 10,
factor: 2,
minTimeout: 1 * 1000,
maxTimeout: Infinity,
randomize: false
};
for (const key in options) {
opts[key] = options[key];
}
if (opts.minTimeout > opts.maxTimeout) {
throw new Error('minTimeout is greater than maxTimeout');
}
const timeouts: number[] = [];
for (let i: number = 0; i < opts.retries; i++) {
timeouts.push(createTimeout(i, opts));
}
if (options && options.forever && !timeouts.length) {
timeouts.push(createTimeout(opts.retries, opts));
}
// sort the array numerically ascending
timeouts.sort(function (a, b) {
return a - b;
});
return timeouts;
}
export function createTimeout(attempt: number, opts: Pick<
RetryOptions,
'factor' | 'minTimeout' | 'maxTimeout' | 'randomize'
>) {
const random = (opts.randomize)
? (Math.random() + 1)
: 1;
let timeout = Math.round(random * Math.max(opts.minTimeout, 1) * Math.pow(opts.factor, attempt));
timeout = Math.min(timeout, opts.maxTimeout);
return timeout;
};
export function wrap(obj: any, options: any, methods: any) {
if (options instanceof Array) {
methods = options;
options = null;
}
if (!methods) {
methods = [];
for (const key in obj) {
if (typeof obj[key] === 'function') {
methods.push(key);
}
}
}
for (let i = 0; i < methods.length; i++) {
const method = methods[i];
const original = obj[method];
obj[method] = function retryWrapper(original: any) {
const op = exports.operation(options);
const args = Array.prototype.slice.call(arguments, 1);
const callback = args.pop();
args.push(function (err: any) {
if (op.retry(err)) {
return;
}
if (err) {
arguments[0] = op.mainError();
}
callback.apply(obj, arguments);
});
op.attempt(function () {
original.apply(obj, args);
});
}.bind(obj, original);
obj[method].options = options;
}
}