pipeproc
Version:
Multi-process log processing for nodejs
322 lines (321 loc) • 12.9 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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const messages_1 = require("../../common/messages");
const client_1 = require("../../client");
const async_1 = require("async");
const debug_1 = __importDefault(require("debug"));
const backoff_1 = require("backoff");
const d = debug_1.default(`pipeproc:worker:${process.pid}`);
const pipeProcClient = client_1.PipeProc();
function sendMessageToNode(msg, cb) {
if (process && typeof process.send === "function") {
process.send(msg, cb);
}
}
let procList = [];
let systemProcList = [];
let totalInvocations = 0;
let readyToStop = 0;
process.on("message", function (e) {
if (e.type === "worker_init") {
pipeProcClient.connect({ isWorker: true, socket: e.data.address, tls: e.data.tls })
.then(function (status) {
d("worker concurrency:", e.data.workerConcurrency);
d("worker restartAfter:", e.data.workerRestartAfter);
for (let i = 0; i < e.data.workerConcurrency; i += 1) {
const strategy = new backoff_1.ExponentialStrategy({
randomisationFactor: 0.5,
initialDelay: 10,
maxDelay: 3000,
factor: 2
});
async_1.forever(function (next) {
if (e.data.workerRestartAfter && totalInvocations >= e.data.workerRestartAfter) {
return next(new Error("stop"));
}
pipeProcClient.availableProc(procList)
.then(function (result) {
if (result && result.procName && result.log) {
const myProc = procList.find(pr => pr.name === result.procName);
procExec(myProc, result.log, function (err) {
if (e.data.workerRestartAfter) {
totalInvocations += 1;
}
if (err) {
d(err);
setTimeout(next, strategy.next());
}
else {
strategy.reset();
setImmediate(next);
}
});
}
else {
d("no results");
setTimeout(next, strategy.next());
}
})
.catch(function (err) {
d(err);
setTimeout(next, strategy.next());
});
}, function (signal) {
return __awaiter(this, void 0, void 0, function* () {
if (signal && signal.message === "stop") {
readyToStop += 1;
}
if (readyToStop === e.data.workerConcurrency) {
d("restarting after:", totalInvocations, "invocations");
yield pipeProcClient.shutdown();
process.removeAllListeners();
process.exit(0);
}
});
});
}
sendMessageToNode(messages_1.prepareMessage({
type: "worker_connected",
msgKey: e.msgKey,
data: {
status: status
}
}));
})
.catch(function (err) {
sendMessageToNode(messages_1.prepareMessage({
type: "worker_connection_failure",
msgKey: e.msgKey,
errStatus: err.message
}));
});
}
});
process.on("message", function (e) {
if (e.type === "register_system_procs") {
d("registering...");
procList = procList.concat(e.data.procs);
systemProcList = systemProcList.concat(e.data.systemProcs);
sendMessageToNode(messages_1.prepareMessage({
type: "register_system_proc_ok",
msgKey: e.msgKey,
data: {
status: "ok"
}
}));
}
});
function procExec(myProc, myLog, callback) {
d("executing", myProc.name);
const mySystemProc = systemProcList.find(sp => sp.name === myProc.name);
if (!mySystemProc) {
return callback(new Error("invalid_procs_passed"));
}
let processorFn;
try {
if (mySystemProc.externalProcessor) {
//tslint:disable non-literal-require
processorFn = require(mySystemProc.externalProcessor);
if (typeof processorFn !== "function") {
processorFn = processorFn.default;
}
//tslint:enable
}
if (mySystemProc.inlineProcessor) {
//tslint:disable no-function-constructor-with-string-args
processorFn = (new Function("log", "done", `
return (${mySystemProc.inlineProcessor}).call(null, log, done);
`));
//tslint:enable
}
}
catch (e) {
return callback(e);
}
let myCommitLog;
let shouldCommit = false;
let processorErr;
let ackCommitErr;
async_1.series([
function (cb) {
try {
const myProcessorPromise = processorFn(myLog, function (err, data) {
if (err) {
processorErr = err;
}
else if (data && ((Array.isArray(mySystemProc.to) && mySystemProc.to.length > 0) ||
mySystemProc.to)) {
if (Array.isArray(mySystemProc.to) && mySystemProc.to.length > 0) {
myCommitLog = [];
mySystemProc.to.forEach(topic => {
if (Array.isArray(data) && data.length > 0) {
shouldCommit = true;
data.forEach(dataBody => {
myCommitLog.push({
topic: topic,
body: dataBody
});
});
}
else {
shouldCommit = true;
myCommitLog.push({
topic: topic,
body: data
});
}
});
}
else {
if (Array.isArray(data) && data.length > 0) {
myCommitLog = [];
shouldCommit = true;
data.forEach(dataBody => {
myCommitLog.push({
topic: mySystemProc.to,
body: dataBody
});
});
}
else {
shouldCommit = true;
myCommitLog = {
topic: mySystemProc.to,
body: data
};
}
}
}
cb();
});
if (myProcessorPromise instanceof Promise) {
myProcessorPromise.then(function (data) {
if (!data)
return cb();
if (Array.isArray(mySystemProc.to) && mySystemProc.to.length > 0) {
myCommitLog = [];
mySystemProc.to.forEach(topic => {
if (Array.isArray(data) && data.length > 0) {
shouldCommit = true;
data.forEach(dataBody => {
myCommitLog.push({
topic: topic,
body: dataBody
});
});
}
else {
shouldCommit = true;
myCommitLog.push({
topic: topic,
body: data
});
}
});
}
else {
if (Array.isArray(data) && data.length > 0) {
myCommitLog = [];
shouldCommit = true;
data.forEach(dataBody => {
myCommitLog.push({
topic: mySystemProc.to,
body: dataBody
});
});
}
else {
shouldCommit = true;
myCommitLog = {
topic: mySystemProc.to,
body: data
};
}
}
cb();
}).catch(function (err) {
processorErr = err;
cb();
});
}
}
catch (e) {
cb(e);
}
},
function (cb) {
if (processorErr) {
pipeProcClient.reclaimProc(myProc.name)
.then(function () {
cb();
})
.catch(cb);
}
else {
setImmediate(cb);
}
},
function (cb) {
if (processorErr) {
setImmediate(cb);
}
else {
if (shouldCommit) {
pipeProcClient.ackCommit(myProc.name, myCommitLog)
.then(function () {
cb();
})
.catch(function (err) {
ackCommitErr = err;
cb();
});
}
else {
pipeProcClient.ack(myProc.name)
.then(function () {
cb();
})
.catch(function (err) {
ackCommitErr = err;
cb();
});
}
}
},
function (cb) {
if (ackCommitErr) {
pipeProcClient.reclaimProc(myProc.name)
.then(function () {
cb();
})
.catch(cb);
}
else {
cb();
}
},
function (cb) {
if (processorErr) {
cb(processorErr);
}
else if (ackCommitErr) {
cb(ackCommitErr);
}
else {
cb();
}
}
], callback);
}
;