@rotorsoft/act-pg 1.10.0 → 1.10.2
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/README.md +24 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/postgres-store.d.ts +15 -6
- package/dist/@types/postgres-store.d.ts.map +1 -1
- package/dist/index.cjs +66 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +66 -20
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/postgres-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,SAAS,EACT,KAAK,EAEL,OAAO,EACP,cAAc,EACd,KAAK,EACL,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,MAAM,EACN,OAAO,EACP,KAAK,EACL,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAQxB,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;AAUpB,KAAK,MAAM,GAAG,QAAQ,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,cAAc,CAAC,EAAE,UAAU,CAAC;CAC7B,CAAC,GACA,EAAE,CAAC,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/postgres-store.ts"],"names":[],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -59,6 +59,7 @@ types.setTypeParser(
|
|
|
59
59
|
var SAFE_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
60
60
|
var PG_UNIQUE_VIOLATION = "23505";
|
|
61
61
|
var NOTIFY_CHANNEL_PREFIX = "act_commit";
|
|
62
|
+
var NOTIFY_MAX_PAYLOAD_BYTES = 8e3;
|
|
62
63
|
function notify_channel(schema, table) {
|
|
63
64
|
return `${NOTIFY_CHANNEL_PREFIX}_${schema}_${table}`;
|
|
64
65
|
}
|
|
@@ -74,7 +75,35 @@ var DEFAULT_CONFIG = {
|
|
|
74
75
|
password: "postgres",
|
|
75
76
|
schema: "public",
|
|
76
77
|
table: "events",
|
|
77
|
-
notify: false
|
|
78
|
+
notify: false,
|
|
79
|
+
// Opinionated pool defaults (#1119). node-postgres ships `max: 10`
|
|
80
|
+
// with no acquisition timeout and no statement timeout — a saturated
|
|
81
|
+
// pool makes every caller hang indefinitely instead of failing with
|
|
82
|
+
// a diagnosable error. Nearly every store method holds a client for
|
|
83
|
+
// a multi-statement transaction, so multi-lane drains plus API
|
|
84
|
+
// traffic can exhaust a 10-client pool quickly. All four values are
|
|
85
|
+
// plain `pg.PoolConfig` fields — caller config overrides any of them
|
|
86
|
+
// via the constructor spread.
|
|
87
|
+
//
|
|
88
|
+
// - `max: 20` — floor for the default lane's parallel handler budget
|
|
89
|
+
// (streamLimit 10, each commit holds a client) plus API commits,
|
|
90
|
+
// the optional LISTEN client, and headroom. Sizing rule in the
|
|
91
|
+
// README: Σ per-lane streamLimit + API concurrency + notify + 2–4.
|
|
92
|
+
// - `connectionTimeoutMillis: 10_000` — fail acquisition fast (the
|
|
93
|
+
// pg default of 0 waits forever); surfaces as StoreError via
|
|
94
|
+
// `_client()` so operators see *which* operation starved.
|
|
95
|
+
// - `idleTimeoutMillis: 30_000` — keep idle clients warm across
|
|
96
|
+
// drain cycles (cycleMs can exceed the pg default of 10s in
|
|
97
|
+
// low-traffic deployments) without pinning connections for long.
|
|
98
|
+
// - `statement_timeout: 60_000` — per-statement (not per-transaction)
|
|
99
|
+
// ceiling; every statement the store issues (claim CTE, seed DDL,
|
|
100
|
+
// truncate, restore's per-event inserts) legitimately completes
|
|
101
|
+
// orders of magnitude faster, so 60s only fires on a wedged server
|
|
102
|
+
// or a lost lock instead of holding the client hostage.
|
|
103
|
+
max: 20,
|
|
104
|
+
connectionTimeoutMillis: 1e4,
|
|
105
|
+
idleTimeoutMillis: 3e4,
|
|
106
|
+
statement_timeout: 6e4
|
|
78
107
|
};
|
|
79
108
|
var PostgresStore = class {
|
|
80
109
|
_pool;
|
|
@@ -145,6 +174,21 @@ var PostgresStore = class {
|
|
|
145
174
|
}
|
|
146
175
|
this._resolve_pii_key = this.config.pii_encryption ? (0, import_act_crypto.makeKeyResolver)(this.config.pii_encryption) : void 0;
|
|
147
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Acquire a pooled client, translating acquisition failures into
|
|
179
|
+
* {@link StoreError} with the calling operation as context. With the
|
|
180
|
+
* default `connectionTimeoutMillis`, a saturated pool fails here
|
|
181
|
+
* after 10s with `Store operation "<operation>" failed` (driver
|
|
182
|
+
* error preserved as `cause`) instead of hanging indefinitely.
|
|
183
|
+
* Every method that checks out a client routes through this helper.
|
|
184
|
+
*/
|
|
185
|
+
async _client(operation) {
|
|
186
|
+
try {
|
|
187
|
+
return await this._pool.connect();
|
|
188
|
+
} catch (error) {
|
|
189
|
+
throw new import_act.StoreError(operation, { cause: error });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
148
192
|
/**
|
|
149
193
|
* Dispose of the store and close all database connections.
|
|
150
194
|
* Releases any active LISTEN client first so the pool can drain cleanly.
|
|
@@ -178,7 +222,7 @@ var PostgresStore = class {
|
|
|
178
222
|
* @throws Error if seeding fails
|
|
179
223
|
*/
|
|
180
224
|
async seed() {
|
|
181
|
-
const client = await this.
|
|
225
|
+
const client = await this._client("seed");
|
|
182
226
|
try {
|
|
183
227
|
await client.query("BEGIN");
|
|
184
228
|
await client.query(
|
|
@@ -391,7 +435,7 @@ var PostgresStore = class {
|
|
|
391
435
|
*/
|
|
392
436
|
async commit(stream, msgs, meta, expectedVersion) {
|
|
393
437
|
if (msgs.length === 0) return [];
|
|
394
|
-
const client = await this.
|
|
438
|
+
const client = await this._client("commit");
|
|
395
439
|
let version = -1;
|
|
396
440
|
try {
|
|
397
441
|
await client.query("BEGIN");
|
|
@@ -443,10 +487,11 @@ var PostgresStore = class {
|
|
|
443
487
|
events: committed.map((c) => ({ id: c.id, name: c.name })),
|
|
444
488
|
by: this._by
|
|
445
489
|
});
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
490
|
+
if (Buffer.byteLength(payload, "utf8") < NOTIFY_MAX_PAYLOAD_BYTES)
|
|
491
|
+
await client.query(`SELECT pg_notify($1, $2)`, [
|
|
492
|
+
this._channel,
|
|
493
|
+
payload
|
|
494
|
+
]);
|
|
450
495
|
}
|
|
451
496
|
await client.query("COMMIT");
|
|
452
497
|
return committed;
|
|
@@ -473,7 +518,7 @@ var PostgresStore = class {
|
|
|
473
518
|
* @returns Leased streams with metadata
|
|
474
519
|
*/
|
|
475
520
|
async claim(lagging, leading, by, millis, lane) {
|
|
476
|
-
const client = await this.
|
|
521
|
+
const client = await this._client("claim");
|
|
477
522
|
try {
|
|
478
523
|
await client.query("BEGIN");
|
|
479
524
|
const lane_clause = lane !== void 0 ? `AND s.lane = $5` : "";
|
|
@@ -555,7 +600,7 @@ var PostgresStore = class {
|
|
|
555
600
|
* @returns subscribed count and current max watermark.
|
|
556
601
|
*/
|
|
557
602
|
async subscribe(streams) {
|
|
558
|
-
const client = await this.
|
|
603
|
+
const client = await this._client("subscribe");
|
|
559
604
|
try {
|
|
560
605
|
await client.query("BEGIN");
|
|
561
606
|
let subscribed = 0;
|
|
@@ -615,30 +660,31 @@ var PostgresStore = class {
|
|
|
615
660
|
* @returns Acked leases.
|
|
616
661
|
*/
|
|
617
662
|
async ack(leases) {
|
|
618
|
-
const client = await this.
|
|
663
|
+
const client = await this._client("ack");
|
|
619
664
|
try {
|
|
620
665
|
await client.query("BEGIN");
|
|
621
666
|
const { rows } = await client.query(
|
|
622
667
|
`
|
|
623
668
|
WITH input AS (
|
|
624
669
|
SELECT * FROM jsonb_to_recordset($1::jsonb)
|
|
625
|
-
AS x(stream text, by text, at int, lagging boolean)
|
|
670
|
+
AS x(stream text, by text, at int, lagging boolean, due bigint)
|
|
626
671
|
)
|
|
627
672
|
UPDATE ${this._fqs} AS s
|
|
628
673
|
SET
|
|
629
|
-
at = i.at,
|
|
674
|
+
at = CASE WHEN i.due IS NULL THEN i.at ELSE s.at END,
|
|
630
675
|
retry = -1,
|
|
631
676
|
leased_by = NULL,
|
|
632
677
|
leased_until = NULL,
|
|
633
|
-
deferred_at = NULL
|
|
678
|
+
deferred_at = CASE WHEN i.due IS NULL THEN NULL
|
|
679
|
+
ELSE to_timestamp(i.due / 1000.0) END
|
|
634
680
|
FROM input i
|
|
635
681
|
WHERE s.stream = i.stream AND s.leased_by = i.by
|
|
636
|
-
RETURNING s.stream, s.source, s.at, i.by, s.retry, i.lagging, s.lane
|
|
682
|
+
RETURNING s.stream, s.source, s.at, i.by, s.retry, i.lagging, s.lane, i.due
|
|
637
683
|
`,
|
|
638
684
|
[JSON.stringify(leases)]
|
|
639
685
|
);
|
|
640
686
|
await client.query("COMMIT");
|
|
641
|
-
return rows.map((row) => ({
|
|
687
|
+
return rows.filter((row) => row.due === null).map((row) => ({
|
|
642
688
|
stream: row.stream,
|
|
643
689
|
source: row.source ?? void 0,
|
|
644
690
|
at: row.at,
|
|
@@ -661,7 +707,7 @@ var PostgresStore = class {
|
|
|
661
707
|
* @returns Blocked leases.
|
|
662
708
|
*/
|
|
663
709
|
async block(leases) {
|
|
664
|
-
const client = await this.
|
|
710
|
+
const client = await this._client("block");
|
|
665
711
|
try {
|
|
666
712
|
await client.query("BEGIN");
|
|
667
713
|
const { rows } = await client.query(
|
|
@@ -892,7 +938,7 @@ var PostgresStore = class {
|
|
|
892
938
|
if (conditions.length) sql += " WHERE " + conditions.join(" AND ");
|
|
893
939
|
values.push(limit);
|
|
894
940
|
sql += ` ORDER BY stream LIMIT $${values.length}`;
|
|
895
|
-
const client = await this.
|
|
941
|
+
const client = await this._client("query_streams");
|
|
896
942
|
try {
|
|
897
943
|
const [streamsResult, maxResult] = await Promise.all([
|
|
898
944
|
client.query(sql, values),
|
|
@@ -1122,7 +1168,7 @@ var PostgresStore = class {
|
|
|
1122
1168
|
*/
|
|
1123
1169
|
async _subscribe_notifications(handler) {
|
|
1124
1170
|
await this._teardown_listen();
|
|
1125
|
-
const client = await this.
|
|
1171
|
+
const client = await this._client("notify");
|
|
1126
1172
|
const on_notification = (msg) => {
|
|
1127
1173
|
if (msg.channel !== this._channel) return;
|
|
1128
1174
|
if (!msg.payload) return;
|
|
@@ -1183,7 +1229,7 @@ var PostgresStore = class {
|
|
|
1183
1229
|
async truncate(targets) {
|
|
1184
1230
|
if (!targets.length) return /* @__PURE__ */ new Map();
|
|
1185
1231
|
const streams = targets.map((t) => t.stream);
|
|
1186
|
-
const client = await this.
|
|
1232
|
+
const client = await this._client("truncate");
|
|
1187
1233
|
try {
|
|
1188
1234
|
await client.query("BEGIN");
|
|
1189
1235
|
await client.query(`DELETE FROM ${this._fqs} WHERE stream = ANY($1)`, [
|
|
@@ -1234,7 +1280,7 @@ var PostgresStore = class {
|
|
|
1234
1280
|
* from 1. `created` is preserved verbatim from the source.
|
|
1235
1281
|
*/
|
|
1236
1282
|
async restore(driver) {
|
|
1237
|
-
const client = await this.
|
|
1283
|
+
const client = await this._client("restore");
|
|
1238
1284
|
try {
|
|
1239
1285
|
await client.query("BEGIN");
|
|
1240
1286
|
await client.query(
|