UNPKG

reliable-zeromq

Version:

A collection of reliable zeromq messaging constructs

169 lines 15.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (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 __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ZMQSubscriber = void 0; const zmq = __importStar(require("zeromq")); const Errors_1 = require("../Errors"); const JSONBigInt_1 = __importDefault(require("../Utils/JSONBigInt")); const ZMQPublisher_1 = require("../ZMQPublisher"); const ZMQRequest_1 = require("../ZMQRequest"); const TopicEntry_1 = __importDefault(require("./TopicEntry")); class ZMQSubscriber { constructor(aErrorHandlers) { this.mEndpoints = new Map(); this.mSubscriptions = new Map(); this.mTokenId = 0; this.mErrorHandlers = aErrorHandlers ?? Errors_1.DEFAULT_ZMQ_SUBSCRIBER_ERROR_HANDLERS; } get SubscriptionId() { return ++this.mTokenId; } static CloseEndpoint(lEndpoint) { lEndpoint.Subscriber.linger = 0; lEndpoint.Subscriber.close(); lEndpoint.Requester.Close(); } async AddSubscriptionEndpoint(aEndpoint) { const lSubSocket = new zmq.Subscriber; lSubSocket.connect(aEndpoint.PublisherAddress); const lSocketEntry = { Subscriber: lSubSocket, Requester: new ZMQRequest_1.ZMQRequest(aEndpoint.RequestAddress), TopicEntries: new Map(), }; this.mEndpoints.set(aEndpoint.PublisherAddress, lSocketEntry); for await (const aBuffers of lSubSocket) { if (this.mEndpoints.has(aEndpoint.PublisherAddress)) { this.ParseNewMessage(aBuffers, aEndpoint); } } } EmitCacheError(aEndpoint, aTopic, aMessageId) { this.mErrorHandlers.CacheError({ Endpoint: aEndpoint, Topic: aTopic, MessageNonce: aMessageId, }); } EmitDroppedMessageWarn(aTopic, aNonces) { this.mErrorHandlers.DroppedMessageWarn({ Topic: aTopic, Nonces: aNonces, }); } InitTopicEntry(aEndpoint, aTopic, aSubscriptionId, aCallback) { const lTopicEntry = new TopicEntry_1.default(aEndpoint, aTopic, this.RecoverMissingMessages.bind(this)); lTopicEntry.Callbacks.set(aSubscriptionId, aCallback); return lTopicEntry; } ParseNewMessage(aBuffers, aEndpoint) { const lEncodedMessage = aBuffers.map((aBuffer) => aBuffer.toString()); const [lTopic, lType, lReceivedNonce, lMessage] = [ lEncodedMessage[ZMQPublisher_1.EPublishMessage.Topic], lEncodedMessage[ZMQPublisher_1.EPublishMessage.MessageType], Number(lEncodedMessage[ZMQPublisher_1.EPublishMessage.Nonce]), lEncodedMessage[ZMQPublisher_1.EPublishMessage.Message], ]; const lTopicEntry = this.mEndpoints.get(aEndpoint.PublisherAddress).TopicEntries.get(lTopic); if (lType === ZMQPublisher_1.EMessageType.HEARTBEAT) { lTopicEntry.ProcessHeartbeatMessage(lReceivedNonce); } else if (lType === ZMQPublisher_1.EMessageType.PUBLISH) { lTopicEntry.ProcessPublishMessage(lReceivedNonce, lMessage); } } PruneIfEmpty(aInternalSubscription) { const lEndpoint = this.mEndpoints.get(aInternalSubscription.Endpoint); const lTopicEntry = lEndpoint.TopicEntries.get(aInternalSubscription.Topic); if (lTopicEntry.Callbacks.size === 0) { lEndpoint.Subscriber.unsubscribe(aInternalSubscription.Topic); lEndpoint.TopicEntries.delete(aInternalSubscription.Topic); if (lEndpoint.TopicEntries.size === 0) { ZMQSubscriber.CloseEndpoint(lEndpoint); this.mEndpoints.delete(aInternalSubscription.Endpoint); } } } async RecoverMissingMessages(aEndpoint, aTopic, aMessageIds) { this.EmitDroppedMessageWarn(aTopic, aMessageIds); const lFormattedRequest = [aTopic, ...aMessageIds]; // PERF: Array manipulation const lEndpointEntry = this.mEndpoints.get(aEndpoint.PublisherAddress); const lMissingMessages = await lEndpointEntry.Requester.Send(JSONBigInt_1.default.Stringify(lFormattedRequest)); if (lMissingMessages.ResponseType === ZMQRequest_1.ERequestResponse.SUCCESS) { const lParsedMessages = JSONBigInt_1.default.Parse(lMissingMessages.Response); for (let i = 0; i < lParsedMessages.length; ++i) { const lParsedMessage = lParsedMessages[i]; if (lParsedMessage.length !== 1) { lEndpointEntry.TopicEntries.get(aTopic)?.ProcessPublishMessage(lParsedMessage[ZMQPublisher_1.EPublishMessage.Nonce], lParsedMessage[ZMQPublisher_1.EPublishMessage.Message]); } else { this.EmitCacheError(aEndpoint, aTopic, aMessageIds[i]); } } } else { for (let i = 0; i < aMessageIds.length; ++i) { this.EmitCacheError(aEndpoint, aTopic, aMessageIds[i]); } } } Close() { this.mEndpoints.forEach((aEndpoint) => { ZMQSubscriber.CloseEndpoint(aEndpoint); }); this.mEndpoints.clear(); this.mSubscriptions.clear(); } Subscribe(aEndpoint, aTopic, aCallback) { let lEndpoint = this.mEndpoints.get(aEndpoint.PublisherAddress); if (!lEndpoint) { this.AddSubscriptionEndpoint(aEndpoint); lEndpoint = this.mEndpoints.get(aEndpoint.PublisherAddress); } const lSubscriptionId = this.SubscriptionId; const lExistingTopic = lEndpoint.TopicEntries.get(aTopic); if (lExistingTopic) { lExistingTopic.Callbacks.set(lSubscriptionId, aCallback); } else { const lTopicEntry = this.InitTopicEntry(aEndpoint, aTopic, lSubscriptionId, aCallback); lEndpoint.Subscriber.subscribe(aTopic); lEndpoint.TopicEntries.set(aTopic, lTopicEntry); } this.mSubscriptions.set(lSubscriptionId, { Endpoint: aEndpoint.PublisherAddress, Topic: aTopic }); return lSubscriptionId; } Unsubscribe(aSubscriptionId) { const lInternalSubscription = this.mSubscriptions.get(aSubscriptionId); if (lInternalSubscription) { const lEndpoint = this.mEndpoints.get(lInternalSubscription.Endpoint); const lTopicEntry = lEndpoint.TopicEntries.get(lInternalSubscription.Topic); lTopicEntry.Callbacks.delete(aSubscriptionId); this.mSubscriptions.delete(aSubscriptionId); this.PruneIfEmpty(lInternalSubscription); } } } exports.ZMQSubscriber = ZMQSubscriber; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiWk1RU3Vic2NyaWJlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL1NyYy9aTVFTdWJzY3JpYmVyL1pNUVN1YnNjcmliZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDRDQUE4QjtBQUM5QixzQ0FBK0Y7QUFDL0YscUVBQTZDO0FBQzdDLGtEQU95QjtBQUN6Qiw4Q0FBK0U7QUFDL0UsOERBQXNDO0FBdUJ0QyxNQUFhLGFBQWE7SUFPdEIsWUFBbUIsY0FBNEM7UUFMOUMsZUFBVSxHQUFnQyxJQUFJLEdBQUcsRUFBMEIsQ0FBQztRQUVyRixtQkFBYyxHQUF1QyxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQy9ELGFBQVEsR0FBVyxDQUFDLENBQUM7UUFJekIsSUFBSSxDQUFDLGNBQWMsR0FBRyxjQUFjLElBQUksOENBQXFDLENBQUM7SUFDbEYsQ0FBQztJQUVELElBQVksY0FBYztRQUV0QixPQUFPLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUMzQixDQUFDO0lBRU8sTUFBTSxDQUFDLGFBQWEsQ0FBQyxTQUF5QjtRQUVsRCxTQUFTLENBQUMsVUFBVSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7UUFDaEMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUM3QixTQUFTLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ2hDLENBQUM7SUFFTyxLQUFLLENBQUMsdUJBQXVCLENBQUMsU0FBaUM7UUFFbkUsTUFBTSxVQUFVLEdBQW1CLElBQUksR0FBRyxDQUFDLFVBQVUsQ0FBQztRQUN0RCxVQUFVLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBRS9DLE1BQU0sWUFBWSxHQUFtQjtZQUNqQyxVQUFVLEVBQUUsVUFBVTtZQUN0QixTQUFTLEVBQUUsSUFBSSx1QkFBVSxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUM7WUFDbkQs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