@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.js CHANGED
@@ -413,16 +413,13 @@ var PostgresStore = class {
413
413
  async commit(stream, msgs, meta, expectedVersion) {
414
414
  if (msgs.length === 0) return [];
415
415
  const client = await this._client("commit");
416
- let version = -1;
417
416
  try {
418
- await client.query("BEGIN");
419
417
  const last = await client.query(
420
- `SELECT version
421
- FROM ${this._fqt}
422
- WHERE stream=$1 ORDER BY version DESC LIMIT 1`,
418
+ `SELECT version FROM ${this._fqt}
419
+ WHERE stream=$1 ORDER BY version DESC LIMIT 1`,
423
420
  [stream]
424
421
  );
425
- version = last.rowCount ? last.rows[0].version : -1;
422
+ let version = last.rows.at(0)?.version ?? -1;
426
423
  if (typeof expectedVersion === "number" && version !== expectedVersion)
427
424
  throw new ConcurrencyError(
428
425
  stream,
@@ -430,52 +427,69 @@ var PostgresStore = class {
430
427
  msgs,
431
428
  expectedVersion
432
429
  );
433
- const committed = [];
430
+ const base_version = version;
431
+ const names = [];
432
+ const datas = [];
433
+ const piis = [];
434
+ const versions = [];
434
435
  for (const { name, data, pii } of msgs) {
435
436
  version++;
436
- const sql = `
437
+ names.push(name);
438
+ datas.push(JSON.stringify(data));
439
+ piis.push(
440
+ this._resolve_pii_key && pii != null ? JSON.stringify(await encrypt(pii, this._resolve_pii_key)) : pii != null ? JSON.stringify(pii) : null
441
+ );
442
+ versions.push(version);
443
+ }
444
+ 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
445
+ FROM l, unnest($1::text[], $2::text[], $3::text[], $4::int[])
446
+ WITH ORDINALITY AS u(name, data, pii, version, ord)
447
+ ORDER BY u.ord`;
448
+ const notify_ctes = this.config.notify ? `,
449
+ payload AS (
450
+ SELECT json_build_object(
451
+ 'stream', $5::text,
452
+ 'events', json_agg(json_build_object('id', ins.id, 'name', ins.name) ORDER BY ins.version),
453
+ 'by', $9::text
454
+ )::text AS p
455
+ FROM ins
456
+ ),
457
+ n AS (
458
+ SELECT pg_notify($8, payload.p) FROM payload
459
+ WHERE octet_length(payload.p) < $10
460
+ )` : "";
461
+ 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";
462
+ const sql = `WITH l AS (SELECT pg_advisory_xact_lock(hashtext($7))),
463
+ ins AS (
437
464
  INSERT INTO ${this._fqt}(name, data, pii, stream, version, meta)
438
- VALUES($1, $2, $3, $4, $5, $6) RETURNING *`;
439
- const pii_for_write = this._resolve_pii_key && pii != null ? JSON.stringify(await encrypt(pii, this._resolve_pii_key)) : pii ?? null;
440
- const vals = [name, data, pii_for_write, stream, version, meta];
441
- try {
442
- const { rows } = await client.query(sql, vals);
443
- const row = rows.at(0);
444
- if (this._resolve_pii_key && typeof row.pii === "string") {
445
- const decrypted = await decrypt(row.pii, this._resolve_pii_key);
446
- row.pii = decrypted;
447
- }
448
- committed.push(row);
449
- } catch (error) {
450
- if (error?.code === PG_UNIQUE_VIOLATION) {
451
- throw new ConcurrencyError(
452
- stream,
453
- version - 1,
454
- msgs,
455
- expectedVersion ?? -1
456
- );
465
+ ${insert_select}
466
+ RETURNING *
467
+ )${notify_ctes}
468
+ ${final_select}`;
469
+ 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];
470
+ const params = this.config.notify ? [...base_params, this._channel, this._by, NOTIFY_MAX_PAYLOAD_BYTES] : base_params;
471
+ try {
472
+ const { rows } = await client.query(sql, params);
473
+ if (this._resolve_pii_key) {
474
+ for (const row of rows) {
475
+ if (typeof row.pii === "string") {
476
+ const decrypted = await decrypt(row.pii, this._resolve_pii_key);
477
+ row.pii = decrypted;
478
+ }
457
479
  }
458
- throw error;
459
480
  }
481
+ return rows;
482
+ } catch (error) {
483
+ if (error?.code === PG_UNIQUE_VIOLATION) {
484
+ throw new ConcurrencyError(
485
+ stream,
486
+ base_version,
487
+ msgs,
488
+ expectedVersion ?? -1
489
+ );
490
+ }
491
+ throw error;
460
492
  }
461
- if (this.config.notify) {
462
- const payload = JSON.stringify({
463
- stream,
464
- events: committed.map((c) => ({ id: c.id, name: c.name })),
465
- by: this._by
466
- });
467
- if (Buffer.byteLength(payload, "utf8") < NOTIFY_MAX_PAYLOAD_BYTES)
468
- await client.query(`SELECT pg_notify($1, $2)`, [
469
- this._channel,
470
- payload
471
- ]);
472
- }
473
- await client.query("COMMIT");
474
- return committed;
475
- } catch (error) {
476
- await client.query("ROLLBACK").catch(() => {
477
- });
478
- throw error;
479
493
  } finally {
480
494
  client.release();
481
495
  }
@@ -1214,6 +1228,9 @@ var PostgresStore = class {
1214
1228
  const client = await this._client("truncate");
1215
1229
  try {
1216
1230
  await client.query("BEGIN");
1231
+ await client.query("SELECT pg_advisory_xact_lock(hashtext($1))", [
1232
+ this._fqt
1233
+ ]);
1217
1234
  const result = /* @__PURE__ */ new Map();
1218
1235
  if (full.length) {
1219
1236
  await client.query(`DELETE FROM ${this._fqs} WHERE stream = ANY($1)`, [