@rotorsoft/act-pg 1.17.1 → 1.19.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
@@ -311,13 +311,36 @@ var PostgresStore = class {
311
311
  );
312
312
  await client.query(
313
313
  `CREATE TABLE IF NOT EXISTS ${this._fqc} (
314
- id int PRIMARY KEY DEFAULT 0,
314
+ key text PRIMARY KEY,
315
315
  at int NOT NULL DEFAULT -1,
316
- CONSTRAINT ${this.config.table}_correlated_singleton CHECK (id = 0)
316
+ leased_by text,
317
+ leased_until timestamptz
317
318
  ) TABLESPACE pg_default;`
318
319
  );
320
+ await client.query(`
321
+ DO $$
322
+ BEGIN
323
+ IF EXISTS (
324
+ SELECT 1 FROM information_schema.columns
325
+ WHERE table_schema = '${this.config.schema}'
326
+ AND table_name = '${this.config.table}_correlated'
327
+ AND column_name = 'id'
328
+ ) THEN
329
+ ALTER TABLE ${this._fqc} ADD COLUMN IF NOT EXISTS key text;
330
+ ALTER TABLE ${this._fqc} ADD COLUMN IF NOT EXISTS leased_by text;
331
+ ALTER TABLE ${this._fqc} ADD COLUMN IF NOT EXISTS leased_until timestamptz;
332
+ UPDATE ${this._fqc} SET key = '' WHERE key IS NULL;
333
+ ALTER TABLE ${this._fqc}
334
+ DROP CONSTRAINT IF EXISTS ${this.config.table}_correlated_singleton;
335
+ ALTER TABLE ${this._fqc}
336
+ DROP CONSTRAINT IF EXISTS ${this.config.table}_correlated_pkey;
337
+ ALTER TABLE ${this._fqc} DROP COLUMN IF EXISTS id;
338
+ ALTER TABLE ${this._fqc} ALTER COLUMN key SET NOT NULL;
339
+ ALTER TABLE ${this._fqc} ADD PRIMARY KEY (key);
340
+ END IF;
341
+ END $$;`);
319
342
  await client.query(
320
- `INSERT INTO ${this._fqc} (id) VALUES (0) ON CONFLICT (id) DO NOTHING;`
343
+ `INSERT INTO ${this._fqc} (key) VALUES ('') ON CONFLICT (key) DO NOTHING;`
321
344
  );
322
345
  await client.query(
323
346
  `ALTER TABLE ${this._fqs}
@@ -782,7 +805,7 @@ var PostgresStore = class {
782
805
  * @param streams - Streams to register with optional source.
783
806
  * @returns subscribed count and current max watermark.
784
807
  */
785
- async subscribe(streams, correlated_at) {
808
+ async subscribe(streams, correlated_at, correlator) {
786
809
  const client = await this._client("subscribe");
787
810
  try {
788
811
  await client.query("BEGIN");
@@ -819,20 +842,61 @@ var PostgresStore = class {
819
842
  [JSON.stringify(streams)]
820
843
  );
821
844
  }
822
- if (correlated_at !== void 0)
845
+ let correlating;
846
+ if (correlator) {
847
+ const { rowCount } = await client.query(
848
+ `INSERT INTO ${this._fqc} (key, at, leased_by, leased_until)
849
+ SELECT $1,
850
+ GREATEST(COALESCE($2::int, -1),
851
+ COALESCE((SELECT at FROM ${this._fqc} WHERE key = ''), -1)),
852
+ $3,
853
+ -- A non-positive millis releases outright rather than
854
+ -- setting an expiry a hair in the future, which would still
855
+ -- refuse a successor asking in the same instant.
856
+ CASE WHEN $4::int > 0
857
+ THEN now() + ($4::int * interval '1 millisecond')
858
+ ELSE NULL END
859
+ ON CONFLICT (key) DO UPDATE
860
+ SET at = GREATEST(${this._fqc}.at, COALESCE($2::int, -1)),
861
+ leased_by = CASE WHEN $4::int > 0 THEN EXCLUDED.leased_by END,
862
+ leased_until = EXCLUDED.leased_until
863
+ WHERE ${this._fqc}.leased_until IS NULL
864
+ OR ${this._fqc}.leased_until < now()
865
+ OR ${this._fqc}.leased_by = EXCLUDED.leased_by`,
866
+ [
867
+ correlator.key,
868
+ correlated_at ?? null,
869
+ correlator.by,
870
+ correlator.millis
871
+ ]
872
+ );
873
+ correlating = (rowCount ?? 0) > 0;
874
+ if (!correlating && correlated_at !== void 0)
875
+ await client.query(
876
+ `UPDATE ${this._fqc} SET at = GREATEST(at, $1::int) WHERE key = $2`,
877
+ [correlated_at, correlator.key]
878
+ );
879
+ if (correlated_at !== void 0)
880
+ await client.query(
881
+ `UPDATE ${this._fqc} SET at = GREATEST(at, $1::int) WHERE key = ''`,
882
+ [correlated_at]
883
+ );
884
+ } else if (correlated_at !== void 0)
823
885
  await client.query(
824
- `UPDATE ${this._fqc} SET at = GREATEST(at, $1::int) WHERE id = 0`,
886
+ `UPDATE ${this._fqc} SET at = GREATEST(at, $1::int) WHERE key = ''`,
825
887
  [correlated_at]
826
888
  );
827
889
  const { rows } = await client.query(
828
890
  `SELECT (SELECT COALESCE(MAX(at), -1) FROM ${this._fqs}) AS max,
829
- (SELECT at FROM ${this._fqc} WHERE id = 0) AS correlated_at`
891
+ (SELECT at FROM ${this._fqc} WHERE key = $1) AS correlated_at`,
892
+ [correlator?.key ?? ""]
830
893
  );
831
894
  await client.query("COMMIT");
832
895
  return {
833
896
  subscribed,
834
897
  watermark: rows[0]?.max ?? -1,
835
- correlated_at: Number(rows[0]?.correlated_at ?? -1)
898
+ correlated_at: Number(rows[0]?.correlated_at ?? -1),
899
+ ...correlating === void 0 ? {} : { correlating }
836
900
  };
837
901
  } catch (error) {
838
902
  await client.query("ROLLBACK").catch(() => {
@@ -1505,12 +1569,6 @@ var PostgresStore = class {
1505
1569
  this._fqt
1506
1570
  ]);
1507
1571
  const result = /* @__PURE__ */ new Map();
1508
- const retired = full.filter((t) => t.snapshot === void 0).map((t) => t.stream);
1509
- if (retired.length) {
1510
- await client.query(`DELETE FROM ${this._fqs} WHERE stream = ANY($1)`, [
1511
- retired
1512
- ]);
1513
- }
1514
1572
  for (const { stream, snapshot, meta } of full) {
1515
1573
  const { rowCount } = await client.query(
1516
1574
  `DELETE FROM ${this._fqt} WHERE stream = $1`,