@uns-kit/core 3.0.37 → 3.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATIONS.md +21 -0
- package/README.md +11 -0
- package/dist/runtime/input-queue-boundary.d.ts +15 -0
- package/dist/runtime/input-queue-boundary.d.ts.map +1 -0
- package/dist/runtime/input-queue-boundary.js +33 -0
- package/dist/runtime/input-queue-boundary.js.map +1 -0
- package/dist/runtime/mqtt-handover-state.d.ts +4 -1
- package/dist/runtime/mqtt-handover-state.d.ts.map +1 -1
- package/dist/runtime/mqtt-handover-state.js +5 -3
- package/dist/runtime/mqtt-handover-state.js.map +1 -1
- package/dist/uns/handover-manager.d.ts +3 -0
- package/dist/uns/handover-manager.d.ts.map +1 -1
- package/dist/uns/handover-manager.js +61 -11
- package/dist/uns/handover-manager.js.map +1 -1
- package/dist/uns/uns-interfaces.d.ts +1 -0
- package/dist/uns/uns-interfaces.d.ts.map +1 -1
- package/dist/uns/uns-interfaces.js.map +1 -1
- package/dist/uns-mqtt/mqtt-proxy.d.ts +1 -0
- package/dist/uns-mqtt/mqtt-proxy.d.ts.map +1 -1
- package/dist/uns-mqtt/mqtt-proxy.js +1 -0
- package/dist/uns-mqtt/mqtt-proxy.js.map +1 -1
- package/dist/uns-mqtt/mqtt-worker.d.ts.map +1 -1
- package/dist/uns-mqtt/mqtt-worker.js +11 -0
- package/dist/uns-mqtt/mqtt-worker.js.map +1 -1
- package/dist/uns-mqtt/subscription-readiness.d.ts +2 -0
- package/dist/uns-mqtt/subscription-readiness.d.ts.map +1 -1
- package/dist/uns-mqtt/subscription-readiness.js +5 -1
- package/dist/uns-mqtt/subscription-readiness.js.map +1 -1
- package/dist/uns-mqtt/throttled-queue.d.ts +5 -8
- package/dist/uns-mqtt/throttled-queue.d.ts.map +1 -1
- package/dist/uns-mqtt/throttled-queue.js +25 -1
- package/dist/uns-mqtt/throttled-queue.js.map +1 -1
- package/dist/uns-mqtt/uns-mqtt-proxy.d.ts +10 -8
- package/dist/uns-mqtt/uns-mqtt-proxy.d.ts.map +1 -1
- package/dist/uns-mqtt/uns-mqtt-proxy.js +43 -3
- package/dist/uns-mqtt/uns-mqtt-proxy.js.map +1 -1
- package/package.json +1 -1
|
@@ -43,7 +43,11 @@ export class SubscriptionReadiness {
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
ready() {
|
|
46
|
-
return this.
|
|
46
|
+
return this.filters.size > 0 && this.handoverReady();
|
|
47
|
+
}
|
|
48
|
+
/** A connected output-only proxy has an empty input boundary, not a pending SUBACK. */
|
|
49
|
+
handoverReady() {
|
|
50
|
+
return this.connected && this.pending.size === 0
|
|
47
51
|
&& [...this.filters.values()].every(filter => filter.acknowledged);
|
|
48
52
|
}
|
|
49
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscription-readiness.js","sourceRoot":"","sources":["../../src/uns-mqtt/subscription-readiness.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"subscription-readiness.js","sourceRoot":"","sources":["../../src/uns-mqtt/subscription-readiness.ts"],"names":[],"mappings":"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","sourcesContent":["/** Transport evidence only. No extra subscriptions, topics or credentials are published. */\nexport class SubscriptionReadiness {\n private connected = false;\n private sequence = 0;\n private filters = new Map<string, {revision: number; acknowledged: boolean}>();\n private pending = new Map<number, {remove: boolean; filters: Array<{topic: string; revision: number}>}>();\n\n connection(connected: boolean): void {\n this.connected = connected;\n if (!connected) { this.filters.clear(); this.pending.clear(); }\n }\n\n sent(packet: {cmd: string; messageId?: number; subscriptions?: Array<{topic: string}>; unsubscriptions?: string[]}): void {\n if (!['subscribe', 'unsubscribe'].includes(packet.cmd) || !Number.isInteger(packet.messageId)) return;\n const topics = packet.cmd === 'subscribe' ? packet.subscriptions?.map(s => s.topic) : packet.unsubscriptions;\n if (!topics?.length) return;\n const filters = topics.map(topic => {\n const revision = ++this.sequence;\n this.filters.set(topic, {revision, acknowledged: false});\n return {topic, revision};\n });\n this.pending.set(packet.messageId!, {remove: packet.cmd === 'unsubscribe', filters});\n }\n\n received(packet: {cmd: string; messageId?: number; granted?: number[]}): void {\n if (!['suback', 'unsuback'].includes(packet.cmd) || packet.messageId === undefined) return;\n const pending = this.pending.get(packet.messageId);\n if (!pending || pending.remove !== (packet.cmd === 'unsuback')) return;\n this.pending.delete(packet.messageId);\n pending.filters.forEach(({topic, revision}, index) => {\n if (this.filters.get(topic)?.revision !== revision) return;\n const grant = packet.granted?.[index];\n const accepted = pending.remove ? grant === undefined || grant === 0 || grant === 17 : grant === 0 || grant === 1 || grant === 2;\n if (pending.remove && accepted) this.filters.delete(topic);\n else this.filters.set(topic, {revision, acknowledged: accepted && !pending.remove});\n });\n }\n\n ready(): boolean {\n return this.filters.size > 0 && this.handoverReady();\n }\n\n /** A connected output-only proxy has an empty input boundary, not a pending SUBACK. */\n handoverReady(): boolean {\n return this.connected && this.pending.size === 0\n && [...this.filters.values()].every(filter => filter.acknowledged);\n }\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { IClientPublishOptions } from "mqtt";
|
|
2
|
+
import { type InputQueueCursor } from "../runtime/input-queue-boundary.js";
|
|
2
3
|
/**
|
|
3
4
|
* Abstract base class that encapsulates common functionality for managing
|
|
4
5
|
* a throttled in‑memory queue with an optional disk persistence.
|
|
@@ -162,19 +163,15 @@ export declare class ThrottledSubscriber extends ThrottledQueue<SubscriberQueueI
|
|
|
162
163
|
/**
|
|
163
164
|
* Switch the subscriber to a passive state and return a snapshot.
|
|
164
165
|
*/
|
|
165
|
-
becomePassive(n: number, strict?: boolean): Promise<
|
|
166
|
-
referenceHash: string;
|
|
167
|
-
batchSize: number;
|
|
168
|
-
}>;
|
|
166
|
+
becomePassive(n: number, strict?: boolean): Promise<InputQueueCursor>;
|
|
169
167
|
/**
|
|
170
168
|
* Switch the subscriber to an active state and resume processing.
|
|
171
169
|
*/
|
|
172
170
|
private preparedResume;
|
|
171
|
+
getInputPreview(): string[];
|
|
172
|
+
canResumeInput(preview: unknown): boolean;
|
|
173
173
|
/** Validate a unique overlap without consuming input or activating this owner. */
|
|
174
|
-
prepareResume(snapshot:
|
|
175
|
-
referenceHash: string;
|
|
176
|
-
batchSize: number;
|
|
177
|
-
}): void;
|
|
174
|
+
prepareResume(snapshot: InputQueueCursor): void;
|
|
178
175
|
activatePreparedResume(): void;
|
|
179
176
|
becomeActive(snapshot: {
|
|
180
177
|
referenceHash: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"throttled-queue.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/throttled-queue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"throttled-queue.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/throttled-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { basePath } from "../base-path.js";
|
|
5
5
|
import logger from "../logger.js";
|
|
6
|
+
import { inputCursorSchema, inputFingerprint, inputPreviewSchema, uniqueInputBoundary } from "../runtime/input-queue-boundary.js";
|
|
6
7
|
/**
|
|
7
8
|
* Abstract base class that encapsulates common functionality for managing
|
|
8
9
|
* a throttled in‑memory queue with an optional disk persistence.
|
|
@@ -401,16 +402,39 @@ export class ThrottledSubscriber extends ThrottledQueue {
|
|
|
401
402
|
if (batchSize > 0) {
|
|
402
403
|
logger.info(`${this.instanceName} - Subscriber batch reference hash: ${referenceHash}, size: ${batchSize}`);
|
|
403
404
|
}
|
|
404
|
-
return { referenceHash, batchSize };
|
|
405
|
+
return { referenceHash, batchSize, ...(strict ? { processedTail: this.lastProcessedItems.map(inputFingerprint) } : {}) };
|
|
405
406
|
}
|
|
406
407
|
/**
|
|
407
408
|
* Switch the subscriber to an active state and resume processing.
|
|
408
409
|
*/
|
|
409
410
|
preparedResume;
|
|
411
|
+
getInputPreview() {
|
|
412
|
+
if (this.active)
|
|
413
|
+
throw Error("MQTT successor is not passive");
|
|
414
|
+
return this.queue.slice(-100).map(inputFingerprint);
|
|
415
|
+
}
|
|
416
|
+
canResumeInput(preview) {
|
|
417
|
+
const parsed = inputPreviewSchema.safeParse(preview);
|
|
418
|
+
if (!this.active || !parsed.success)
|
|
419
|
+
return false;
|
|
420
|
+
try {
|
|
421
|
+
uniqueInputBoundary(this.lastProcessedItems.map(inputFingerprint), parsed.data);
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
410
428
|
/** Validate a unique overlap without consuming input or activating this owner. */
|
|
411
429
|
prepareResume(snapshot) {
|
|
412
430
|
if (this.active)
|
|
413
431
|
throw Error("MQTT successor is not passive");
|
|
432
|
+
this.preparedResume = undefined;
|
|
433
|
+
snapshot = inputCursorSchema.parse(snapshot);
|
|
434
|
+
if (snapshot.processedTail) {
|
|
435
|
+
this.preparedResume = uniqueInputBoundary(snapshot.processedTail, this.queue.map(inputFingerprint));
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
414
438
|
if (snapshot.batchSize === 0) {
|
|
415
439
|
this.preparedResume = 0;
|
|
416
440
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"throttled-queue.js","sourceRoot":"","sources":["../../src/uns-mqtt/throttled-queue.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { IClientPublishOptions } from \"mqtt\";\nimport { join } from \"path\";\n\nimport { basePath } from \"../base-path.js\";\nimport logger from \"../logger.js\";\n\n/**\n * Abstract base class that encapsulates common functionality for managing\n * a throttled in‑memory queue with an optional disk persistence.\n */\nabstract class ThrottledQueue<T> {\n protected queue: T[] = [];\n protected lastProcessedItems: T[] = [];\n protected isProcessing = false;\n protected inFlight = 0;\n protected maxConcurrency: number;\n public delay: number;\n protected persistToDisk: boolean;\n protected persistenceFilePath: string;\n protected previousLoggedSize = 0;\n protected instanceName: string;\n protected active: boolean = true;\n private inactiveLogSent = false;\n\n constructor(delay: number, persistToDisk: boolean, instanceName: string, maxConcurrency: number = 1) {\n this.delay = delay;\n this.persistToDisk = persistToDisk;\n this.instanceName = instanceName;\n this.maxConcurrency = Math.max(1, maxConcurrency);\n\n if (this.persistToDisk) {\n this.loadQueueFromDisk();\n }\n }\n\n /**\n * Process the queue items one at a time with a delay between each.\n */\n protected async processQueue(): Promise<void> {\n if (this.isProcessing) return;\n\n if (!this.active) {\n if (!this.inactiveLogSent) {\n logger.info(`${this.instanceName} - Queue is not active. Exiting processQueue.`);\n this.inactiveLogSent = true;\n }\n this.isProcessing = false;\n return;\n }\n this.isProcessing = true;\n // Reset the flag when processing actually starts.\n this.inactiveLogSent = false;\n\n try {\n while (this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n // Check if the queue is active\n if (!this.active) {\n logger.info(`${this.instanceName} - Queue processing paused.`);\n return; // Exit the loop and stop processing\n }\n\n const item = this.queue.shift();\n if (!item) {\n continue;\n }\n\n this.inFlight += 1;\n void this.processItemWithBookkeeping(item);\n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n this.logQueueSize();\n\n if (this.delay > 0 && this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n await new Promise((resolve) => setTimeout(resolve, this.delay));\n }\n }\n } finally {\n this.isProcessing = false;\n if (this.active && this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n void this.processQueue();\n }\n }\n }\n\n private async processItemWithBookkeeping(item: T): Promise<void> {\n try {\n await this.processItem(item);\n // Only store 10 last processed items\n if (this.lastProcessedItems.length >= 10) {\n this.lastProcessedItems.shift();\n }\n // Store the last processed item\n this.lastProcessedItems.push(item);\n } catch (error) {\n // Specific error handling can be done in the subclass if needed.\n logger.error(`${this.instanceName} - Error processing item: ${(error as Error).message}`);\n } finally {\n this.inFlight = Math.max(0, this.inFlight - 1);\n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n this.logQueueSize();\n\n // Check if the queue is active\n if (this.active && this.queue.length > 0) {\n void this.processQueue();\n }\n }\n }\n\n /**\n * Log changes to the queue size at significant thresholds.\n */\n protected logQueueSize(): void {\n const className = `${this.constructor.name.includes(\"Publisher\") ? \"Publisher\" : this.constructor.name.includes(\"Subscriber\") ? \"Subscriber\" : \"Unknown\"}`;\n const pendingCount = this.getPendingCount();\n if (\n pendingCount > 1\n && pendingCount > this.previousLoggedSize\n && Math.floor(pendingCount / 100) > Math.floor(this.previousLoggedSize / 100)\n ) {\n logger.info(`${this.instanceName} - ${className} queue size length is ${pendingCount}.`);\n this.previousLoggedSize = pendingCount;\n } else if (pendingCount === 0 && this.previousLoggedSize > 0) {\n logger.info(`${this.instanceName} - ${className} queue is empty.`);\n this.previousLoggedSize = 0;\n }\n }\n\n protected getPendingCount(): number {\n return this.queue.length + this.inFlight;\n }\n\n /**\n * Save the current queue to disk.\n */\n protected saveQueueToDisk(): void {\n try {\n const queueData = this.queue.map((item) => this.serializeItem(item));\n const dir = join(this.persistenceFilePath, \"..\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(this.persistenceFilePath, JSON.stringify(queueData, null, 2));\n logger.debug(`${this.instanceName} - Queue saved to disk.`);\n } catch (error: any) {\n logger.error(`${this.instanceName} - Error saving queue to disk: ${error.message}`);\n }\n }\n\n /**\n * Load the queue from disk.\n */\n protected loadQueueFromDisk(): void {\n try {\n if (existsSync(this.persistenceFilePath)) {\n const queueData = JSON.parse(readFileSync(this.persistenceFilePath, \"utf8\"));\n this.queue = queueData.map((data: any) => this.deserializeItem(data));\n logger.info(`${this.instanceName} - Queue loaded from disk.`);\n }\n } catch (error: any) {\n logger.error(`${this.instanceName} - Error loading queue from disk: ${error.message}`);\n }\n }\n\n /**\n * Process a single queue item.\n */\n protected abstract processItem(item: T): Promise<void>;\n\n /**\n * Serialize a queue item for persistence.\n */\n protected abstract serializeItem(item: T): any;\n\n /**\n * Deserialize persisted data into a queue item.\n */\n protected abstract deserializeItem(data: any): T;\n}\n\n/**\n * Interface representing a queued publish item.\n */\ninterface PublisherQueueItem {\n topic: string;\n message: string;\n id: string;\n options?: IClientPublishOptions;\n resolve: () => void;\n reject: (err: any) => void;\n}\n\n/**\n * ThrottledPublisher: manages a queue of outgoing publish requests,\n * sending one message at a time using a provided publish function.\n */\nexport class ThrottledPublisher extends ThrottledQueue<PublisherQueueItem> {\n private publishFunction: (topic: string, message: string, id: string, options?: IClientPublishOptions) => Promise<void>;\n private maxPendingPublishes?: number;\n\n /**\n * @param delay Delay between messages in milliseconds.\n * @param publishFunction Function to perform the actual publish.\n * @param persistToDisk Whether to persist the queue to disk.\n * @param persistenceFilePath File path for queue persistence.\n * @param instanceName Unique instance name for logging.\n */\n constructor(\n delay: number,\n publishFunction: (topic: string, message: string, id: string, options?: IClientPublishOptions) => Promise<void>,\n persistToDisk: boolean = false,\n persistenceFilePath: string = join(basePath, \"/publisherQueue/\", \"throttled-publisher-queue.json\"),\n instanceName: string,\n active: boolean,\n maxConcurrency: number = 1,\n maxPendingPublishes?: number,\n ) {\n super(delay, persistToDisk, instanceName, maxConcurrency);\n this.persistenceFilePath = persistenceFilePath;\n this.publishFunction = publishFunction;\n this.active = active;\n this.maxPendingPublishes = maxPendingPublishes;\n if (active) {\n logger.info(`${this.instanceName} - Publisher is active.`);\n } else {\n logger.info(`${this.instanceName} - Publisher is passive.`);\n }\n }\n\n /**\n * Enqueue a publish request.\n */\n public enqueue(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n try {\n this.assertCanAcceptPublish();\n return this.enqueueAcceptedPublish(topic, message, id, options);\n } catch (error) {\n return Promise.reject(error);\n }\n }\n\n /**\n * Enqueue a publish request and throw synchronously if the bounded queue is full.\n */\n public enqueueOrThrow(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n this.assertCanAcceptPublish();\n return this.enqueueAcceptedPublish(topic, message, id, options);\n }\n\n private assertCanAcceptPublish(): void {\n if (this.maxPendingPublishes !== undefined && this.getPendingCount() >= this.maxPendingPublishes) {\n throw new Error(`${this.instanceName} - Publisher queue is full (${this.maxPendingPublishes}).`);\n }\n }\n\n private enqueueAcceptedPublish(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n return new Promise((resolve, reject) => {\n this.queue.push({ topic, message, id, options, resolve, reject });\n\n // Trigger logging after a new message is added.\n this.logQueueSize(); \n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n if (this.active) {\n this.processQueue();\n }\n });\n }\n\n private computeHashForBatch(messages: any[]): string {\n const data = JSON.stringify(messages);\n return createHash(\"sha256\").update(data).digest(\"hex\");\n }\n\n /**\n * Switch the publisher to a passive state and return a snapshot\n */\n public becomeActive(snapshot: { referenceHash: string; batchSize: number }): void {\n // If snapshot is empty (batchSize is zero), start processing from the beginning.\n if (snapshot.batchSize === 0 || snapshot.batchSize === undefined || snapshot.referenceHash === undefined) {\n this.active = true;\n logger.info(`${this.instanceName} - Publisher became active.`);\n this.processQueue();\n return;\n }\n\n const { referenceHash, batchSize } = snapshot;\n let resumeIndex = -1;\n\n // Use batchSize as the window length.\n for (let i = 0; i <= this.queue.length - batchSize; i++) {\n const currentBatch = this.queue.slice(i, i + batchSize);\n const currentHash = this.computeHashForBatch(currentBatch);\n if (currentHash === referenceHash) {\n resumeIndex = i + batchSize; // resume immediately after the matched batch\n break;\n }\n }\n\n if (resumeIndex !== -1) {\n // Adjust the queue so that processing resumes after the matching batch.\n this.queue = this.queue.slice(resumeIndex);\n logger.info(`${this.instanceName} - Publisher resumed from the matched snapshot position.`);\n } else if (this.queue.length > 0) {\n // Log info message and process the entire queue if no match is found.\n logger.info(`${this.instanceName} - No matching batch found during resume; processing the entire publisher's queue.`);\n }\n\n this.active = true;\n logger.info(`${this.instanceName} - Publisher became active.`);\n this.processQueue();\n }\n\n /**\n * Switch the publisher to a passive state and return a snapshot\n */\n public async becomePassive(n: number): Promise<{ referenceHash: string; batchSize: number; }> {\n this.active = false;\n logger.info(`${this.instanceName} - Publisher became passive.`);\n const batch = this.queue.slice(-n);\n const batchSize = batch.length;\n const referenceHash = this.computeHashForBatch(batch);\n if (batchSize > 0) {\n logger.info(`${this.instanceName} - Publisher batch reference hash: ${referenceHash}, size: ${batchSize}`);\n }\n return { referenceHash, batchSize };\n }\n\n public getState(): boolean {\n return this.active;\n }\n\n /**\n * Process a single publish request.\n */\n protected async processItem(item: PublisherQueueItem): Promise<void> {\n try {\n await this.publishFunction(item.topic, item.message, item.id, item.options);\n item.resolve();\n } catch (error) {\n item.reject(error);\n }\n }\n\n /**\n * Serialize a publish request for persistence.\n */\n protected serializeItem(item: PublisherQueueItem): any {\n return {\n topic: item.topic,\n message: item.message,\n id: item.id,\n options: item.options,\n };\n }\n\n /**\n * Deserialize persisted data into a publish request.\n */\n protected deserializeItem(data: any): PublisherQueueItem {\n return {\n topic: data.topic,\n message: data.message,\n id: data.id,\n options: data.options,\n resolve: () => {},\n reject: (err: any) => logger.error(`${this.instanceName} - Error processing queued publish item: ${err.message}`),\n };\n }\n}\n\n/**\n * Interface representing a queued subscribe item.\n */\ninterface SubscriberQueueItem {\n topic: string;\n message: string;\n timestamp: number;\n}\n\n/**\n * ThrottledSubscriber: manages a queue of incoming messages and processes\n * them one at a time using a provided message handler.\n */\nexport class ThrottledSubscriber extends ThrottledQueue<SubscriberQueueItem> {\n private messageHandler: (topic: string, message: string) => Promise<void>;\n\n /**\n * @param delay Delay between processing messages in milliseconds.\n * @param messageHandler Function to process incoming messages.\n * @param persistToDisk Whether to persist the queue to disk.\n * @param persistenceFilePath File path for queue persistence.\n * @param instanceName Unique instance name for logging.\n */\n constructor(\n delay: number,\n messageHandler: (topic: string, message: string) => Promise<void>,\n persistToDisk: boolean = false,\n persistenceFilePath: string = join(basePath, \"/subscriberQueue/\", \"throttled-subscriber-queue.json\"),\n instanceName: string,\n active: boolean,\n ) {\n super(delay, persistToDisk, instanceName);\n this.persistenceFilePath = persistenceFilePath;\n this.messageHandler = messageHandler;\n this.active = active;\n if (active) {\n logger.info(`${this.instanceName} - Subscriber is active.`);\n } else {\n logger.info(`${this.instanceName} - Subscriber is passive.`);\n }\n }\n\n /**\n * Enqueue subscribe messages for processing.\n */\n public enqueue(topic: string, message: string): void {\n this.queue.push({ topic, message, timestamp: Date.now() });\n \n // Trigger logging after a new message is added.\n this.logQueueSize();\n \n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n if (this.active) {\n this.processQueue();\n }\n }\n\n /**\n * Process a single subscribe message.\n */\n protected async processItem(item: SubscriberQueueItem): Promise<void> {\n await this.messageHandler(item.topic, item.message);\n }\n\n /**\n * Serialize a subscribe message for persistence.\n */\n protected serializeItem(item: SubscriberQueueItem): any {\n return {\n message: item.message,\n };\n }\n\n /**\n * Deserialize persisted data into a subscribe message.\n */\n protected deserializeItem(data: any): SubscriberQueueItem {\n return {\n topic: data.topic,\n message: data.message,\n timestamp: data.timestamp,\n };\n }\n\n /**\n * Get the current state of the subscriber (active or passive).\n */\n public getState(): boolean {\n return this.active;\n }\n\n /**\n * Computes a SHA‑256 hash for a given batch of subscriber items.\n */\n private computeHashForBatch(messages: SubscriberQueueItem[], strict = false): string {\n const data = JSON.stringify(messages.map((item) => strict ? { topic: item.topic, message: item.message } : this.serializeItem(item)));\n return createHash(\"sha256\").update(data).digest(\"hex\");\n }\n\n /**\n * Switch the subscriber to a passive state and return a snapshot.\n */\n public async becomePassive(n: number, strict = false): Promise<{ referenceHash: string; batchSize: number; }> {\n this.active = false;\n logger.info(`${this.instanceName} - Subscriber is passive.`);\n while (this.inFlight > 0) await new Promise(resolve => setTimeout(resolve, 1));\n const batch = this.lastProcessedItems.slice(-n);\n const batchSize = batch.length;\n const referenceHash = this.computeHashForBatch(batch, strict);\n if (batchSize > 0) {\n logger.info(`${this.instanceName} - Subscriber batch reference hash: ${referenceHash}, size: ${batchSize}`);\n }\n return { referenceHash, batchSize };\n }\n\n /**\n * Switch the subscriber to an active state and resume processing.\n */\n private preparedResume: number | undefined;\n\n /** Validate a unique overlap without consuming input or activating this owner. */\n public prepareResume(snapshot: { referenceHash: string; batchSize: number }): void {\n if (this.active) throw Error(\"MQTT successor is not passive\");\n if (snapshot.batchSize === 0) { this.preparedResume = 0; return; }\n const matches: number[] = [];\n for (let i = 0; i <= this.queue.length - snapshot.batchSize; i++) {\n if (this.computeHashForBatch(this.queue.slice(i, i + snapshot.batchSize), true) === snapshot.referenceHash)\n matches.push(i + snapshot.batchSize);\n }\n if (matches.length !== 1) throw Error(\"MQTT input overlap is missing or ambiguous; successor stays passive\");\n this.preparedResume = matches[0];\n }\n\n public activatePreparedResume(): void {\n if (this.active || this.preparedResume === undefined) throw Error(\"MQTT resume was not prepared\");\n this.queue = this.queue.slice(this.preparedResume);\n this.preparedResume = undefined;\n this.active = true;\n void this.processQueue();\n }\n\n public becomeActive(snapshot: { referenceHash: string; batchSize: number }): void {\n // If snapshot is empty (batchSize is zero), start processing from the beginning.\n if (snapshot.batchSize === 0 || snapshot.batchSize === undefined || snapshot.referenceHash === undefined) {\n this.active = true;\n logger.info(`${this.instanceName} - Subscriber became active.`); \n this.processQueue();\n return;\n }\n\n const { referenceHash, batchSize } = snapshot;\n let resumeIndex = -1;\n\n // Use batchSize as the window length.\n for (let i = 0; i <= this.queue.length - batchSize; i++) {\n const currentBatch = this.queue.slice(i, i + batchSize);\n const currentHash = this.computeHashForBatch(currentBatch);\n if (currentHash === referenceHash) {\n resumeIndex = i + batchSize; // resume immediately after the matched batch\n break;\n }\n }\n\n if (resumeIndex !== -1) {\n // Adjust the queue so that processing resumes after the matching batch.\n const queueSlice = this.queue.slice(resumeIndex); \n this.queue = queueSlice;\n logger.info(`${this.instanceName} - Subscriber resumed from the matched snapshot position.`);\n } else if (this.queue.length > 0) {\n // Log info message and process the entire queue if no match is found.\n logger.info(`${this.instanceName} - No matching batch found during resume; processing the entire subscriber's queue.`);\n }\n\n this.active = true;\n logger.info(`${this.instanceName} - Subscriber became active.`); \n this.processQueue();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"throttled-queue.js","sourceRoot":"","sources":["../../src/uns-mqtt/throttled-queue.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { IClientPublishOptions } from \"mqtt\";\nimport { join } from \"path\";\n\nimport { basePath } from \"../base-path.js\";\nimport logger from \"../logger.js\";\nimport { inputCursorSchema, inputFingerprint, inputPreviewSchema, uniqueInputBoundary, type InputQueueCursor } from \"../runtime/input-queue-boundary.js\";\n\n/**\n * Abstract base class that encapsulates common functionality for managing\n * a throttled in‑memory queue with an optional disk persistence.\n */\nabstract class ThrottledQueue<T> {\n protected queue: T[] = [];\n protected lastProcessedItems: T[] = [];\n protected isProcessing = false;\n protected inFlight = 0;\n protected maxConcurrency: number;\n public delay: number;\n protected persistToDisk: boolean;\n protected persistenceFilePath: string;\n protected previousLoggedSize = 0;\n protected instanceName: string;\n protected active: boolean = true;\n private inactiveLogSent = false;\n\n constructor(delay: number, persistToDisk: boolean, instanceName: string, maxConcurrency: number = 1) {\n this.delay = delay;\n this.persistToDisk = persistToDisk;\n this.instanceName = instanceName;\n this.maxConcurrency = Math.max(1, maxConcurrency);\n\n if (this.persistToDisk) {\n this.loadQueueFromDisk();\n }\n }\n\n /**\n * Process the queue items one at a time with a delay between each.\n */\n protected async processQueue(): Promise<void> {\n if (this.isProcessing) return;\n\n if (!this.active) {\n if (!this.inactiveLogSent) {\n logger.info(`${this.instanceName} - Queue is not active. Exiting processQueue.`);\n this.inactiveLogSent = true;\n }\n this.isProcessing = false;\n return;\n }\n this.isProcessing = true;\n // Reset the flag when processing actually starts.\n this.inactiveLogSent = false;\n\n try {\n while (this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n // Check if the queue is active\n if (!this.active) {\n logger.info(`${this.instanceName} - Queue processing paused.`);\n return; // Exit the loop and stop processing\n }\n\n const item = this.queue.shift();\n if (!item) {\n continue;\n }\n\n this.inFlight += 1;\n void this.processItemWithBookkeeping(item);\n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n this.logQueueSize();\n\n if (this.delay > 0 && this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n await new Promise((resolve) => setTimeout(resolve, this.delay));\n }\n }\n } finally {\n this.isProcessing = false;\n if (this.active && this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n void this.processQueue();\n }\n }\n }\n\n private async processItemWithBookkeeping(item: T): Promise<void> {\n try {\n await this.processItem(item);\n // Only store 10 last processed items\n if (this.lastProcessedItems.length >= 10) {\n this.lastProcessedItems.shift();\n }\n // Store the last processed item\n this.lastProcessedItems.push(item);\n } catch (error) {\n // Specific error handling can be done in the subclass if needed.\n logger.error(`${this.instanceName} - Error processing item: ${(error as Error).message}`);\n } finally {\n this.inFlight = Math.max(0, this.inFlight - 1);\n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n this.logQueueSize();\n\n // Check if the queue is active\n if (this.active && this.queue.length > 0) {\n void this.processQueue();\n }\n }\n }\n\n /**\n * Log changes to the queue size at significant thresholds.\n */\n protected logQueueSize(): void {\n const className = `${this.constructor.name.includes(\"Publisher\") ? \"Publisher\" : this.constructor.name.includes(\"Subscriber\") ? \"Subscriber\" : \"Unknown\"}`;\n const pendingCount = this.getPendingCount();\n if (\n pendingCount > 1\n && pendingCount > this.previousLoggedSize\n && Math.floor(pendingCount / 100) > Math.floor(this.previousLoggedSize / 100)\n ) {\n logger.info(`${this.instanceName} - ${className} queue size length is ${pendingCount}.`);\n this.previousLoggedSize = pendingCount;\n } else if (pendingCount === 0 && this.previousLoggedSize > 0) {\n logger.info(`${this.instanceName} - ${className} queue is empty.`);\n this.previousLoggedSize = 0;\n }\n }\n\n protected getPendingCount(): number {\n return this.queue.length + this.inFlight;\n }\n\n /**\n * Save the current queue to disk.\n */\n protected saveQueueToDisk(): void {\n try {\n const queueData = this.queue.map((item) => this.serializeItem(item));\n const dir = join(this.persistenceFilePath, \"..\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(this.persistenceFilePath, JSON.stringify(queueData, null, 2));\n logger.debug(`${this.instanceName} - Queue saved to disk.`);\n } catch (error: any) {\n logger.error(`${this.instanceName} - Error saving queue to disk: ${error.message}`);\n }\n }\n\n /**\n * Load the queue from disk.\n */\n protected loadQueueFromDisk(): void {\n try {\n if (existsSync(this.persistenceFilePath)) {\n const queueData = JSON.parse(readFileSync(this.persistenceFilePath, \"utf8\"));\n this.queue = queueData.map((data: any) => this.deserializeItem(data));\n logger.info(`${this.instanceName} - Queue loaded from disk.`);\n }\n } catch (error: any) {\n logger.error(`${this.instanceName} - Error loading queue from disk: ${error.message}`);\n }\n }\n\n /**\n * Process a single queue item.\n */\n protected abstract processItem(item: T): Promise<void>;\n\n /**\n * Serialize a queue item for persistence.\n */\n protected abstract serializeItem(item: T): any;\n\n /**\n * Deserialize persisted data into a queue item.\n */\n protected abstract deserializeItem(data: any): T;\n}\n\n/**\n * Interface representing a queued publish item.\n */\ninterface PublisherQueueItem {\n topic: string;\n message: string;\n id: string;\n options?: IClientPublishOptions;\n resolve: () => void;\n reject: (err: any) => void;\n}\n\n/**\n * ThrottledPublisher: manages a queue of outgoing publish requests,\n * sending one message at a time using a provided publish function.\n */\nexport class ThrottledPublisher extends ThrottledQueue<PublisherQueueItem> {\n private publishFunction: (topic: string, message: string, id: string, options?: IClientPublishOptions) => Promise<void>;\n private maxPendingPublishes?: number;\n\n /**\n * @param delay Delay between messages in milliseconds.\n * @param publishFunction Function to perform the actual publish.\n * @param persistToDisk Whether to persist the queue to disk.\n * @param persistenceFilePath File path for queue persistence.\n * @param instanceName Unique instance name for logging.\n */\n constructor(\n delay: number,\n publishFunction: (topic: string, message: string, id: string, options?: IClientPublishOptions) => Promise<void>,\n persistToDisk: boolean = false,\n persistenceFilePath: string = join(basePath, \"/publisherQueue/\", \"throttled-publisher-queue.json\"),\n instanceName: string,\n active: boolean,\n maxConcurrency: number = 1,\n maxPendingPublishes?: number,\n ) {\n super(delay, persistToDisk, instanceName, maxConcurrency);\n this.persistenceFilePath = persistenceFilePath;\n this.publishFunction = publishFunction;\n this.active = active;\n this.maxPendingPublishes = maxPendingPublishes;\n if (active) {\n logger.info(`${this.instanceName} - Publisher is active.`);\n } else {\n logger.info(`${this.instanceName} - Publisher is passive.`);\n }\n }\n\n /**\n * Enqueue a publish request.\n */\n public enqueue(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n try {\n this.assertCanAcceptPublish();\n return this.enqueueAcceptedPublish(topic, message, id, options);\n } catch (error) {\n return Promise.reject(error);\n }\n }\n\n /**\n * Enqueue a publish request and throw synchronously if the bounded queue is full.\n */\n public enqueueOrThrow(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n this.assertCanAcceptPublish();\n return this.enqueueAcceptedPublish(topic, message, id, options);\n }\n\n private assertCanAcceptPublish(): void {\n if (this.maxPendingPublishes !== undefined && this.getPendingCount() >= this.maxPendingPublishes) {\n throw new Error(`${this.instanceName} - Publisher queue is full (${this.maxPendingPublishes}).`);\n }\n }\n\n private enqueueAcceptedPublish(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n return new Promise((resolve, reject) => {\n this.queue.push({ topic, message, id, options, resolve, reject });\n\n // Trigger logging after a new message is added.\n this.logQueueSize(); \n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n if (this.active) {\n this.processQueue();\n }\n });\n }\n\n private computeHashForBatch(messages: any[]): string {\n const data = JSON.stringify(messages);\n return createHash(\"sha256\").update(data).digest(\"hex\");\n }\n\n /**\n * Switch the publisher to a passive state and return a snapshot\n */\n public becomeActive(snapshot: { referenceHash: string; batchSize: number }): void {\n // If snapshot is empty (batchSize is zero), start processing from the beginning.\n if (snapshot.batchSize === 0 || snapshot.batchSize === undefined || snapshot.referenceHash === undefined) {\n this.active = true;\n logger.info(`${this.instanceName} - Publisher became active.`);\n this.processQueue();\n return;\n }\n\n const { referenceHash, batchSize } = snapshot;\n let resumeIndex = -1;\n\n // Use batchSize as the window length.\n for (let i = 0; i <= this.queue.length - batchSize; i++) {\n const currentBatch = this.queue.slice(i, i + batchSize);\n const currentHash = this.computeHashForBatch(currentBatch);\n if (currentHash === referenceHash) {\n resumeIndex = i + batchSize; // resume immediately after the matched batch\n break;\n }\n }\n\n if (resumeIndex !== -1) {\n // Adjust the queue so that processing resumes after the matching batch.\n this.queue = this.queue.slice(resumeIndex);\n logger.info(`${this.instanceName} - Publisher resumed from the matched snapshot position.`);\n } else if (this.queue.length > 0) {\n // Log info message and process the entire queue if no match is found.\n logger.info(`${this.instanceName} - No matching batch found during resume; processing the entire publisher's queue.`);\n }\n\n this.active = true;\n logger.info(`${this.instanceName} - Publisher became active.`);\n this.processQueue();\n }\n\n /**\n * Switch the publisher to a passive state and return a snapshot\n */\n public async becomePassive(n: number): Promise<{ referenceHash: string; batchSize: number; }> {\n this.active = false;\n logger.info(`${this.instanceName} - Publisher became passive.`);\n const batch = this.queue.slice(-n);\n const batchSize = batch.length;\n const referenceHash = this.computeHashForBatch(batch);\n if (batchSize > 0) {\n logger.info(`${this.instanceName} - Publisher batch reference hash: ${referenceHash}, size: ${batchSize}`);\n }\n return { referenceHash, batchSize };\n }\n\n public getState(): boolean {\n return this.active;\n }\n\n /**\n * Process a single publish request.\n */\n protected async processItem(item: PublisherQueueItem): Promise<void> {\n try {\n await this.publishFunction(item.topic, item.message, item.id, item.options);\n item.resolve();\n } catch (error) {\n item.reject(error);\n }\n }\n\n /**\n * Serialize a publish request for persistence.\n */\n protected serializeItem(item: PublisherQueueItem): any {\n return {\n topic: item.topic,\n message: item.message,\n id: item.id,\n options: item.options,\n };\n }\n\n /**\n * Deserialize persisted data into a publish request.\n */\n protected deserializeItem(data: any): PublisherQueueItem {\n return {\n topic: data.topic,\n message: data.message,\n id: data.id,\n options: data.options,\n resolve: () => {},\n reject: (err: any) => logger.error(`${this.instanceName} - Error processing queued publish item: ${err.message}`),\n };\n }\n}\n\n/**\n * Interface representing a queued subscribe item.\n */\ninterface SubscriberQueueItem {\n topic: string;\n message: string;\n timestamp: number;\n}\n\n/**\n * ThrottledSubscriber: manages a queue of incoming messages and processes\n * them one at a time using a provided message handler.\n */\nexport class ThrottledSubscriber extends ThrottledQueue<SubscriberQueueItem> {\n private messageHandler: (topic: string, message: string) => Promise<void>;\n\n /**\n * @param delay Delay between processing messages in milliseconds.\n * @param messageHandler Function to process incoming messages.\n * @param persistToDisk Whether to persist the queue to disk.\n * @param persistenceFilePath File path for queue persistence.\n * @param instanceName Unique instance name for logging.\n */\n constructor(\n delay: number,\n messageHandler: (topic: string, message: string) => Promise<void>,\n persistToDisk: boolean = false,\n persistenceFilePath: string = join(basePath, \"/subscriberQueue/\", \"throttled-subscriber-queue.json\"),\n instanceName: string,\n active: boolean,\n ) {\n super(delay, persistToDisk, instanceName);\n this.persistenceFilePath = persistenceFilePath;\n this.messageHandler = messageHandler;\n this.active = active;\n if (active) {\n logger.info(`${this.instanceName} - Subscriber is active.`);\n } else {\n logger.info(`${this.instanceName} - Subscriber is passive.`);\n }\n }\n\n /**\n * Enqueue subscribe messages for processing.\n */\n public enqueue(topic: string, message: string): void {\n this.queue.push({ topic, message, timestamp: Date.now() });\n \n // Trigger logging after a new message is added.\n this.logQueueSize();\n \n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n if (this.active) {\n this.processQueue();\n }\n }\n\n /**\n * Process a single subscribe message.\n */\n protected async processItem(item: SubscriberQueueItem): Promise<void> {\n await this.messageHandler(item.topic, item.message);\n }\n\n /**\n * Serialize a subscribe message for persistence.\n */\n protected serializeItem(item: SubscriberQueueItem): any {\n return {\n message: item.message,\n };\n }\n\n /**\n * Deserialize persisted data into a subscribe message.\n */\n protected deserializeItem(data: any): SubscriberQueueItem {\n return {\n topic: data.topic,\n message: data.message,\n timestamp: data.timestamp,\n };\n }\n\n /**\n * Get the current state of the subscriber (active or passive).\n */\n public getState(): boolean {\n return this.active;\n }\n\n /**\n * Computes a SHA‑256 hash for a given batch of subscriber items.\n */\n private computeHashForBatch(messages: SubscriberQueueItem[], strict = false): string {\n const data = JSON.stringify(messages.map((item) => strict ? { topic: item.topic, message: item.message } : this.serializeItem(item)));\n return createHash(\"sha256\").update(data).digest(\"hex\");\n }\n\n /**\n * Switch the subscriber to a passive state and return a snapshot.\n */\n public async becomePassive(n: number, strict = false): Promise<InputQueueCursor> {\n this.active = false;\n logger.info(`${this.instanceName} - Subscriber is passive.`);\n while (this.inFlight > 0) await new Promise(resolve => setTimeout(resolve, 1));\n const batch = this.lastProcessedItems.slice(-n);\n const batchSize = batch.length;\n const referenceHash = this.computeHashForBatch(batch, strict);\n if (batchSize > 0) {\n logger.info(`${this.instanceName} - Subscriber batch reference hash: ${referenceHash}, size: ${batchSize}`);\n }\n return { referenceHash, batchSize, ...(strict ? { processedTail: this.lastProcessedItems.map(inputFingerprint) } : {}) };\n }\n\n /**\n * Switch the subscriber to an active state and resume processing.\n */\n private preparedResume: number | undefined;\n\n public getInputPreview(): string[] {\n if (this.active) throw Error(\"MQTT successor is not passive\");\n return this.queue.slice(-100).map(inputFingerprint);\n }\n\n public canResumeInput(preview: unknown): boolean {\n const parsed = inputPreviewSchema.safeParse(preview);\n if (!this.active || !parsed.success) return false;\n try { uniqueInputBoundary(this.lastProcessedItems.map(inputFingerprint), parsed.data); return true; }\n catch { return false; }\n }\n\n /** Validate a unique overlap without consuming input or activating this owner. */\n public prepareResume(snapshot: InputQueueCursor): void {\n if (this.active) throw Error(\"MQTT successor is not passive\");\n this.preparedResume = undefined;\n snapshot = inputCursorSchema.parse(snapshot);\n if (snapshot.processedTail) {\n this.preparedResume = uniqueInputBoundary(snapshot.processedTail, this.queue.map(inputFingerprint));\n return;\n }\n if (snapshot.batchSize === 0) { this.preparedResume = 0; return; }\n const matches: number[] = [];\n for (let i = 0; i <= this.queue.length - snapshot.batchSize; i++) {\n if (this.computeHashForBatch(this.queue.slice(i, i + snapshot.batchSize), true) === snapshot.referenceHash)\n matches.push(i + snapshot.batchSize);\n }\n if (matches.length !== 1) throw Error(\"MQTT input overlap is missing or ambiguous; successor stays passive\");\n this.preparedResume = matches[0];\n }\n\n public activatePreparedResume(): void {\n if (this.active || this.preparedResume === undefined) throw Error(\"MQTT resume was not prepared\");\n this.queue = this.queue.slice(this.preparedResume);\n this.preparedResume = undefined;\n this.active = true;\n void this.processQueue();\n }\n\n public becomeActive(snapshot: { referenceHash: string; batchSize: number }): void {\n // If snapshot is empty (batchSize is zero), start processing from the beginning.\n if (snapshot.batchSize === 0 || snapshot.batchSize === undefined || snapshot.referenceHash === undefined) {\n this.active = true;\n logger.info(`${this.instanceName} - Subscriber became active.`); \n this.processQueue();\n return;\n }\n\n const { referenceHash, batchSize } = snapshot;\n let resumeIndex = -1;\n\n // Use batchSize as the window length.\n for (let i = 0; i <= this.queue.length - batchSize; i++) {\n const currentBatch = this.queue.slice(i, i + batchSize);\n const currentHash = this.computeHashForBatch(currentBatch);\n if (currentHash === referenceHash) {\n resumeIndex = i + batchSize; // resume immediately after the matched batch\n break;\n }\n }\n\n if (resumeIndex !== -1) {\n // Adjust the queue so that processing resumes after the matching batch.\n const queueSlice = this.queue.slice(resumeIndex); \n this.queue = queueSlice;\n logger.info(`${this.instanceName} - Subscriber resumed from the matched snapshot position.`);\n } else if (this.queue.length > 0) {\n // Log info message and process the entire queue if no match is found.\n logger.info(`${this.instanceName} - No matching batch found during resume; processing the entire subscriber's queue.`);\n }\n\n this.active = true;\n logger.info(`${this.instanceName} - Subscriber became active.`); \n this.processQueue();\n }\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type MqttProxyHandoverState } from "../runtime/mqtt-handover-state.js";
|
|
2
|
+
import { type InputQueueCursor } from "../runtime/input-queue-boundary.js";
|
|
2
3
|
import { type MqttPublishQueueStatus } from "./publish-queue-contract.js";
|
|
3
4
|
import { IClientPublishOptions } from "mqtt";
|
|
4
5
|
import { IMqttPublishRequest, IUnsPacket, IUnsParameters, UnsEvents } from "../uns/uns-interfaces.js";
|
|
@@ -94,16 +95,17 @@ export default class UnsMqttProxy extends UnsProxy {
|
|
|
94
95
|
instanceName: string;
|
|
95
96
|
compatibility: string;
|
|
96
97
|
};
|
|
97
|
-
exportHandoverState(cursor:
|
|
98
|
-
batchSize: number;
|
|
99
|
-
referenceHash: string;
|
|
100
|
-
}): MqttProxyHandoverState;
|
|
98
|
+
exportHandoverState(cursor: InputQueueCursor): MqttProxyHandoverState;
|
|
101
99
|
validateHandoverState(input: MqttProxyHandoverState): void;
|
|
102
100
|
importHandoverState(input: MqttProxyHandoverState): void;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
getHandoverOffer(): Promise<{
|
|
102
|
+
inputPreview: string[];
|
|
103
|
+
instanceName: string;
|
|
104
|
+
compatibility: string;
|
|
105
|
+
}>;
|
|
106
|
+
canPrepareHandoverInput(preview: unknown): Promise<boolean>;
|
|
107
|
+
private subscriberQuery;
|
|
108
|
+
prepareHandoverResume(cursor: InputQueueCursor): Promise<void>;
|
|
107
109
|
activateHandoverResume(): Promise<void>;
|
|
108
110
|
private subscriberControl;
|
|
109
111
|
setSubscriberActive(batchSize?: number, referenceHash?: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uns-mqtt-proxy.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/uns-mqtt-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4D,KAAK,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"uns-mqtt-proxy.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/uns-mqtt-proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MQTT_HANDOVER_STATE_FORMAT, mqttProxyHandoverStateSchema } from "../runtime/mqtt-handover-state.js";
|
|
2
|
+
import { inputPreviewSchema } from "../runtime/input-queue-boundary.js";
|
|
2
3
|
import { assertDisabledPublishQueueEmpty } from "./disabled-publish-queue.js";
|
|
3
4
|
import { randomUUID, createHash } from "node:crypto";
|
|
4
5
|
import { mqttPublishQueueIdentity, mqttPublishQueueSchema } from "./publish-queue-contract.js";
|
|
@@ -190,8 +191,17 @@ export default class UnsMqttProxy extends UnsProxy {
|
|
|
190
191
|
if (pending) {
|
|
191
192
|
clearTimeout(pending.timer);
|
|
192
193
|
this.queueRequests.delete(msg.id);
|
|
193
|
-
if (msg.status === "success")
|
|
194
|
-
|
|
194
|
+
if (msg.status === "success") {
|
|
195
|
+
try {
|
|
196
|
+
if (pending.readResult)
|
|
197
|
+
pending.readResult(msg.result);
|
|
198
|
+
else
|
|
199
|
+
pending.resolve();
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
pending.reject(Error("Invalid MQTT input boundary reply"));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
195
205
|
else
|
|
196
206
|
pending.reject(new Error(msg.error));
|
|
197
207
|
}
|
|
@@ -227,7 +237,7 @@ export default class UnsMqttProxy extends UnsProxy {
|
|
|
227
237
|
this.event.emit("input", { topic: msg.topic, message: msg.message.toString(), packet: msg.packet });
|
|
228
238
|
}
|
|
229
239
|
else if (msg && (msg.command === "handover_subscriber" || msg.command === "handover_publisher")) {
|
|
230
|
-
this.event.emit("mqttWorker", { command: msg.command, batchSize: msg.batchSize, referenceHash: msg.referenceHash, instanceName: this.instanceName });
|
|
240
|
+
this.event.emit("mqttWorker", { command: msg.command, batchSize: msg.batchSize, referenceHash: msg.referenceHash, ...(msg.processedTail ? { processedTail: msg.processedTail } : {}), instanceName: this.instanceName });
|
|
231
241
|
}
|
|
232
242
|
else if (msg && msg.command === "mqttProxyStatus") {
|
|
233
243
|
if (msg.event === "mqtt-connected")
|
|
@@ -369,6 +379,36 @@ export default class UnsMqttProxy extends UnsProxy {
|
|
|
369
379
|
this.lastValues = new Map(state.values.map(entry => [entry.topic, { value: entry.value, uom: entry.uom, timestamp: new Date(entry.timestamp) }]));
|
|
370
380
|
this.currentSequenceId = new Map(state.sequences.map(([topic, sequence]) => [topic, sequence]));
|
|
371
381
|
}
|
|
382
|
+
async getHandoverOffer() {
|
|
383
|
+
return { ...this.getHandoverCompatibility(), inputPreview: inputPreviewSchema.parse(await this.subscriberQuery("previewSubscriberInput")) };
|
|
384
|
+
}
|
|
385
|
+
async canPrepareHandoverInput(preview) {
|
|
386
|
+
if (!inputPreviewSchema.safeParse(preview).success)
|
|
387
|
+
return false;
|
|
388
|
+
const result = await this.subscriberQuery("reviewSubscriberInput", preview);
|
|
389
|
+
if (typeof result !== "boolean")
|
|
390
|
+
throw Error("Invalid MQTT input review reply");
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
subscriberQuery(command, preview) {
|
|
394
|
+
if (this.workerFailure)
|
|
395
|
+
return Promise.reject(this.workerFailure);
|
|
396
|
+
if (this.queueRequests.size >= 64)
|
|
397
|
+
return Promise.reject(Error("Too many pending MQTT controls"));
|
|
398
|
+
const id = randomUUID();
|
|
399
|
+
return new Promise((resolve, reject) => {
|
|
400
|
+
const timer = setTimeout(() => { this.queueRequests.delete(id); reject(Error("MQTT input boundary review timed out")); }, 5000);
|
|
401
|
+
this.queueRequests.set(id, { resolve: () => { }, reject, timer, readResult: resolve });
|
|
402
|
+
try {
|
|
403
|
+
this.worker.postMessage({ command, id, preview });
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
clearTimeout(timer);
|
|
407
|
+
this.queueRequests.delete(id);
|
|
408
|
+
reject(error);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
}
|
|
372
412
|
prepareHandoverResume(cursor) {
|
|
373
413
|
return this.subscriberControl("prepareSubscriberResume", cursor);
|
|
374
414
|
}
|