@rotorsoft/act-pg 1.18.0 → 1.19.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/index.js CHANGED
@@ -2,7 +2,6 @@
2
2
  import { randomUUID } from "crypto";
3
3
  import {
4
4
  ConcurrencyError,
5
- dateReviver,
6
5
  log,
7
6
  SNAP_EVENT,
8
7
  StoreError,
@@ -19,7 +18,7 @@ var logger = log();
19
18
  var { Pool, types } = pg;
20
19
  var JSONB_OID = types.builtins.JSONB;
21
20
  var scopedTypes = {
22
- getTypeParser: ((oid, format) => oid === JSONB_OID ? (val) => JSON.parse(val, dateReviver) : types.getTypeParser(
21
+ getTypeParser: ((oid, format) => oid === JSONB_OID ? (val) => JSON.parse(val) : types.getTypeParser(
23
22
  oid,
24
23
  format
25
24
  ))
@@ -311,13 +310,36 @@ var PostgresStore = class {
311
310
  );
312
311
  await client.query(
313
312
  `CREATE TABLE IF NOT EXISTS ${this._fqc} (
314
- id int PRIMARY KEY DEFAULT 0,
313
+ key text PRIMARY KEY,
315
314
  at int NOT NULL DEFAULT -1,
316
- CONSTRAINT ${this.config.table}_correlated_singleton CHECK (id = 0)
315
+ leased_by text,
316
+ leased_until timestamptz
317
317
  ) TABLESPACE pg_default;`
318
318
  );
319
+ await client.query(`
320
+ DO $$
321
+ BEGIN
322
+ IF EXISTS (
323
+ SELECT 1 FROM information_schema.columns
324
+ WHERE table_schema = '${this.config.schema}'
325
+ AND table_name = '${this.config.table}_correlated'
326
+ AND column_name = 'id'
327
+ ) THEN
328
+ ALTER TABLE ${this._fqc} ADD COLUMN IF NOT EXISTS key text;
329
+ ALTER TABLE ${this._fqc} ADD COLUMN IF NOT EXISTS leased_by text;
330
+ ALTER TABLE ${this._fqc} ADD COLUMN IF NOT EXISTS leased_until timestamptz;
331
+ UPDATE ${this._fqc} SET key = '' WHERE key IS NULL;
332
+ ALTER TABLE ${this._fqc}
333
+ DROP CONSTRAINT IF EXISTS ${this.config.table}_correlated_singleton;
334
+ ALTER TABLE ${this._fqc}
335
+ DROP CONSTRAINT IF EXISTS ${this.config.table}_correlated_pkey;
336
+ ALTER TABLE ${this._fqc} DROP COLUMN IF EXISTS id;
337
+ ALTER TABLE ${this._fqc} ALTER COLUMN key SET NOT NULL;
338
+ ALTER TABLE ${this._fqc} ADD PRIMARY KEY (key);
339
+ END IF;
340
+ END $$;`);
319
341
  await client.query(
320
- `INSERT INTO ${this._fqc} (id) VALUES (0) ON CONFLICT (id) DO NOTHING;`
342
+ `INSERT INTO ${this._fqc} (key) VALUES ('') ON CONFLICT (key) DO NOTHING;`
321
343
  );
322
344
  await client.query(
323
345
  `ALTER TABLE ${this._fqs}
@@ -512,11 +534,7 @@ var PostgresStore = class {
512
534
  const result = await this._pool.query(sql, values);
513
535
  for (const row of result.rows) {
514
536
  if (this._resolve_pii_key && typeof row.pii === "string") {
515
- const decrypted = await decrypt(
516
- row.pii,
517
- this._resolve_pii_key,
518
- dateReviver
519
- );
537
+ const decrypted = await decrypt(row.pii, this._resolve_pii_key);
520
538
  row.pii = decrypted;
521
539
  }
522
540
  await Promise.resolve(callback(row));
@@ -596,11 +614,7 @@ var PostgresStore = class {
596
614
  if (this._resolve_pii_key) {
597
615
  for (const row of rows) {
598
616
  if (typeof row.pii === "string") {
599
- const decrypted = await decrypt(
600
- row.pii,
601
- this._resolve_pii_key,
602
- dateReviver
603
- );
617
+ const decrypted = await decrypt(row.pii, this._resolve_pii_key);
604
618
  row.pii = decrypted;
605
619
  }
606
620
  }
@@ -782,7 +796,7 @@ var PostgresStore = class {
782
796
  * @param streams - Streams to register with optional source.
783
797
  * @returns subscribed count and current max watermark.
784
798
  */
785
- async subscribe(streams, correlated_at) {
799
+ async subscribe(streams, correlated_at, correlator) {
786
800
  const client = await this._client("subscribe");
787
801
  try {
788
802
  await client.query("BEGIN");
@@ -819,20 +833,61 @@ var PostgresStore = class {
819
833
  [JSON.stringify(streams)]
820
834
  );
821
835
  }
822
- if (correlated_at !== void 0)
836
+ let correlating;
837
+ if (correlator) {
838
+ const { rowCount } = await client.query(
839
+ `INSERT INTO ${this._fqc} (key, at, leased_by, leased_until)
840
+ SELECT $1,
841
+ GREATEST(COALESCE($2::int, -1),
842
+ COALESCE((SELECT at FROM ${this._fqc} WHERE key = ''), -1)),
843
+ $3,
844
+ -- A non-positive millis releases outright rather than
845
+ -- setting an expiry a hair in the future, which would still
846
+ -- refuse a successor asking in the same instant.
847
+ CASE WHEN $4::int > 0
848
+ THEN now() + ($4::int * interval '1 millisecond')
849
+ ELSE NULL END
850
+ ON CONFLICT (key) DO UPDATE
851
+ SET at = GREATEST(${this._fqc}.at, COALESCE($2::int, -1)),
852
+ leased_by = CASE WHEN $4::int > 0 THEN EXCLUDED.leased_by END,
853
+ leased_until = EXCLUDED.leased_until
854
+ WHERE ${this._fqc}.leased_until IS NULL
855
+ OR ${this._fqc}.leased_until < now()
856
+ OR ${this._fqc}.leased_by = EXCLUDED.leased_by`,
857
+ [
858
+ correlator.key,
859
+ correlated_at ?? null,
860
+ correlator.by,
861
+ correlator.millis
862
+ ]
863
+ );
864
+ correlating = (rowCount ?? 0) > 0;
865
+ if (!correlating && correlated_at !== void 0)
866
+ await client.query(
867
+ `UPDATE ${this._fqc} SET at = GREATEST(at, $1::int) WHERE key = $2`,
868
+ [correlated_at, correlator.key]
869
+ );
870
+ if (correlated_at !== void 0)
871
+ await client.query(
872
+ `UPDATE ${this._fqc} SET at = GREATEST(at, $1::int) WHERE key = ''`,
873
+ [correlated_at]
874
+ );
875
+ } else if (correlated_at !== void 0)
823
876
  await client.query(
824
- `UPDATE ${this._fqc} SET at = GREATEST(at, $1::int) WHERE id = 0`,
877
+ `UPDATE ${this._fqc} SET at = GREATEST(at, $1::int) WHERE key = ''`,
825
878
  [correlated_at]
826
879
  );
827
880
  const { rows } = await client.query(
828
881
  `SELECT (SELECT COALESCE(MAX(at), -1) FROM ${this._fqs}) AS max,
829
- (SELECT at FROM ${this._fqc} WHERE id = 0) AS correlated_at`
882
+ (SELECT at FROM ${this._fqc} WHERE key = $1) AS correlated_at`,
883
+ [correlator?.key ?? ""]
830
884
  );
831
885
  await client.query("COMMIT");
832
886
  return {
833
887
  subscribed,
834
888
  watermark: rows[0]?.max ?? -1,
835
- correlated_at: Number(rows[0]?.correlated_at ?? -1)
889
+ correlated_at: Number(rows[0]?.correlated_at ?? -1),
890
+ ...correlating === void 0 ? {} : { correlating }
836
891
  };
837
892
  } catch (error) {
838
893
  await client.query("ROLLBACK").catch(() => {