redis-smq-common
Version:
Provides essential components and utilities shared across RedisSMQ packages.
87 lines • 2.83 kB
JavaScript
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