UNPKG

redis-smq-common

Version:

Provides essential components and utilities shared across RedisSMQ packages.

87 lines 2.83 kB
import { Runnable } from '../runnable/index.js'; import { AbortError } from '../errors/index.js'; import { Timer } from '../timer/index.js'; import { BackoffConfig } from './backoff-config.js'; export class Backoff extends Runnable { timer; config; logger; attempts = 0; constructor(logger, config = {}) { super(); this.logger = logger.createLogger(this.constructor.name); this.config = BackoffConfig.parseConfig(config); this.timer = new Timer(this.logger); this.logger.debug(`Backoff initialized: baseDelay=${this.config.baseDelay}ms, maxDelay=${this.config.maxDelay}ms`); } runTask(task, callback) { if (!this.isOperational()) { return callback(new Error('Backoff is not operational')); } this.attempts++; task((err, result) => { if (err) { if (err instanceof AbortError) { return callback(err); } this.logger.debug(`Operation failed:`, err.message); if (this.config.maxAttempts && this.attempts >= this.config.maxAttempts) { return callback(err); } this.scheduleRetry(task, callback); return; } this.reset(); callback(null, result); }); } scheduleRetry(task, callback) { if (!this.isOperational()) { return callback(new Error('Backoff is not operational')); } const delay = this.calculateNextDelay(); this.timer.schedule(() => { this.runTask(task, callback); }, delay); } calculateNextDelay() { const { baseDelay, maxDelay, jitter } = this.config; let delay = this.getNextDelay(baseDelay, this.attempts); delay = Math.max(delay, baseDelay); if (jitter) { const range = delay * 0.2; delay = delay + (Math.random() * range * 2 - range); } delay = Math.min(delay, maxDelay); return Math.max(1, Math.floor(delay)); } goingUp() { return super.goingUp().concat([(cb) => this.timer.run(cb)]); } goingDown() { return [ (cb) => { this.reset(); this.timer.shutdown(cb); }, ].concat(super.goingDown()); } getConfig() { return { ...this.config }; } execute(task, callback) { if (!this.isOperational()) { return callback(new Error('Backoff instance is not operational')); } this.runTask(task, callback); } getAttempts() { return this.attempts; } reset() { this.timer.reset(); this.attempts = 0; } } //# sourceMappingURL=backoff.js.map