@yarkivaev/scada 2.4.5 → 2.4.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yarkivaev/scada",
3
- "version": "2.4.5",
3
+ "version": "2.4.6",
4
4
  "description": "SCADA domain objects, state persistence, and plant monitoring",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,8 +43,8 @@ function segmentSinks(postgres, pool) {
43
43
  /**
44
44
  * Pipeline for streaming STOMP segment data to PostgreSQL segments table.
45
45
  * Subscribes to the durable ingest queue so shovel traffic survives consumer gaps.
46
- * Pending heartbeats are coalesced per machine. Source acceptance is FIFO:
47
- * queued lane work must not consume another delivery's execution deadline.
46
+ * Pending heartbeats are coalesced per machine. Machine lanes run in parallel;
47
+ * only storage execution consumes its watchdog, not bounded lane queue wait.
48
48
  *
49
49
  * @param {string} stomp - STOMP broker URL
50
50
  * @param {string} postgres - PostgreSQL connection URL
@@ -71,7 +71,10 @@ export default function segmentPipeline(stomp, postgres, pool, config) {
71
71
  interval: config.interval
72
72
  });
73
73
  const destination = config.segmentsDestination || segmentsIngestDestination;
74
- source = stompSource(stomp, destination, coalesce, stompOptions(config, { serial: true, manualAck: true }));
74
+ // Each admitted frame can flush one heartbeat and then persist itself; prefetch bounds queue depth.
75
+ const deliveryTimeoutMs = Math.min(2147483647, (config.acceptTimeoutMs ?? 10000) * (config.prefetch ?? 32) * 2);
76
+ source = stompSource(stomp, destination, coalesce,
77
+ stompOptions(config, { serial: false, manualAck: true, deliveryTimeoutMs }));
75
78
  const silence = silentOpenWatch(closeSilentOpen(pool), budget, {
76
79
  intervalMs: (config.poll || 5) * 1000
77
80
  });
@@ -3,7 +3,7 @@ export default function stompOptions(config, overrides = {}) {
3
3
  const options = { login: config.login, passcode: config.passcode, host: config.host,
4
4
  prefetch: config.prefetch, maxMessageBytes: config.maxMessageBytes,
5
5
  maxPendingBytes: config.maxPendingBytes, budget: config.budget,
6
- acceptTimeoutMs: config.acceptTimeoutMs, stopTimeoutMs: config.stopTimeoutMs,
6
+ acceptTimeoutMs: config.acceptTimeoutMs, deliveryTimeoutMs: config.deliveryTimeoutMs, stopTimeoutMs: config.stopTimeoutMs,
7
7
  onError: config.onError, onFatal: config.onFatal, onState: config.onState, ...overrides };
8
8
  return Object.fromEntries(Object.entries(options).filter(([, value]) => { return value !== undefined; }));
9
9
  }
@@ -3,8 +3,9 @@ import budgetAccept from './budgetAccept.js';
3
3
 
4
4
  export function stompSettings(options, prefetch) {
5
5
  const config = { acceptTimeoutMs: 10000, stopTimeoutMs: 5000, maxMessageBytes: 1048576, ...options };
6
+ config.deliveryTimeoutMs ??= config.acceptTimeoutMs;
6
7
  config.maxPendingBytes ??= prefetch * config.maxMessageBytes;
7
- ['acceptTimeoutMs', 'stopTimeoutMs', 'maxMessageBytes', 'maxPendingBytes'].forEach((key) => { positive(config[key], key); });
8
+ ['acceptTimeoutMs', 'deliveryTimeoutMs', 'stopTimeoutMs', 'maxMessageBytes', 'maxPendingBytes'].forEach((key) => { positive(config[key], key); });
8
9
  if (!Number.isSafeInteger(config.maxMessageBytes) || !Number.isSafeInteger(config.maxPendingBytes)) {
9
10
  throw new TypeError('STOMP byte capacities must be safe integers');
10
11
  }
@@ -85,7 +85,7 @@ async function deliver(ctx, body) {
85
85
  try {
86
86
  await deadline(Promise.resolve().then(() => {
87
87
  return acceptStomp(ctx, body, settle);
88
- }), ctx.lifecycle.acceptTimeoutMs, 'STOMP acceptance', (error) => {
88
+ }), ctx.lifecycle.deliveryTimeoutMs, 'STOMP acceptance', (error) => {
89
89
  ctx.lifecycle.fail(error);
90
90
  });
91
91
  if (!ctx.manualAck) {
@@ -139,6 +139,8 @@ function bind(channel, destination, collector, delivery) {
139
139
  * Messages are `{destination, payload, settle}`. Default ack-after-accept;
140
140
  * with `manualAck` the collector owns settle. Arrivals enqueue in
141
141
  * subscribe order. With `serial: false` persist may overlap after enqueue.
142
+ * deliveryTimeoutMs optionally includes bounded collector queue wait; body reads
143
+ * retain acceptTimeoutMs. Collectors owning an execution deadline can separate them.
142
144
  * A finite prefetch (default 32) bounds unacknowledged broker deliveries.
143
145
  *
144
146
  * @param {string} url - STOMP broker URL