@rotorsoft/act-pg 1.14.0 → 1.15.0

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/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 bigint NOT NULL DEFAULT -1,
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.source IS NULL
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.source IS NOT NULL
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.source IS NOT NULL
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::bigint) WHERE id = 0`,
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(