event-driven
Version:
makes building event-driven-architecture easy
113 lines (112 loc) • 5.78 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Subscriber = void 0;
const models_1 = require("./backend/models");
const mongoose_1 = require("mongoose");
const nats_1 = require("nats");
const _1 = require(".");
const sc = nats_1.StringCodec();
const Subscriber = ({ clientGroup, js, batch, expires, }) => {
return ({ subject, eventHandlers, }) => __awaiter(void 0, void 0, void 0, function* () {
for (let i = 0; i < eventHandlers.length; i++) {
const durableName = `${clientGroup}=${subject.replace(/\./g, '-')}=0=${i}`;
try {
const opts = nats_1.consumerOpts();
opts.ackWait(30000);
opts.manualAck();
opts.durable(durableName);
opts.maxAckPending(500);
const psub = yield js.pullSubscribe(subject, opts);
(() => __awaiter(void 0, void 0, void 0, function* () {
var e_1, _a;
try {
for (var psub_1 = __asyncValues(psub), psub_1_1; psub_1_1 = yield psub_1.next(), !psub_1_1.done;) {
const m = psub_1_1.value;
processEvent(m, _1._maxRetryCount, js, clientGroup, eventHandlers[i], durableName, subject, _1._showError, _1._showProcessTimeWarning);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (psub_1_1 && !psub_1_1.done && (_a = psub_1.return)) yield _a.call(psub_1);
}
finally { if (e_1) throw e_1.error; }
}
}))();
psub.pull({ batch, expires });
setTimeout(() => {
setInterval(() => {
psub.pull({ batch, expires });
}, expires);
}, Math.random() * expires);
}
catch (err) {
//@ts-ignore
console.error(err.message, `clientGroup: ${clientGroup}, index: ${i}, subject: ${subject}`);
throw err;
}
}
});
};
exports.Subscriber = Subscriber;
const processEvent = (jsMsg, maxRetryCount, js, clientGroup, eventHandler, durableName, subject, showError, showProcessTimeWarning) => __awaiter(void 0, void 0, void 0, function* () {
const headers = jsMsg.headers;
const eventId = headers === null || headers === void 0 ? void 0 : headers.get('eventId');
const parentId = new mongoose_1.Types.ObjectId(eventId);
const msgData = JSON.parse(sc.decode(jsMsg.data));
const retryingDurableName = headers === null || headers === void 0 ? void 0 : headers.get('retryingDurableName');
if (retryingDurableName && retryingDurableName !== durableName) {
jsMsg.ack();
return;
}
const fail = getFail(jsMsg, parentId, durableName, clientGroup, subject, maxRetryCount, showError);
try {
let start = new Date();
yield eventHandler({
input: msgData,
config: { js, clientGroup, parentIds: [parentId], durableName },
});
let diff = new Date().getTime() - start.getTime();
if (showProcessTimeWarning && showProcessTimeWarning < diff)
console.log('\x1b[33m%s\x1b[0m', `[WARNING-TIME] ${diff / 1000}s, eventId:${parentId}, durableName: ${durableName}`);
jsMsg.ack();
}
catch (error) {
yield fail(error);
}
});
const getFail = (jsMsg, parentId, durableName, clientGroup, subject, maxRetryCount, showError) => (error) => __awaiter(void 0, void 0, void 0, function* () {
const eventError = yield models_1.createEventError({
eventId: parentId,
durableName,
clientGroup,
subject,
error,
});
const { errorCount } = eventError;
console.error('\x1b[31m%s\x1b[0m', `Failed ${errorCount} times, durableName: ${durableName}, errMsg: ${error.message}, eventId:${parentId}`);
if (showError)
console.error('\x1b[31m%s\x1b[0m', `${durableName}`, error.stack);
if (errorCount >= maxRetryCount) {
console.error('\x1b[41m%s\x1b[0m', `Error added to dead letter queue`);
jsMsg.ack();
}
else
jsMsg.nak((errorCount - 1) * 5000);
});