@rotorsoft/act-pg 1.16.1 → 1.17.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.map +1 -1
- package/dist/index.cjs +61 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +61 -108
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
|
@@ -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,EACX,cAAc,EACf,MAAM,gBAAgB,CAAC;AAUxB,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,IAAI,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
|
@@ -40,7 +40,6 @@ var import_act = require("@rotorsoft/act");
|
|
|
40
40
|
var import_act_crypto = require("@rotorsoft/act-crypto");
|
|
41
41
|
var import_pg = __toESM(require("pg"), 1);
|
|
42
42
|
var logger = (0, import_act.log)();
|
|
43
|
-
var SOURCE_METACHARACTER_CLASS = "[]^$.*+?()[{}|\\\\]";
|
|
44
43
|
var { Pool, types } = import_pg.default;
|
|
45
44
|
var JSONB_OID = types.builtins.JSONB;
|
|
46
45
|
var scopedTypes = {
|
|
@@ -360,6 +359,11 @@ var PostgresStore = class {
|
|
|
360
359
|
`ALTER TABLE ${this._fqs}
|
|
361
360
|
ADD COLUMN IF NOT EXISTS correlated_at int;`
|
|
362
361
|
);
|
|
362
|
+
await client.query(
|
|
363
|
+
`UPDATE ${this._fqs}
|
|
364
|
+
SET correlated_at = (SELECT COALESCE(MAX(id), -1) FROM ${this._fqt})
|
|
365
|
+
WHERE correlated_at IS NULL;`
|
|
366
|
+
);
|
|
363
367
|
await client.query(
|
|
364
368
|
`DO $$
|
|
365
369
|
BEGIN
|
|
@@ -413,6 +417,11 @@ var PostgresStore = class {
|
|
|
413
417
|
ON ${this._fqs} (lane, priority DESC, at)
|
|
414
418
|
WHERE blocked = false AND at < correlated_at;`
|
|
415
419
|
);
|
|
420
|
+
await client.query(
|
|
421
|
+
`CREATE INDEX IF NOT EXISTS "${this.config.table}_streams_at_ix"
|
|
422
|
+
ON ${this._fqs} (at)
|
|
423
|
+
WHERE blocked = false AND at < correlated_at;`
|
|
424
|
+
);
|
|
416
425
|
await client.query("COMMIT");
|
|
417
426
|
logger.info(
|
|
418
427
|
`Seeded schema "${this.config.schema}" with table "${this.config.table}"`
|
|
@@ -672,129 +681,73 @@ var PostgresStore = class {
|
|
|
672
681
|
-- and lock EVERY claimable stream for this transaction, starving
|
|
673
682
|
-- overlapping competing consumers. We only lock the small
|
|
674
683
|
-- lagging+leading candidate slice, down in the "locked" CTE.
|
|
675
|
-
-- Eligibility, split by SOURCE CLASS rather than expressed as one
|
|
676
|
-
-- disjunction (#1448). The three classes are mutually exclusive, so
|
|
677
|
-
-- the UNION ALL returns exactly what the old OR-chain did \u2014 but each
|
|
678
|
-
-- arm now carries a single, planner-legible predicate.
|
|
679
684
|
--
|
|
680
|
-
--
|
|
681
|
-
--
|
|
682
|
-
--
|
|
683
|
-
--
|
|
684
|
-
--
|
|
685
|
-
--
|
|
686
|
-
--
|
|
687
|
-
--
|
|
685
|
+
-- Eligibility is a pure subscription-table predicate (#1488).
|
|
686
|
+
-- claim does not read the event log at all: correlate marks the
|
|
687
|
+
-- highest event id that resolves to a target, and at <
|
|
688
|
+
-- correlated_at is the whole question. The probe this replaced ran
|
|
689
|
+
-- an EXISTS against the events table once per eligible row, which
|
|
690
|
+
-- cost O(subscribed streams) per claim per worker no matter how
|
|
691
|
+
-- little work was pending.
|
|
692
|
+
--
|
|
693
|
+
-- The predicate is repeated in each arm below rather than factored
|
|
694
|
+
-- into a shared CTE, and that repetition is load-bearing (#1510). A
|
|
695
|
+
-- CTE referenced more than once is materialized, so LIMIT 8 was
|
|
696
|
+
-- applied to a fully-built 100,000-row result instead of pushing into
|
|
697
|
+
-- the index: measured at 75.6 ms for a claim that returns 8 rows.
|
|
698
|
+
-- Reading the base table in each arm lets the planner stop after the
|
|
699
|
+
-- limit \u2014 19.9 ms with the existing index, 2.0 ms once the
|
|
700
|
+
-- at-ordered partial index below serves the two watermark arms.
|
|
688
701
|
--
|
|
689
|
-
--
|
|
690
|
-
--
|
|
691
|
-
|
|
692
|
-
|
|
702
|
+
-- A row with no mark compares unknown and is excluded by SQL's own
|
|
703
|
+
-- rules. That is definitional (#1446): a subscription is claimable iff
|
|
704
|
+
-- a mark says so. seed() marks rows that predate the column.
|
|
705
|
+
--
|
|
706
|
+
-- Priority lanes (ACT-102): higher priority first, then
|
|
707
|
+
-- lagging-watermark order. With everyone at priority=0 the ORDER BY
|
|
708
|
+
-- collapses to plain at ASC, so existing workloads see no change.
|
|
709
|
+
--
|
|
710
|
+
-- The lagging frontier is a UNION of two portions (ACT-1223): the
|
|
711
|
+
-- priority portion takes the first (lagging - fair) slots by
|
|
712
|
+
-- priority DESC, at ASC; a fairness reserve then fills fair more
|
|
713
|
+
-- by pure at ASC (priority ignored), excluding the ones already
|
|
714
|
+
-- chosen, so a default-priority lagging stream is never starved out
|
|
715
|
+
-- by sustained higher-priority load.
|
|
716
|
+
prio AS (
|
|
717
|
+
SELECT stream, source, at, lane, TRUE AS lagging
|
|
693
718
|
FROM ${this._fqs} s
|
|
694
|
-
WHERE blocked = false
|
|
719
|
+
WHERE s.blocked = false
|
|
720
|
+
AND s.at < s.correlated_at
|
|
695
721
|
${lane_clause}
|
|
696
|
-
AND (leased_by IS NULL OR leased_until <= NOW())
|
|
697
|
-
AND (deferred_at IS NULL OR deferred_at <= NOW())
|
|
722
|
+
AND (s.leased_by IS NULL OR s.leased_until <= NOW())
|
|
723
|
+
AND (s.deferred_at IS NULL OR s.deferred_at <= NOW())
|
|
724
|
+
ORDER BY s.priority DESC, s.at ASC
|
|
725
|
+
LIMIT ($1::int - $5::int)
|
|
698
726
|
),
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
-- row alone. Read from the base table, NOT from the eligible CTE:
|
|
702
|
-
-- it is referenced several times so PG materializes it, and a
|
|
703
|
-
-- materialized scan cannot use the partial index this predicate was
|
|
704
|
-
-- built for (WHERE blocked = false AND at < correlated_at).
|
|
705
|
-
-- The comparison is NULL-safe: an unmarked row compares
|
|
706
|
-
-- unknown and is excluded here, falling through to the legacy arms.
|
|
707
|
-
SELECT stream, source, at, priority, lane
|
|
727
|
+
fair AS (
|
|
728
|
+
SELECT stream, source, at, lane, TRUE AS lagging
|
|
708
729
|
FROM ${this._fqs} s
|
|
709
730
|
WHERE s.blocked = false
|
|
710
731
|
AND s.at < s.correlated_at
|
|
711
732
|
${lane_clause}
|
|
712
733
|
AND (s.leased_by IS NULL OR s.leased_until <= NOW())
|
|
713
734
|
AND (s.deferred_at IS NULL OR s.deferred_at <= NOW())
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
-- as it did before, and each row migrates to the fast arm the first
|
|
718
|
-
-- time correlate marks it. Deleted once correlate marks universally.
|
|
719
|
-
--
|
|
720
|
-
-- Every arm below is watermark-agnostic (#1446): a fresh
|
|
721
|
-
-- subscription sits at -1, and e.id > s.at already answers
|
|
722
|
-
-- correctly there, since the first event has a greater id.
|
|
723
|
-
-- There used to be a fourth arm claiming at < 0 unconditionally,
|
|
724
|
-
-- which handed out an empty lease and a no-op ack for every
|
|
725
|
-
-- subscription with no matching events yet.
|
|
726
|
-
-- Source-less subscription: any non-snapshot event past the
|
|
727
|
-
-- watermark counts, so the id index alone answers it.
|
|
728
|
-
SELECT stream, source, at, priority, lane FROM eligible s
|
|
729
|
-
WHERE s.correlated_at IS NULL
|
|
730
|
-
AND s.source IS NULL
|
|
731
|
-
AND EXISTS (
|
|
732
|
-
SELECT 1 FROM ${this._fqt} e
|
|
733
|
-
WHERE e.id > s.at AND e.name <> '${import_act.SNAP_EVENT}' LIMIT 1
|
|
734
|
-
)
|
|
735
|
-
UNION ALL
|
|
736
|
-
-- Literal source (no regex metacharacter): exact equality, so
|
|
737
|
-
-- "s1" never claims "s12", and (stream, id) is usable.
|
|
738
|
-
SELECT stream, source, at, priority, lane FROM eligible s
|
|
739
|
-
WHERE s.correlated_at IS NULL
|
|
740
|
-
AND s.source IS NOT NULL
|
|
741
|
-
AND s.source !~ '${SOURCE_METACHARACTER_CLASS}'
|
|
742
|
-
AND EXISTS (
|
|
743
|
-
SELECT 1 FROM ${this._fqt} e
|
|
744
|
-
WHERE e.stream = s.source AND e.id > s.at
|
|
745
|
-
AND e.name <> '${import_act.SNAP_EVENT}' LIMIT 1
|
|
746
|
-
)
|
|
747
|
-
UNION ALL
|
|
748
|
-
-- Pattern source (e.g. '^(A|B)$'): POSIX match, so the
|
|
749
|
-
-- calculator's static regex reaction is claimed for every stream
|
|
750
|
-
-- it anchors. Unavoidably a scan \u2014 but only for the subscriptions
|
|
751
|
-
-- that actually declare a pattern.
|
|
752
|
-
SELECT stream, source, at, priority, lane FROM eligible s
|
|
753
|
-
WHERE s.correlated_at IS NULL
|
|
754
|
-
AND s.source IS NOT NULL
|
|
755
|
-
AND s.source ~ '${SOURCE_METACHARACTER_CLASS}'
|
|
756
|
-
AND EXISTS (
|
|
757
|
-
SELECT 1 FROM ${this._fqt} e
|
|
758
|
-
WHERE e.id > s.at AND e.name <> '${import_act.SNAP_EVENT}'
|
|
759
|
-
AND e.stream ~ s.source LIMIT 1
|
|
760
|
-
)
|
|
735
|
+
AND s.stream NOT IN (SELECT stream FROM prio)
|
|
736
|
+
ORDER BY s.at ASC
|
|
737
|
+
LIMIT $5
|
|
761
738
|
),
|
|
762
|
-
-- Priority lanes (ACT-102): higher priority first, then
|
|
763
|
-
-- lagging-watermark order. With everyone at priority=0 the
|
|
764
|
-
-- ORDER BY collapses to plain at ASC so existing workloads
|
|
765
|
-
-- see no behavior change.
|
|
766
|
-
--
|
|
767
|
-
-- The lagging frontier is a UNION of two portions (ACT-1223): the
|
|
768
|
-
-- priority-ordered portion takes the first (lagging - fair) slots
|
|
769
|
-
-- by priority DESC, at ASC; a fairness reserve then fills fair more
|
|
770
|
-
-- slots by pure at ASC (priority ignored), excluding the ones
|
|
771
|
-
-- already chosen, so a default-priority lagging stream is never
|
|
772
|
-
-- starved out by sustained higher-priority load. With all
|
|
773
|
-
-- priorities equal both portions order by at, a no-op merge.
|
|
774
739
|
lag AS (
|
|
775
|
-
|
|
776
|
-
SELECT stream, source, at, lane, TRUE AS lagging
|
|
777
|
-
FROM available
|
|
778
|
-
ORDER BY priority DESC, at ASC
|
|
779
|
-
LIMIT ($1::int - $5::int)
|
|
780
|
-
)
|
|
781
|
-
UNION
|
|
782
|
-
(
|
|
783
|
-
SELECT stream, source, at, lane, TRUE AS lagging
|
|
784
|
-
FROM available
|
|
785
|
-
WHERE stream NOT IN (
|
|
786
|
-
SELECT stream FROM available
|
|
787
|
-
ORDER BY priority DESC, at ASC
|
|
788
|
-
LIMIT ($1::int - $5::int)
|
|
789
|
-
)
|
|
790
|
-
ORDER BY at ASC
|
|
791
|
-
LIMIT $5
|
|
792
|
-
)
|
|
740
|
+
SELECT * FROM prio UNION SELECT * FROM fair
|
|
793
741
|
),
|
|
794
742
|
lead AS (
|
|
795
743
|
SELECT stream, source, at, lane, FALSE AS lagging
|
|
796
|
-
FROM
|
|
797
|
-
|
|
744
|
+
FROM ${this._fqs} s
|
|
745
|
+
WHERE s.blocked = false
|
|
746
|
+
AND s.at < s.correlated_at
|
|
747
|
+
${lane_clause}
|
|
748
|
+
AND (s.leased_by IS NULL OR s.leased_until <= NOW())
|
|
749
|
+
AND (s.deferred_at IS NULL OR s.deferred_at <= NOW())
|
|
750
|
+
ORDER BY s.at DESC
|
|
798
751
|
LIMIT $2
|
|
799
752
|
),
|
|
800
753
|
combined AS (
|