@rotorsoft/act-pg 1.14.0 → 1.15.1
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 +2 -7
- package/dist/@types/postgres-store.d.ts.map +1 -1
- package/dist/index.cjs +53 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +53 -7
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -306,13 +306,14 @@ var PostgresStore = class {
|
|
|
306
306
|
leased_until timestamptz,
|
|
307
307
|
priority int NOT NULL DEFAULT 0,
|
|
308
308
|
lane text NOT NULL DEFAULT 'default',
|
|
309
|
-
deferred_at timestamptz
|
|
309
|
+
deferred_at timestamptz,
|
|
310
|
+
correlated_at int
|
|
310
311
|
) TABLESPACE pg_default;`
|
|
311
312
|
);
|
|
312
313
|
await client.query(
|
|
313
314
|
`CREATE TABLE IF NOT EXISTS ${this._fqc} (
|
|
314
315
|
id int PRIMARY KEY DEFAULT 0,
|
|
315
|
-
at
|
|
316
|
+
at int NOT NULL DEFAULT -1,
|
|
316
317
|
CONSTRAINT ${this.config.table}_correlated_singleton CHECK (id = 0)
|
|
317
318
|
) TABLESPACE pg_default;`
|
|
318
319
|
);
|
|
@@ -331,6 +332,10 @@ var PostgresStore = class {
|
|
|
331
332
|
`ALTER TABLE ${this._fqs}
|
|
332
333
|
ADD COLUMN IF NOT EXISTS deferred_at timestamptz;`
|
|
333
334
|
);
|
|
335
|
+
await client.query(
|
|
336
|
+
`ALTER TABLE ${this._fqs}
|
|
337
|
+
ADD COLUMN IF NOT EXISTS correlated_at int;`
|
|
338
|
+
);
|
|
334
339
|
await client.query(
|
|
335
340
|
`DO $$
|
|
336
341
|
BEGIN
|
|
@@ -379,6 +384,11 @@ var PostgresStore = class {
|
|
|
379
384
|
`CREATE INDEX IF NOT EXISTS "${this.config.table}_streams_lane_ix"
|
|
380
385
|
ON ${this._fqs} (lane);`
|
|
381
386
|
);
|
|
387
|
+
await client.query(
|
|
388
|
+
`CREATE INDEX IF NOT EXISTS "${this.config.table}_streams_correlated_at_ix"
|
|
389
|
+
ON ${this._fqs} (lane, priority DESC, at)
|
|
390
|
+
WHERE blocked = false AND at < correlated_at;`
|
|
391
|
+
);
|
|
382
392
|
await client.query("COMMIT");
|
|
383
393
|
logger.info(
|
|
384
394
|
`Seeded schema "${this.config.schema}" with table "${this.config.table}"`
|
|
@@ -655,7 +665,7 @@ var PostgresStore = class {
|
|
|
655
665
|
-- Measured at 10k subscribed streams with 10 pending: 5,792 ms
|
|
656
666
|
-- before, 10.3 ms after. See libs/act-pg/PERFORMANCE.md for #1448.
|
|
657
667
|
eligible AS (
|
|
658
|
-
SELECT stream, source, at, priority, lane
|
|
668
|
+
SELECT stream, source, at, priority, lane, correlated_at
|
|
659
669
|
FROM ${this._fqs} s
|
|
660
670
|
WHERE blocked = false
|
|
661
671
|
${lane_clause}
|
|
@@ -663,6 +673,26 @@ var PostgresStore = class {
|
|
|
663
673
|
AND (deferred_at IS NULL OR deferred_at <= NOW())
|
|
664
674
|
),
|
|
665
675
|
available AS (
|
|
676
|
+
-- Fast arm (#1485): a marked stream answers from the subscription
|
|
677
|
+
-- row alone. Read from the base table, NOT from the eligible CTE:
|
|
678
|
+
-- it is referenced several times so PG materializes it, and a
|
|
679
|
+
-- materialized scan cannot use the partial index this predicate was
|
|
680
|
+
-- built for (WHERE blocked = false AND at < correlated_at).
|
|
681
|
+
-- The comparison is NULL-safe: an unmarked row compares
|
|
682
|
+
-- unknown and is excluded here, falling through to the legacy arms.
|
|
683
|
+
SELECT stream, source, at, priority, lane
|
|
684
|
+
FROM ${this._fqs} s
|
|
685
|
+
WHERE s.blocked = false
|
|
686
|
+
AND s.at < s.correlated_at
|
|
687
|
+
${lane_clause}
|
|
688
|
+
AND (s.leased_by IS NULL OR s.leased_until <= NOW())
|
|
689
|
+
AND (s.deferred_at IS NULL OR s.deferred_at <= NOW())
|
|
690
|
+
UNION ALL
|
|
691
|
+
-- Legacy arms, gated on an absent mark. NULL means "unknown", so
|
|
692
|
+
-- an install that predates the column probes the event log exactly
|
|
693
|
+
-- as it did before, and each row migrates to the fast arm the first
|
|
694
|
+
-- time correlate marks it. Deleted once correlate marks universally.
|
|
695
|
+
--
|
|
666
696
|
-- Every arm below is watermark-agnostic (#1446): a fresh
|
|
667
697
|
-- subscription sits at -1, and e.id > s.at already answers
|
|
668
698
|
-- correctly there, since the first event has a greater id.
|
|
@@ -672,7 +702,8 @@ var PostgresStore = class {
|
|
|
672
702
|
-- Source-less subscription: any non-snapshot event past the
|
|
673
703
|
-- watermark counts, so the id index alone answers it.
|
|
674
704
|
SELECT stream, source, at, priority, lane FROM eligible s
|
|
675
|
-
WHERE s.
|
|
705
|
+
WHERE s.correlated_at IS NULL
|
|
706
|
+
AND s.source IS NULL
|
|
676
707
|
AND EXISTS (
|
|
677
708
|
SELECT 1 FROM ${this._fqt} e
|
|
678
709
|
WHERE e.id > s.at AND e.name <> '${SNAP_EVENT}' LIMIT 1
|
|
@@ -681,7 +712,8 @@ var PostgresStore = class {
|
|
|
681
712
|
-- Literal source (no regex metacharacter): exact equality, so
|
|
682
713
|
-- "s1" never claims "s12", and (stream, id) is usable.
|
|
683
714
|
SELECT stream, source, at, priority, lane FROM eligible s
|
|
684
|
-
WHERE s.
|
|
715
|
+
WHERE s.correlated_at IS NULL
|
|
716
|
+
AND s.source IS NOT NULL
|
|
685
717
|
AND s.source !~ '${SOURCE_METACHARACTER_CLASS}'
|
|
686
718
|
AND EXISTS (
|
|
687
719
|
SELECT 1 FROM ${this._fqt} e
|
|
@@ -694,7 +726,8 @@ var PostgresStore = class {
|
|
|
694
726
|
-- it anchors. Unavoidably a scan \u2014 but only for the subscriptions
|
|
695
727
|
-- that actually declare a pattern.
|
|
696
728
|
SELECT stream, source, at, priority, lane FROM eligible s
|
|
697
|
-
WHERE s.
|
|
729
|
+
WHERE s.correlated_at IS NULL
|
|
730
|
+
AND s.source IS NOT NULL
|
|
698
731
|
AND s.source ~ '${SOURCE_METACHARACTER_CLASS}'
|
|
699
732
|
AND EXISTS (
|
|
700
733
|
SELECT 1 FROM ${this._fqt} e
|
|
@@ -836,10 +869,23 @@ var PostgresStore = class {
|
|
|
836
869
|
`,
|
|
837
870
|
[JSON.stringify(streams)]
|
|
838
871
|
);
|
|
872
|
+
if (streams.some((s) => s.correlated_at !== void 0))
|
|
873
|
+
await client.query(
|
|
874
|
+
`
|
|
875
|
+
UPDATE ${this._fqs} t
|
|
876
|
+
SET correlated_at = (s->>'correlated_at')::int
|
|
877
|
+
FROM jsonb_array_elements($1::jsonb) AS s
|
|
878
|
+
WHERE t.stream = s->>'stream'
|
|
879
|
+
AND s->>'correlated_at' IS NOT NULL
|
|
880
|
+
AND (t.correlated_at IS NULL
|
|
881
|
+
OR t.correlated_at < (s->>'correlated_at')::int)
|
|
882
|
+
`,
|
|
883
|
+
[JSON.stringify(streams)]
|
|
884
|
+
);
|
|
839
885
|
}
|
|
840
886
|
if (correlated_at !== void 0)
|
|
841
887
|
await client.query(
|
|
842
|
-
`UPDATE ${this._fqc} SET at = GREATEST(at, $1::
|
|
888
|
+
`UPDATE ${this._fqc} SET at = GREATEST(at, $1::int) WHERE id = 0`,
|
|
843
889
|
[correlated_at]
|
|
844
890
|
);
|
|
845
891
|
const { rows } = await client.query(
|