@yarkivaev/scada 2.3.76 → 2.3.77

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.3.76",
3
+ "version": "2.3.77",
4
4
  "description": "SCADA domain objects, state persistence, and plant monitoring",
5
5
  "repository": {
6
6
  "type": "git",
@@ -7,6 +7,7 @@ import migrate, { migratePrivileged } from '../infrastructure/ingest/db/migrate.
7
7
  import stateDataFromPool from '../infrastructure/persistence/stateDataFromPool.js';
8
8
  import { parseRequestTimeoutMs } from '../infrastructure/ingest/parseRequestTimeoutMs.js';
9
9
  import pg from 'pg';
10
+ import { holdClient } from '../infrastructure/persistence/postgresPool.js';
10
11
  import path from 'path';
11
12
  import { fileURLToPath } from 'url';
12
13
 
@@ -59,7 +60,7 @@ export function readDeploymentConfig(env) {
59
60
  */
60
61
  export default function supervisorSink(env = process.env) {
61
62
  const cfg = readDeploymentConfig(env);
62
- const pool = new pg.Pool({ connectionString: cfg.postgres });
63
+ const pool = holdClient(new pg.Pool({ connectionString: cfg.postgres }));
63
64
  const segments = segmentPipeline(cfg.stomp, cfg.postgres, pool, cfg.pipeline);
64
65
  const decisions = decisionPipeline(cfg.stomp, pool, cfg.pipeline);
65
66
  const alerts = alertPipeline(cfg.stomp, pool, cfg.pipeline);
@@ -79,7 +80,7 @@ export default function supervisorSink(env = process.env) {
79
80
  if (!cfg.postgresAdmin) {
80
81
  throw new Error('POSTGRES_ADMIN_URL is required for supervisor-sink database migrations');
81
82
  }
82
- const adminPool = new pg.Pool({ connectionString: cfg.postgresAdmin });
83
+ const adminPool = holdClient(new pg.Pool({ connectionString: cfg.postgresAdmin }));
83
84
  try {
84
85
  await migrate(adminPool, cfg.migrationsDir, cfg.sinkDbProfile);
85
86
  await migratePrivileged(adminPool, cfg.migrationsDir, cfg.sinkDbProfile);
@@ -1,5 +1,24 @@
1
1
  import pg from 'pg';
2
2
 
3
+ /**
4
+ * Logs an idle postgres client failure and leaves the pool open.
5
+ * node-postgres emits `error` when an idle socket dies. With no listener
6
+ * the process exits.
7
+ *
8
+ * @param {import('pg').Pool} pool - long-lived pool
9
+ * @returns {import('pg').Pool} the same pool
10
+ *
11
+ * @example
12
+ * holdClient(new pg.Pool({ connectionString: process.env.POSTGRES_URL }));
13
+ */
14
+ export function holdClient(pool) {
15
+ const host = pool.options && pool.options.host ? pool.options.host : 'postgres';
16
+ pool.on('error', (err) => {
17
+ process.stderr.write(`[scada] postgres idle client failed host=${host} message=${err.message}\n`);
18
+ });
19
+ return pool;
20
+ }
21
+
3
22
  /**
4
23
  * Creates a PostgreSQL pool for supervisor state reads.
5
24
  *
@@ -20,5 +39,5 @@ export default function postgresPool(options = {}) {
20
39
  if (!url) {
21
40
  return undefined;
22
41
  }
23
- return new pg.Pool({ connectionString: url });
42
+ return holdClient(new pg.Pool({ connectionString: url }));
24
43
  }