UNPKG

@minimaltech/node-infra

Version:

Minimal Technology NodeJS Infrastructure - Loopback 4 Framework

253 lines (240 loc) 10 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 }; }; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.QueueHelper = exports.QueueStatuses = void 0; const base_helper_1 = require("../../base/base.helper"); const isEmpty_1 = __importDefault(require("lodash/isEmpty")); // -------------------------------------------------------- class QueueStatuses { static isValid(scheme) { return this.SCHEME_SET.has(scheme); } } exports.QueueStatuses = QueueStatuses; _a = QueueStatuses; QueueStatuses.WAITING = '000_WAITING'; QueueStatuses.PROCESSING = '100_PROCESSING'; QueueStatuses.LOCKED = '200_LOCKED'; QueueStatuses.SETTLED = '300_SETTLED'; QueueStatuses.SCHEME_SET = new Set([_a.WAITING, _a.PROCESSING, _a.LOCKED, _a.SETTLED]); // -------------------------------------------------------- class QueueHelper extends base_helper_1.BaseHelper { constructor(opts) { var _b; super({ scope: `${QueueHelper.name}_${opts.identifier}`, identifier: opts.identifier }); this.autoDispatch = true; this.state = QueueStatuses.WAITING; this.identifier = opts.identifier; this.storage = []; this.processingEvents = new Set([]); this.generator = this._messageListener(); this.totalEvent = 0; this.autoDispatch = (_b = opts.autoDispatch) !== null && _b !== void 0 ? _b : true; this.state = QueueStatuses.WAITING; this.isSettleRequested = false; this.onMessage = opts.onMessage; this.onDataEnqueue = opts.onDataEnqueue; this.onDataDequeue = opts.onDataDequeue; this.onStateChange = opts.onStateChange; } handleMessage() { return __awaiter(this, void 0, void 0, function* () { var _b, _c, _d, _e; const current = this.getElementAt(0); if (!current) { this.logger.warn('[handleMessage] current: %j | Invalid current message to handle!', current); return; } const { isLocked, payload } = current; if (isLocked) { this.logger.info('[handle] Skip LOCKED message | Payload: %j', payload); return; } if (this.state !== QueueStatuses.LOCKED && this.state !== QueueStatuses.SETTLED) { const snap = this.state; this.state = QueueStatuses.PROCESSING; yield ((_b = this.onStateChange) === null || _b === void 0 ? void 0 : _b.call(this, { identifier: this.identifier, from: snap, to: this.state })); } this.getElementAt(0).isLocked = true; this.processingEvents.add(this.getElementAt(0)); yield ((_c = this.onMessage) === null || _c === void 0 ? void 0 : _c.call(this, { identifier: this.identifier, queueElement: this.getElementAt(0) })); const doneElement = this.dequeue(); if (doneElement) { this.processingEvents.delete(doneElement); } if (this.state !== QueueStatuses.LOCKED && this.state !== QueueStatuses.SETTLED) { const snap = this.state; this.state = QueueStatuses.WAITING; yield ((_d = this.onStateChange) === null || _d === void 0 ? void 0 : _d.call(this, { identifier: this.identifier, from: snap, to: this.state })); } if (!this.storage.length) { if (this.isSettleRequested) { const snap = this.state; this.state = QueueStatuses.SETTLED; yield ((_e = this.onStateChange) === null || _e === void 0 ? void 0 : _e.call(this, { identifier: this.identifier, from: snap, to: this.state })); } return; } this.nextMessage(); }); } *_messageListener() { if (!this.onMessage) { this.logger.warn('[_messageListener] Queue has no onMessage listener | Skip initializing message iterator!'); return; } while (true) { yield this.handleMessage(); } } nextMessage() { if (this.state !== QueueStatuses.WAITING) { this.logger.warn('[nextMessage] SKIP request next message | Invalid queue state to request next message | currentState: %s', this.state); return; } this.generator.next(); } enqueue(payload) { return __awaiter(this, void 0, void 0, function* () { var _b; if (this.isSettleRequested || this.state === QueueStatuses.SETTLED) { this.logger.error('[enqueue] isSettled: %s | currentState: %s | Queue was SETTLED | No more element acceptable', this.isSettleRequested, this.state); return; } if (!this.storage) { this.storage = []; } if (!payload) { return; } const queueElement = { isLocked: false, payload }; this.storage.push(queueElement); this.totalEvent++; yield ((_b = this.onDataEnqueue) === null || _b === void 0 ? void 0 : _b.call(this, { identifier: this.identifier, queueElement: queueElement })); if (this.autoDispatch) { this.nextMessage(); } }); } dequeue() { var _b; const value = this.storage.shift(); if (value && !(0, isEmpty_1.default)(value)) { (_b = this.onDataDequeue) === null || _b === void 0 ? void 0 : _b.call(this, { identifier: this.identifier, queueElement: value }); } return value; } lock() { var _b; if (this.state >= QueueStatuses.LOCKED) { this.logger.error('[lock] isSettled | currentState: %s | Invalid queue state to request lock queue!', this.isSettleRequested, this.state); return; } const snap = this.state; this.state = QueueStatuses.LOCKED; (_b = this.onStateChange) === null || _b === void 0 ? void 0 : _b.call(this, { identifier: this.identifier, from: snap, to: this.state }); } unlock(opts) { var _b; if (this.state > QueueStatuses.LOCKED) { this.logger.error('[unlock] isSettled | currentState: %s | Invalid queue state to request unlock queue!', this.isSettleRequested, this.state); return; } const { shouldProcessNextElement = true } = opts; const snap = this.state; this.state = QueueStatuses.WAITING; (_b = this.onStateChange) === null || _b === void 0 ? void 0 : _b.call(this, { identifier: this.identifier, from: snap, to: this.state }); if (!shouldProcessNextElement) { return; } this.nextMessage(); } settle() { var _b; this.isSettleRequested = true; if (this.state !== QueueStatuses.PROCESSING) { const snap = this.state; this.state = QueueStatuses.SETTLED; (_b = this.onStateChange) === null || _b === void 0 ? void 0 : _b.call(this, { identifier: this.identifier, from: snap, to: this.state }); } } isSettled() { return this.state === QueueStatuses.SETTLED && !this.storage.length; } close() { this.settle(); this.generator.return({ state: this.state, totalEvent: this.totalEvent, }); } getElementAt(position) { return this.storage[position]; } getState() { return this.state; } getTotalEvent() { return this.totalEvent; } getProcessingEvents() { return this.processingEvents; } } exports.QueueHelper = QueueHelper; // -------------------------------------------------------- /* export class MultiQueueHelper<ElementType> { public storage: Record<string, QueueHelper<ElementType>>; private onDataEnqueue?: (identifier: string, payload: ElementType) => void; private onDataDequeue?: (identifier: string, payload: ElementType) => void; constructor(opts: IQueueCallback<ElementType>) { const { onDataEnqueue, onDataDequeue } = opts; this.onDataEnqueue = onDataEnqueue; this.onDataDequeue = onDataDequeue; this.storage = {}; } enqueue(identifier: string, value: ElementType) { if (!this.storage[identifier]) { this.storage[identifier] = new QueueHelper({ identifier }); } this.storage[identifier].enqueue(value); if (value && !isEmpty(value)) { this.onDataEnqueue?.(identifier, value); } } dequeue(identifier: string): ElementType | undefined { const value = this.storage[identifier]?.dequeue(); if (value && !isEmpty(value)) { this.onDataDequeue?.(identifier, value); } return value; } getElementAt(identifier: string, position = 0) { return this.storage[identifier].getElementAt(position); } getCurrentData(identifier: string) { return this.storage[identifier]?.getElementAt(0); } getQueue(identifier: string) { return this.storage[identifier]; } removeQueue(queue: string) { this.storage = omit(this.storage, [queue]); } mapData() { return this.storage; } } */ //# sourceMappingURL=queue.helper.js.map