UNPKG

pipeproc

Version:

Multi-process log processing for nodejs

322 lines (321 loc) 12.9 kB
"use strict"; 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); }