redis-smq
Version:
A simple high-performance Redis message queue for Node.js.
201 lines • 8.69 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConsumerHeartbeat = void 0;
const os = __importStar(require("os"));
const redis_smq_common_1 = require("redis-smq-common");
const redis_keys_js_1 = require("../../../common/redis-keys/redis-keys.js");
const index_js_1 = require("../../../config/index.js");
const event_bus_publisher_js_1 = require("./event-bus-publisher.js");
const cpuUsageStatsRef = {
cpuUsage: process.cpuUsage(),
time: process.hrtime(),
};
function cpuUsage() {
const currentTimestamp = process.hrtime();
const currentCPUUsage = process.cpuUsage();
const timestampDiff = process.hrtime(cpuUsageStatsRef.time);
const cpuUsageDiff = process.cpuUsage(cpuUsageStatsRef.cpuUsage);
cpuUsageStatsRef.time = currentTimestamp;
cpuUsageStatsRef.cpuUsage = currentCPUUsage;
const hrtime = (time) => {
return time[0] * 1e3 + time[1] / 1e6;
};
const usageTime = (time) => {
return time / 1000;
};
return Object.assign({ percentage: ((usageTime(cpuUsageDiff.user + cpuUsageDiff.system) /
hrtime(timestampDiff)) *
100).toFixed(1) }, cpuUsageDiff);
}
class ConsumerHeartbeat extends redis_smq_common_1.Runnable {
constructor(consumer, redisClient, eventBus) {
super();
this.consumer = consumer;
this.logger = redis_smq_common_1.logger.getLogger(index_js_1.Configuration.getSetConfig().logger, this.constructor.name.toLowerCase());
this.redisClient = redisClient;
this.logger.debug(`Initializing ConsumerHeartbeat for consumer ${consumer.getId()}`);
if (eventBus) {
this.logger.debug('Event bus provided, setting up event bus publisher');
(0, event_bus_publisher_js_1.eventBusPublisher)(this, eventBus, this.logger);
}
else {
this.logger.debug('No event bus provided, skipping event bus publisher setup');
}
const { keyConsumerHeartbeat } = redis_keys_js_1.redisKeys.getConsumerKeys(consumer.getId());
this.keyConsumerHeartbeat = keyConsumerHeartbeat;
this.logger.debug(`Consumer heartbeat key: ${this.keyConsumerHeartbeat}`);
this.timer = new redis_smq_common_1.Timer();
this.timer.on('error', (err) => {
this.logger.error(`Timer error: ${err.message}`);
this.emit('consumerHeartbeat.error', err);
});
this.logger.info(`ConsumerHeartbeat initialized for consumer ${consumer.getId()}`);
}
getLogger() {
return this.logger;
}
getPayload() {
this.logger.debug('Generating heartbeat payload');
const timestamp = Date.now();
const payload = {
timestamp,
data: {
ram: {
usage: process.memoryUsage(),
free: os.freemem(),
total: os.totalmem(),
},
cpu: cpuUsage(),
},
};
this.logger.debug(`Heartbeat payload generated with timestamp ${timestamp}`);
return payload;
}
beat() {
this.logger.debug('Starting heartbeat cycle');
const redisClient = this.redisClient.getInstance();
if (redisClient instanceof Error) {
this.logger.error(`Failed to get Redis client instance: ${redisClient.message}`);
this.emit('consumerHeartbeat.error', redisClient);
return void 0;
}
const heartbeatPayload = this.getPayload();
const consumerId = this.consumer.getId();
const timestamp = Date.now();
const heartbeatPayloadStr = JSON.stringify(heartbeatPayload);
this.logger.debug(`Setting heartbeat in Redis with TTL ${ConsumerHeartbeat.heartbeatTTL}ms`);
redisClient.set(this.keyConsumerHeartbeat, heartbeatPayloadStr, {
expire: {
mode: 'PX',
value: ConsumerHeartbeat.heartbeatTTL,
},
}, (err) => {
if (err) {
this.logger.error(`Failed to set heartbeat in Redis: ${err.message}`);
this.emit('consumerHeartbeat.error', err);
}
else {
this.logger.debug(`Heartbeat successfully set in Redis for consumer ${consumerId}`);
this.emit('consumerHeartbeat.heartbeat', consumerId, timestamp, heartbeatPayload);
this.logger.debug('Scheduling next heartbeat in 1000ms');
this.timer.setTimeout(() => this.beat(), 1000);
}
});
}
goingUp() {
this.logger.info('ConsumerHeartbeat going up');
return super.goingUp().concat([
(cb) => {
this.logger.debug('Setting up initial heartbeat');
const cleanUp = () => {
this.logger.debug('Cleaning up heartbeat listeners');
this.removeListener('consumerHeartbeat.heartbeat', onHeartbeat);
this.removeListener('consumerHeartbeat.error', onError);
};
const onError = (err) => {
this.logger.error(`Error during initial heartbeat: ${err === null || err === void 0 ? void 0 : err.message}`);
cleanUp();
cb(err);
};
const onHeartbeat = () => {
this.logger.debug('Initial heartbeat successful');
cleanUp();
cb();
};
this.once('consumerHeartbeat.heartbeat', onHeartbeat);
this.once('consumerHeartbeat.error', onError);
this.logger.debug('Initiating first heartbeat');
this.beat();
},
]);
}
goingDown() {
this.logger.info('ConsumerHeartbeat going down');
return [
(cb) => {
this.logger.debug('Resetting timer');
this.timer.reset();
const redisClient = this.redisClient.getInstance();
if (redisClient instanceof Error) {
this.logger.warn(`Could not get Redis client during shutdown: ${redisClient.message}`);
return cb();
}
this.logger.debug(`Deleting heartbeat key ${this.keyConsumerHeartbeat} from Redis`);
redisClient.del(this.keyConsumerHeartbeat, (err) => {
if (err) {
this.logger.warn(`Error deleting heartbeat key: ${err.message} (ignoring)`);
}
else {
this.logger.debug('Heartbeat key successfully deleted from Redis');
}
cb();
});
},
].concat(super.goingDown());
}
static isConsumerAlive(redisClient, consumerId, cb) {
const { keyConsumerHeartbeat } = redis_keys_js_1.redisKeys.getConsumerKeys(consumerId);
redisClient.get(keyConsumerHeartbeat, (err, heartbeat) => {
if (err)
return cb(err);
const isAlive = !!heartbeat;
cb(null, isAlive);
});
}
}
exports.ConsumerHeartbeat = ConsumerHeartbeat;
ConsumerHeartbeat.heartbeatTTL = 10 * 1000;
//# sourceMappingURL=consumer-heartbeat.js.map