datum-focus
Version:
Data shape, model, metadata, JSON, JSON Schema, GraphQL, MongoDB query and aggregations, iterator generators
157 lines (128 loc) • 3.54 kB
text/typescript
export class RetryOperation {
_originalTimeouts: any;
_timeouts: any;
_options: any;
_maxRetryTime: any;
_fn: any | null = null;
_errors: any[] = [];
_attempts: number = 1;
_operationTimeout: any | null = null;
_operationTimeoutCb: any | null = null;
_timeout: any | null = null;
_operationStart: any | null = null;
_timer: any | null = null;
_cachedTimeouts: any;
constructor(timeouts: any, options: any) {
// Compatibility for the old (timeouts, retryForever) signature
if (typeof options === 'boolean') {
options = { forever: options };
}
this._originalTimeouts = JSON.parse(JSON.stringify(timeouts));
this._timeouts = timeouts;
this._options = options || {};
this._maxRetryTime = options && options.maxRetryTime || Infinity;
if (this._options.forever) {
this._cachedTimeouts = this._timeouts.slice(0);
}
}
reset() {
this._attempts = 1;
this._timeouts = this._originalTimeouts.slice(0);
}
stop() {
if (this._timeout) {
clearTimeout(this._timeout);
}
if (this._timer) {
clearTimeout(this._timer);
}
this._timeouts = [];
this._cachedTimeouts = null;
}
retry(err: any) {
if (this._timeout) {
clearTimeout(this._timeout);
}
if (!err) {
return false;
}
const currentTime = new Date().getTime();
if (err && currentTime - this._operationStart >= this._maxRetryTime) {
this._errors.push(err);
this._errors.unshift(new Error('RetryOperation timeout occurred'));
return false;
}
this._errors.push(err);
let timeout = this._timeouts.shift();
if (timeout === undefined) {
if (this._cachedTimeouts) {
// retry forever, only keep last error
this._errors.splice(0, this._errors.length - 1);
timeout = this._cachedTimeouts.slice(-1);
} else {
return false;
}
}
const self = this;
this._timer = setTimeout(function () {
self._attempts++;
if (self._operationTimeoutCb) {
self._timeout = setTimeout(function () {
self._operationTimeoutCb(self._attempts);
}, self._operationTimeout);
if (self._options.unref) {
self._timeout.unref();
}
}
self._fn(self._attempts);
}, timeout);
if (this._options.unref) {
this._timer.unref();
}
return true;
}
attempt(fn: any, timeoutOps?: any) {
this._fn = fn;
if (timeoutOps) {
if (timeoutOps.timeout) {
this._operationTimeout = timeoutOps.timeout;
}
if (timeoutOps.cb) {
this._operationTimeoutCb = timeoutOps.cb;
}
}
const self = this;
if (this._operationTimeoutCb) {
this._timeout = setTimeout(function () {
self._operationTimeoutCb();
}, self._operationTimeout);
}
this._operationStart = new Date().getTime();
this._fn(this._attempts);
};
errors() {
return this._errors;
}
attempts() {
return this._attempts;
}
mainError() {
if (this._errors.length === 0) {
return null;
}
const counts: any = {};
let mainError: any = null;
let mainErrorCount = 0;
for (let i = 0; i < this._errors.length; i++) {
const error = this._errors[i];
const message = error.message;
const count = (counts[message] || 0) + 1;
counts[message] = count;
if (count >= mainErrorCount) {
mainError = error;
mainErrorCount = count;
}
}
return mainError;
}
}