@minimaltech/node-infra
Version:
Minimal Technology NodeJS Infrastructure - Loopback 4 Framework
253 lines (240 loc) • 10 kB
JavaScript
"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