@rotorsoft/act-pg 1.13.0 → 1.13.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.cjs CHANGED
@@ -439,16 +439,13 @@ var PostgresStore = class {
439
439
  async commit(stream, msgs, meta, expectedVersion) {
440
440
  if (msgs.length === 0) return [];
441
441
  const client = await this._client("commit");
442
- let version = -1;
443
442
  try {
444
- await client.query("BEGIN");
445
443
  const last = await client.query(
446
- `SELECT version
447
- FROM ${this._fqt}
448
- WHERE stream=$1 ORDER BY version DESC LIMIT 1`,
444
+ `SELECT version FROM ${this._fqt}
445
+ WHERE stream=$1 ORDER BY version DESC LIMIT 1`,
449
446
  [stream]
450
447
  );
451
- version = last.rowCount ? last.rows[0].version : -1;
448
+ let version = last.rows.at(0)?.version ?? -1;
452
449
  if (typeof expectedVersion === "number" && version !== expectedVersion)
453
450
  throw new import_act.ConcurrencyError(
454
451
  stream,
@@ -456,52 +453,69 @@ var PostgresStore = class {
456
453
  msgs,
457
454
  expectedVersion
458
455
  );
459
- const committed = [];
456
+ const base_version = version;
457
+ const names = [];
458
+ const datas = [];
459
+ const piis = [];
460
+ const versions = [];
460
461
  for (const { name, data, pii } of msgs) {
461
462
  version++;
462
- const sql = `
463
+ names.push(name);
464
+ datas.push(JSON.stringify(data));
465
+ piis.push(
466
+ this._resolve_pii_key && pii != null ? JSON.stringify(await (0, import_act_crypto.encrypt)(pii, this._resolve_pii_key)) : pii != null ? JSON.stringify(pii) : null
467
+ );
468
+ versions.push(version);
469
+ }
470
+ const insert_select = msgs.length === 1 ? `SELECT $1, $2::jsonb, $3::jsonb, $5, $4::int, $6 FROM l` : `SELECT u.name, u.data::jsonb, u.pii::jsonb, $5, u.version, $6
471
+ FROM l, unnest($1::text[], $2::text[], $3::text[], $4::int[])
472
+ WITH ORDINALITY AS u(name, data, pii, version, ord)
473
+ ORDER BY u.ord`;
474
+ const notify_ctes = this.config.notify ? `,
475
+ payload AS (
476
+ SELECT json_build_object(
477
+ 'stream', $5::text,
478
+ 'events', json_agg(json_build_object('id', ins.id, 'name', ins.name) ORDER BY ins.version),
479
+ 'by', $9::text
480
+ )::text AS p
481
+ FROM ins
482
+ ),
483
+ n AS (
484
+ SELECT pg_notify($8, payload.p) FROM payload
485
+ WHERE octet_length(payload.p) < $10
486
+ )` : "";
487
+ const final_select = this.config.notify ? "SELECT ins.* FROM ins LEFT JOIN n ON true ORDER BY ins.version" : "SELECT * FROM ins ORDER BY version";
488
+ const sql = `WITH l AS (SELECT pg_advisory_xact_lock(hashtext($7))),
489
+ ins AS (
463
490
  INSERT INTO ${this._fqt}(name, data, pii, stream, version, meta)
464
- VALUES($1, $2, $3, $4, $5, $6) RETURNING *`;
465
- const pii_for_write = this._resolve_pii_key && pii != null ? JSON.stringify(await (0, import_act_crypto.encrypt)(pii, this._resolve_pii_key)) : pii ?? null;
466
- const vals = [name, data, pii_for_write, stream, version, meta];
467
- try {
468
- const { rows } = await client.query(sql, vals);
469
- const row = rows.at(0);
470
- if (this._resolve_pii_key && typeof row.pii === "string") {
471
- const decrypted = await (0, import_act_crypto.decrypt)(row.pii, this._resolve_pii_key);
472
- row.pii = decrypted;
473
- }
474
- committed.push(row);
475
- } catch (error) {
476
- if (error?.code === PG_UNIQUE_VIOLATION) {
477
- throw new import_act.ConcurrencyError(
478
- stream,
479
- version - 1,
480
- msgs,
481
- expectedVersion ?? -1
482
- );
491
+ ${insert_select}
492
+ RETURNING *
493
+ )${notify_ctes}
494
+ ${final_select}`;
495
+ const base_params = msgs.length === 1 ? [names[0], datas[0], piis[0], versions[0], stream, meta, this._fqt] : [names, datas, piis, versions, stream, meta, this._fqt];
496
+ const params = this.config.notify ? [...base_params, this._channel, this._by, NOTIFY_MAX_PAYLOAD_BYTES] : base_params;
497
+ try {
498
+ const { rows } = await client.query(sql, params);
499
+ if (this._resolve_pii_key) {
500
+ for (const row of rows) {
501
+ if (typeof row.pii === "string") {
502
+ const decrypted = await (0, import_act_crypto.decrypt)(row.pii, this._resolve_pii_key);
503
+ row.pii = decrypted;
504
+ }
483
505
  }
484
- throw error;
485
506
  }
507
+ return rows;
508
+ } catch (error) {
509
+ if (error?.code === PG_UNIQUE_VIOLATION) {
510
+ throw new import_act.ConcurrencyError(
511
+ stream,
512
+ base_version,
513
+ msgs,
514
+ expectedVersion ?? -1
515
+ );
516
+ }
517
+ throw error;
486
518
  }
487
- if (this.config.notify) {
488
- const payload = JSON.stringify({
489
- stream,
490
- events: committed.map((c) => ({ id: c.id, name: c.name })),
491
- by: this._by
492
- });
493
- if (Buffer.byteLength(payload, "utf8") < NOTIFY_MAX_PAYLOAD_BYTES)
494
- await client.query(`SELECT pg_notify($1, $2)`, [
495
- this._channel,
496
- payload
497
- ]);
498
- }
499
- await client.query("COMMIT");
500
- return committed;
501
- } catch (error) {
502
- await client.query("ROLLBACK").catch(() => {
503
- });
504
- throw error;
505
519
  } finally {
506
520
  client.release();
507
521
  }
@@ -1240,6 +1254,9 @@ var PostgresStore = class {
1240
1254
  const client = await this._client("truncate");
1241
1255
  try {
1242
1256
  await client.query("BEGIN");
1257
+ await client.query("SELECT pg_advisory_xact_lock(hashtext($1))", [
1258
+ this._fqt
1259
+ ]);
1243
1260
  const result = /* @__PURE__ */ new Map();
1244
1261
  if (full.length) {
1245
1262
  await client.query(`DELETE FROM ${this._fqs} WHERE stream = ANY($1)`, [