@rotorsoft/act-pg 1.13.17 → 1.13.19
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/dist/.tsbuildinfo +1 -1
- package/dist/@types/postgres-store.d.ts.map +1 -1
- package/dist/index.cjs +69 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +71 -17
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -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;
|
|
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
|
@@ -51,6 +51,7 @@ var scopedTypes = {
|
|
|
51
51
|
};
|
|
52
52
|
var SAFE_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
53
53
|
var PG_UNIQUE_VIOLATION = "23505";
|
|
54
|
+
var PG_NUL_BYTE = /* @__PURE__ */ new Set(["22P05", "22021"]);
|
|
54
55
|
var NOTIFY_CHANNEL_PREFIX = "act_commit";
|
|
55
56
|
var NOTIFY_MAX_PAYLOAD_BYTES = 8e3;
|
|
56
57
|
var NOTIFY_RECONNECT_BASE_MS = 250;
|
|
@@ -309,6 +310,11 @@ var PostgresStore = class {
|
|
|
309
310
|
ON ${this._fqt} (stream COLLATE pg_catalog."default", id)
|
|
310
311
|
WHERE name = '${import_act.SNAP_EVENT}';`
|
|
311
312
|
);
|
|
313
|
+
await client.query(
|
|
314
|
+
`CREATE INDEX IF NOT EXISTS "${this.config.table}_stream_id_ix"
|
|
315
|
+
ON ${this._fqt} (stream COLLATE pg_catalog."default", id)
|
|
316
|
+
WHERE name <> '${import_act.SNAP_EVENT}';`
|
|
317
|
+
);
|
|
312
318
|
await client.query(
|
|
313
319
|
`CREATE TABLE IF NOT EXISTS ${this._fqs} (
|
|
314
320
|
stream text COLLATE pg_catalog."default" PRIMARY KEY,
|
|
@@ -601,6 +607,13 @@ var PostgresStore = class {
|
|
|
601
607
|
expectedVersion ?? -1
|
|
602
608
|
);
|
|
603
609
|
}
|
|
610
|
+
if (PG_NUL_BYTE.has(error?.code ?? "")) {
|
|
611
|
+
throw new import_act.ValidationError(
|
|
612
|
+
stream,
|
|
613
|
+
msgs,
|
|
614
|
+
`Postgres cannot store a NUL byte (\\u0000). One of the ${msgs.length} event(s) committed to "${stream}" (${msgs.map((m) => String(m.name)).join(", ")}) carries one in its name, data, meta or pii. InMemory and SQLite accept it, so this commit would have succeeded there \u2014 strip NUL bytes at the edge where the data enters. Driver: ${error.message}`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
604
617
|
throw error;
|
|
605
618
|
}
|
|
606
619
|
} finally {
|
|
@@ -636,29 +649,69 @@ var PostgresStore = class {
|
|
|
636
649
|
-- and lock EVERY claimable stream for this transaction, starving
|
|
637
650
|
-- overlapping competing consumers. We only lock the small
|
|
638
651
|
-- lagging+leading candidate slice, down in the "locked" CTE.
|
|
639
|
-
|
|
652
|
+
-- Eligibility, split by SOURCE CLASS rather than expressed as one
|
|
653
|
+
-- disjunction (#1448). The three classes are mutually exclusive, so
|
|
654
|
+
-- the UNION ALL returns exactly what the old OR-chain did \u2014 but each
|
|
655
|
+
-- arm now carries a single, planner-legible predicate.
|
|
656
|
+
--
|
|
657
|
+
-- That matters for one arm in particular. Written as
|
|
658
|
+
-- "s.source IS NULL OR (... AND e.stream = s.source) OR (... ~ ...)",
|
|
659
|
+
-- the equality is buried inside a disjunction and is NOT sargable:
|
|
660
|
+
-- no index on (stream, id) can serve it, so every probe degenerated
|
|
661
|
+
-- into a forward scan of the pk from the stream's watermark. With
|
|
662
|
+
-- the classes separated, the literal arm \u2014 every per-aggregate
|
|
663
|
+
-- .to(e => ({target: e.stream})) reaction, i.e. the overwhelming
|
|
664
|
+
-- majority \u2014 becomes an index seek on (stream, id).
|
|
665
|
+
--
|
|
666
|
+
-- Measured at 10k subscribed streams with 10 pending: 5,792 ms
|
|
667
|
+
-- before, 10.3 ms after. See libs/act-pg/PERFORMANCE.md for #1448.
|
|
668
|
+
eligible AS (
|
|
640
669
|
SELECT stream, source, at, priority, lane
|
|
641
670
|
FROM ${this._fqs} s
|
|
642
671
|
WHERE blocked = false
|
|
643
672
|
${lane_clause}
|
|
644
673
|
AND (leased_by IS NULL OR leased_until <= NOW())
|
|
645
674
|
AND (deferred_at IS NULL OR deferred_at <= NOW())
|
|
646
|
-
|
|
675
|
+
),
|
|
676
|
+
available AS (
|
|
677
|
+
-- Every arm below is watermark-agnostic (#1446): a fresh
|
|
678
|
+
-- subscription sits at -1, and e.id > s.at already answers
|
|
679
|
+
-- correctly there, since the first event has a greater id.
|
|
680
|
+
-- There used to be a fourth arm claiming at < 0 unconditionally,
|
|
681
|
+
-- which handed out an empty lease and a no-op ack for every
|
|
682
|
+
-- subscription with no matching events yet.
|
|
683
|
+
-- Source-less subscription: any non-snapshot event past the
|
|
684
|
+
-- watermark counts, so the id index alone answers it.
|
|
685
|
+
SELECT stream, source, at, priority, lane FROM eligible s
|
|
686
|
+
WHERE s.source IS NULL
|
|
687
|
+
AND EXISTS (
|
|
688
|
+
SELECT 1 FROM ${this._fqt} e
|
|
689
|
+
WHERE e.id > s.at AND e.name <> '${import_act.SNAP_EVENT}' LIMIT 1
|
|
690
|
+
)
|
|
691
|
+
UNION ALL
|
|
692
|
+
-- Literal source (no regex metacharacter): exact equality, so
|
|
693
|
+
-- "s1" never claims "s12", and (stream, id) is usable.
|
|
694
|
+
SELECT stream, source, at, priority, lane FROM eligible s
|
|
695
|
+
WHERE s.source IS NOT NULL
|
|
696
|
+
AND s.source !~ '${SOURCE_METACHARACTER_CLASS}'
|
|
697
|
+
AND EXISTS (
|
|
698
|
+
SELECT 1 FROM ${this._fqt} e
|
|
699
|
+
WHERE e.stream = s.source AND e.id > s.at
|
|
700
|
+
AND e.name <> '${import_act.SNAP_EVENT}' LIMIT 1
|
|
701
|
+
)
|
|
702
|
+
UNION ALL
|
|
703
|
+
-- Pattern source (e.g. '^(A|B)$'): POSIX match, so the
|
|
704
|
+
-- calculator's static regex reaction is claimed for every stream
|
|
705
|
+
-- it anchors. Unavoidably a scan \u2014 but only for the subscriptions
|
|
706
|
+
-- that actually declare a pattern.
|
|
707
|
+
SELECT stream, source, at, priority, lane FROM eligible s
|
|
708
|
+
WHERE s.source IS NOT NULL
|
|
709
|
+
AND s.source ~ '${SOURCE_METACHARACTER_CLASS}'
|
|
710
|
+
AND EXISTS (
|
|
647
711
|
SELECT 1 FROM ${this._fqt} e
|
|
648
|
-
WHERE e.id > s.at
|
|
649
|
-
AND e.
|
|
650
|
-
|
|
651
|
-
-- equality \u2014 index-friendly, and exact so "s1" never
|
|
652
|
-
-- claims "s12". A pattern source (e.g. '^(A|B)$') matches
|
|
653
|
-
-- with the POSIX regex operator so the calculator's static
|
|
654
|
-
-- regex reaction is claimed for every stream it anchors.
|
|
655
|
-
AND (
|
|
656
|
-
s.source IS NULL
|
|
657
|
-
OR (s.source !~ '${SOURCE_METACHARACTER_CLASS}' AND e.stream = s.source)
|
|
658
|
-
OR (s.source ~ '${SOURCE_METACHARACTER_CLASS}' AND e.stream ~ s.source)
|
|
659
|
-
)
|
|
660
|
-
LIMIT 1
|
|
661
|
-
))
|
|
712
|
+
WHERE e.id > s.at AND e.name <> '${import_act.SNAP_EVENT}'
|
|
713
|
+
AND e.stream ~ s.source LIMIT 1
|
|
714
|
+
)
|
|
662
715
|
),
|
|
663
716
|
-- Priority lanes (ACT-102): higher priority first, then
|
|
664
717
|
-- lagging-watermark order. With everyone at priority=0 the
|