@5minds/processcube_engine
Version:
The ProcessCube Engine. Stores and executes BPMNs.
299 lines • 12.4 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 __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
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;
};
})();
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkerFacade = void 0;
const child_process_1 = require("child_process");
const diagnostics_channel = __importStar(require("diagnostics_channel"));
const inversify_1 = require("inversify");
const uuid = __importStar(require("uuid"));
const processcube_engine_sdk_1 = require("@5minds/processcube_engine_sdk");
const CancellationToken_1 = require("../Api/CancellationToken");
const MonitoringManager_1 = require("../Tools/MonitoringManager");
const WorkerMessages_1 = require("./WorkerMessages");
let WorkerFacade = class WorkerFacade {
logger;
pendingMessages = {};
subscriptions = {};
child;
exited;
filePath;
shutdownCallback;
resolveStartup;
startupPromise;
functionReportChannel = diagnostics_channel.channel(MonitoringManager_1.FUNCTION_REPORTS_CHANNEL_NAME);
constructor(filePath, loggerNamespace) {
this.startupPromise = new Promise((resolve) => (this.resolveStartup = resolve));
this.filePath = filePath;
this.logger = new processcube_engine_sdk_1.Logger(loggerNamespace ?? `engine:worker_facade:${filePath}`, { workerPath: filePath });
}
getChildProcess() {
return this.child;
}
async init(args) {
this.child = await this.initChild(args, this.filePath);
}
async dispose() {
this.logger.trace('Disposing worker...');
await this.gracefullyShutdownWorker();
}
subscribeToWorkerShutdown(callback) {
this.shutdownCallback = callback;
}
async callFunction(topic, ...args) {
const messageId = uuid.v4();
if (args) {
const cancellationTokenIndex = args.findIndex((arg) => arg instanceof CancellationToken_1.CancellationToken);
if (cancellationTokenIndex > -1) {
const cancellationToken = args.splice(cancellationTokenIndex)[0];
cancellationToken.onCancellation(this.sendFunctionCancellation.bind(this, messageId, 'Request got cancelled.'));
}
}
const message = {
id: messageId,
type: 'FunctionRequestMessage',
topic: topic,
payload: args,
};
return this.sendMessage(message);
}
async publishEventNotification(topic, payload) {
const message = {
id: uuid.v4(),
type: 'EventNotificationMessage',
topic: topic,
payload: payload,
};
return this.serializeAndSend(message);
}
async subscribe(topic, callback, subscribeOnce = false, ...args) {
const message = {
id: uuid.v4(),
type: 'SubscriptionRequestMessage',
topic: topic,
subscribeOnce: subscribeOnce,
payload: args,
};
await this.sendMessage(message);
this.subscriptions[message.id] = {
handleEvent: callback,
handleError: (error) => {
const deserializedError = error;
this.logger.error('Error while receiving subscription event.', { err: deserializedError });
},
subscribeOnce: subscribeOnce,
};
return message.id;
}
unsubscribe(id) {
const subscriptionContext = this.subscriptions[id];
if (!subscriptionContext) {
this.logger.warn('Unable to unsubscribe from subscription - no subscription context with equivalent id found.', { id: id });
return;
}
delete this.subscriptions[id];
const message = {
id: id,
type: 'SubscriptionCancellationMessage',
};
this.serializeAndSend(message);
}
async gracefullyShutdownWorker() {
return new Promise((resolve) => {
if (!this.child || this.exited) {
resolve();
return;
}
const message = {
id: uuid.v4(),
type: 'GracefulShutdownMessage',
};
this.child.once('exit', resolve);
this.serializeAndSend(message);
});
}
sendFunctionCancellation(id, reason) {
const message = {
id: id,
type: 'FunctionCancellationMessage',
reason: reason,
};
this.serializeAndSend(message);
}
async sendMessage(message) {
return new Promise((resolve, reject) => {
this.pendingMessages[message.id] = {
resolve: resolve,
reject: reject,
};
this.serializeAndSend(message);
});
}
serializeAndSend(message) {
const serializedMessage = (0, WorkerMessages_1.serializeWorkerMessage)(message);
this.child.send(serializedMessage);
}
async initChild(args, filePath) {
const childEnv = {
...process.env,
startupArgs: args ? JSON.stringify(args) : undefined,
isFork: 'true',
};
const child = (0, child_process_1.fork)(filePath, { env: childEnv });
child.on('message', this.receiveMessage.bind(this));
child.on('exit', this.handleWorkerExit.bind(this));
child.on('error', this.handleWorkerError.bind(this));
child.on('messageerror', this.handleWorkerMessageError.bind(this));
await this.startupPromise;
return child;
}
receiveMessage(message) {
const deserializedMessage = (0, WorkerMessages_1.deserializeWorkerMessage)(message);
if (!deserializedMessage || !deserializedMessage.id || !deserializedMessage.type) {
this.logger.warn('Unable to dispatch runtime message - message is either undefined, has no type or no id.', { workerMessage: deserializedMessage });
return;
}
switch (deserializedMessage.type) {
case 'FunctionResponseMessage':
return this.handleFunctionResponseMessage(deserializedMessage);
case 'SubscriptionResponseMessage':
return this.handleSubscriptionResponseMessage(deserializedMessage);
case 'SubscriptionEventMessage':
return this.handleSubscriptionEventMessage(deserializedMessage);
case 'StartupFinishedMessage':
return this.handleStartupFinishedMessage();
case 'SystemMessage':
return this.handleSystemMessage(deserializedMessage);
case 'MetricsMessage':
return this.handleMetricsMessage(deserializedMessage);
default:
this.logger.warn('Unable to dispatch message - message type is unknown.', { workerMessage: deserializedMessage });
}
}
handleFunctionResponseMessage(message) {
const pendingMessage = this.pendingMessages[message.id];
if (!pendingMessage) {
this.logger.trace('Unable to dispatch message - no pending message with equivalent id found.', { workerMessage: message });
return;
}
delete this.pendingMessages[message.id];
if (message.error) {
const deserializedError = message.error;
return pendingMessage.reject(deserializedError);
}
return pendingMessage.resolve(message.payload);
}
handleSubscriptionResponseMessage(message) {
const pendingMessage = this.pendingMessages[message.id];
if (!pendingMessage) {
this.logger.trace('Unable to dispatch message - no pending message with equivalent id found.', { workerMessage: message });
return;
}
delete this.pendingMessages[message.id];
if (message.error) {
const deserializedError = message.error;
return pendingMessage.reject(deserializedError);
}
return pendingMessage.resolve();
}
handleSubscriptionEventMessage(message) {
const subscriptionContext = this.subscriptions[message.id];
if (!subscriptionContext) {
this.logger.trace('Unable to dispatch message - no subscription context with equivalent id found.', { workerMessage: message });
return;
}
if (subscriptionContext.subscribeOnce) {
delete this.subscriptions[message.id];
}
if (message.error) {
return subscriptionContext.handleError(message.error);
}
return subscriptionContext.handleEvent(message.payload);
}
handleStartupFinishedMessage() {
this.resolveStartup();
}
handleSystemMessage(message) {
// content of system messages will be forwarded if possible. used in embedding scenarios
if (typeof process.send === 'function') {
process.send(message.content);
}
}
handleMetricsMessage(message) {
if (message.typeOfMetrics == 'FunctionReport') {
this.functionReportChannel.publish(message.payload);
}
}
async handleWorkerExit(exitCode, signal) {
this.exited = true;
if (exitCode != 0) {
this.logger.error(`Worker stopped with exit code \`${exitCode}\`. Exiting.`, { exitCode: exitCode, signal: signal });
if (this.shutdownCallback) {
await this.shutdownCallback(exitCode, signal);
}
else {
process.exit(exitCode);
}
}
else {
this.logger.trace(`Worker stopped with exit code \`${exitCode}\`. Exiting.`, { exitCode: exitCode, signal: signal });
}
}
handleWorkerError(error) {
this.logger.error('Worker encountered an error.', { err: error });
}
handleWorkerMessageError(error) {
this.logger.error('Worker encountered a message error.', { err: error });
}
};
exports.WorkerFacade = WorkerFacade;
exports.WorkerFacade = WorkerFacade = __decorate([
(0, inversify_1.injectable)(),
__param(0, (0, inversify_1.unmanaged)()),
__param(1, (0, inversify_1.unmanaged)()),
__metadata("design:paramtypes", [String, String])
], WorkerFacade);
//# sourceMappingURL=WorkerFacade.js.map