@rotorsoft/act-pg 1.13.6 → 1.13.8

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
@@ -577,11 +577,17 @@ var PostgresStore = class {
577
577
  const client = await this._client("claim");
578
578
  try {
579
579
  await client.query("BEGIN");
580
- const lane_clause = lane !== void 0 ? `AND s.lane = $5` : "";
581
- const params = lane !== void 0 ? [lagging, leading, by, millis, lane] : [lagging, leading, by, millis];
580
+ const lane_clause = lane !== void 0 ? `AND s.lane = $6` : "";
581
+ const fair = lagging >= 2 ? Math.max(1, Math.floor(lagging / 4)) : 0;
582
+ const params = lane !== void 0 ? [lagging, leading, by, millis, fair, lane] : [lagging, leading, by, millis, fair];
582
583
  const { rows } = await client.query(
583
584
  `
584
585
  WITH
586
+ -- Plain read of the eligible frontier \u2014 no row lock here. A CTE
587
+ -- carrying FOR UPDATE never inlines, so locking it would materialize
588
+ -- and lock EVERY claimable stream for this transaction, starving
589
+ -- overlapping competing consumers. We only lock the small
590
+ -- lagging+leading candidate slice, down in the "locked" CTE.
585
591
  available AS (
586
592
  SELECT stream, source, at, priority, lane
587
593
  FROM ${this._fqs} s
@@ -605,17 +611,38 @@ var PostgresStore = class {
605
611
  )
606
612
  LIMIT 1
607
613
  ))
608
- FOR UPDATE SKIP LOCKED
609
614
  ),
610
615
  -- Priority lanes (ACT-102): higher priority first, then
611
616
  -- lagging-watermark order. With everyone at priority=0 the
612
617
  -- ORDER BY collapses to plain at ASC so existing workloads
613
618
  -- see no behavior change.
619
+ --
620
+ -- The lagging frontier is a UNION of two portions (ACT-1223): the
621
+ -- priority-ordered portion takes the first (lagging - fair) slots
622
+ -- by priority DESC, at ASC; a fairness reserve then fills fair more
623
+ -- slots by pure at ASC (priority ignored), excluding the ones
624
+ -- already chosen, so a default-priority lagging stream is never
625
+ -- starved out by sustained higher-priority load. With all
626
+ -- priorities equal both portions order by at, a no-op merge.
614
627
  lag AS (
615
- SELECT stream, source, at, lane, TRUE AS lagging
616
- FROM available
617
- ORDER BY priority DESC, at ASC
618
- LIMIT $1
628
+ (
629
+ SELECT stream, source, at, lane, TRUE AS lagging
630
+ FROM available
631
+ ORDER BY priority DESC, at ASC
632
+ LIMIT ($1::int - $5::int)
633
+ )
634
+ UNION
635
+ (
636
+ SELECT stream, source, at, lane, TRUE AS lagging
637
+ FROM available
638
+ WHERE stream NOT IN (
639
+ SELECT stream FROM available
640
+ ORDER BY priority DESC, at ASC
641
+ LIMIT ($1::int - $5::int)
642
+ )
643
+ ORDER BY at ASC
644
+ LIMIT $5
645
+ )
619
646
  ),
620
647
  lead AS (
621
648
  SELECT stream, source, at, lane, FALSE AS lagging
@@ -627,6 +654,20 @@ var PostgresStore = class {
627
654
  SELECT DISTINCT ON (stream) stream, source, at, lane, lagging
628
655
  FROM (SELECT * FROM lag UNION ALL SELECT * FROM lead) t
629
656
  ORDER BY stream, at
657
+ ),
658
+ -- Lock ONLY the <= lagging+leading candidate rows. The
659
+ -- lease-eligibility predicate is re-asserted here under the lock so a
660
+ -- lease acquired by a competing worker between the unlocked read and
661
+ -- this lock is never stolen. Competing workers SKIP-LOCK just this
662
+ -- slice, not the whole frontier, and claim other eligible streams.
663
+ locked AS (
664
+ SELECT s2.stream
665
+ FROM ${this._fqs} s2
666
+ WHERE s2.stream IN (SELECT stream FROM combined)
667
+ AND s2.blocked = false
668
+ AND (s2.leased_by IS NULL OR s2.leased_until <= NOW())
669
+ AND (s2.deferred_at IS NULL OR s2.deferred_at <= NOW())
670
+ FOR UPDATE OF s2 SKIP LOCKED
630
671
  )
631
672
  UPDATE ${this._fqs} s
632
673
  SET
@@ -635,6 +676,7 @@ var PostgresStore = class {
635
676
  retry = s.retry + 1
636
677
  FROM combined c
637
678
  WHERE s.stream = c.stream
679
+ AND s.stream IN (SELECT stream FROM locked)
638
680
  RETURNING s.stream, s.source, s.at, s.retry, c.lagging, s.lane
639
681
  `,
640
682
  params