@mastra/pg 1.36.0-alpha.4 → 1.36.0-alpha.6

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
@@ -2066,8 +2066,10 @@ function truncateQuery(query, maxLength = 100) {
2066
2066
  */
2067
2067
  var PoolAdapter = class {
2068
2068
  $pool;
2069
- constructor($pool) {
2069
+ onRollbackError;
2070
+ constructor($pool, onRollbackError) {
2070
2071
  this.$pool = $pool;
2072
+ this.onRollbackError = onRollbackError;
2071
2073
  }
2072
2074
  connect() {
2073
2075
  return this.$pool.connect();
@@ -2104,8 +2106,14 @@ var PoolAdapter = class {
2104
2106
  }
2105
2107
  async tx(callback) {
2106
2108
  const client = await this.$pool.connect();
2109
+ let destroyClient = false;
2107
2110
  try {
2108
- await client.query("BEGIN");
2111
+ try {
2112
+ await client.query("BEGIN");
2113
+ } catch (error) {
2114
+ destroyClient = true;
2115
+ throw error;
2116
+ }
2109
2117
  const txClient = new TransactionClient(client);
2110
2118
  try {
2111
2119
  const result = await callback(txClient);
@@ -2117,12 +2125,16 @@ var PoolAdapter = class {
2117
2125
  try {
2118
2126
  await client.query("ROLLBACK");
2119
2127
  } catch (rollbackError) {
2120
- console.error("Transaction rollback failed:", rollbackError);
2128
+ destroyClient = true;
2129
+ try {
2130
+ if (this.onRollbackError) await this.onRollbackError(rollbackError);
2131
+ else console.error("Failed to rollback transaction:", rollbackError);
2132
+ } catch {}
2121
2133
  }
2122
2134
  throw error;
2123
2135
  }
2124
2136
  } finally {
2125
- client.release();
2137
+ client.release(destroyClient || void 0);
2126
2138
  }
2127
2139
  }
2128
2140
  };
@@ -2229,6 +2241,7 @@ var TransactionClient = class {
2229
2241
  var PinnedClientAdapter = class {
2230
2242
  $pool;
2231
2243
  pinnedClient;
2244
+ onRollbackError;
2232
2245
  /**
2233
2246
  * Serialization tail. Domain init() methods fire via Promise.all in
2234
2247
  * MastraCompositeStore.#runInit(), so without our own gate every domain's
@@ -2239,9 +2252,14 @@ var PinnedClientAdapter = class {
2239
2252
  * the reliance on pg's internal queue.
2240
2253
  */
2241
2254
  #tail = Promise.resolve();
2242
- constructor($pool, pinnedClient) {
2255
+ #rollbackError;
2256
+ constructor($pool, pinnedClient, onRollbackError) {
2243
2257
  this.$pool = $pool;
2244
2258
  this.pinnedClient = pinnedClient;
2259
+ this.onRollbackError = onRollbackError;
2260
+ }
2261
+ get shouldDestroyClient() {
2262
+ return this.#rollbackError !== void 0;
2245
2263
  }
2246
2264
  /**
2247
2265
  * Run `fn` after any previously enqueued work on this pinned client has
@@ -2249,7 +2267,11 @@ var PinnedClientAdapter = class {
2249
2267
  * resume on the next caller's turn.
2250
2268
  */
2251
2269
  #enqueue(fn) {
2252
- const next = this.#tail.then(fn, fn);
2270
+ const run = async () => {
2271
+ if (this.shouldDestroyClient) throw this.#rollbackError;
2272
+ return fn();
2273
+ };
2274
+ const next = this.#tail.then(run, run);
2253
2275
  this.#tail = next.catch(() => void 0);
2254
2276
  return next;
2255
2277
  }
@@ -2301,7 +2323,12 @@ var PinnedClientAdapter = class {
2301
2323
  }
2302
2324
  tx(callback) {
2303
2325
  return this.#enqueue(async () => {
2304
- await this.pinnedClient.query("BEGIN");
2326
+ try {
2327
+ await this.pinnedClient.query("BEGIN");
2328
+ } catch (error) {
2329
+ this.#rollbackError = error;
2330
+ throw error;
2331
+ }
2305
2332
  const txClient = new TransactionClient(this.pinnedClient);
2306
2333
  try {
2307
2334
  const result = await callback(txClient);
@@ -2313,7 +2340,11 @@ var PinnedClientAdapter = class {
2313
2340
  try {
2314
2341
  await this.pinnedClient.query("ROLLBACK");
2315
2342
  } catch (rollbackError) {
2316
- console.error("Transaction rollback failed:", rollbackError);
2343
+ this.#rollbackError = rollbackError;
2344
+ try {
2345
+ if (this.onRollbackError) await this.onRollbackError(rollbackError);
2346
+ else console.error("Failed to rollback transaction:", rollbackError);
2347
+ } catch {}
2317
2348
  }
2318
2349
  throw error;
2319
2350
  }
@@ -19368,13 +19399,13 @@ const timingFeatures = [
19368
19399
  * condition for rows level with it. Empty values sort last in both directions, so they are past
19369
19400
  * every value and level only with each other.
19370
19401
  */
19371
- function keysetSortBounds(field, direction, sortValue, values) {
19402
+ function keysetSortBounds(field, direction, sortValue, values, stringSort = false) {
19372
19403
  if (sortValue === null) return {
19373
19404
  past: [],
19374
19405
  level: `${field} IS NULL`
19375
19406
  };
19376
19407
  values.push(sortValue);
19377
- const parameter = `$${values.length}::${typeof sortValue === "number" ? "float8" : "timestamptz"}`;
19408
+ const parameter = `$${values.length}::${stringSort ? "text COLLATE \"C\"" : typeof sortValue === "number" ? "float8" : "timestamptz"}`;
19378
19409
  return {
19379
19410
  past: [`${field} ${direction === "asc" ? ">" : "<"} ${parameter}`, `${field} IS NULL`],
19380
19411
  level: `${field} = ${parameter}`
@@ -20422,10 +20453,14 @@ function selectedKeys(rows, values) {
20422
20453
  return `"${field}" ${field === "traceId" || field === "spanId" ? "=" : "IS NOT DISTINCT FROM"} $${values.length}::text`;
20423
20454
  }).join(" AND ")})`).join(" OR ");
20424
20455
  }
20456
+ function sortColumn(plan) {
20457
+ const column = `s."${plan.orderBy.field}"`;
20458
+ return plan.orderBy.field === "model" || plan.orderBy.field === "provider" ? `${column} COLLATE "C"` : column;
20459
+ }
20425
20460
  function afterCursor(plan, values) {
20426
20461
  if (!plan.cursor) return "TRUE";
20427
20462
  const cursor = plan.cursor;
20428
- const { past: terms, level } = keysetSortBounds(`s."${plan.orderBy.field}"`, plan.orderBy.direction, cursor.sortValue, values);
20463
+ const { past: terms, level } = keysetSortBounds(sortColumn(plan), plan.orderBy.direction, cursor.sortValue, values, plan.orderBy.field === "model" || plan.orderBy.field === "provider");
20429
20464
  const equal = [level];
20430
20465
  for (const key of identityFields) {
20431
20466
  const ref = `s."${key}" COLLATE "C"`;
@@ -20506,10 +20541,12 @@ function compilePostgresSpanQuery(schema, plan) {
20506
20541
  "endedAt",
20507
20542
  "durationMs",
20508
20543
  "status",
20509
- "version"
20544
+ "version",
20545
+ "model",
20546
+ "provider"
20510
20547
  ].map((field) => `s."${field}"`).join(", ")}
20511
20548
  FROM selected s WHERE ${after}
20512
- ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, ${identityOrder}
20549
+ ORDER BY ${sortColumn(plan)} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, ${identityOrder}
20513
20550
  LIMIT $${values.length}`,
20514
20551
  values
20515
20552
  };
@@ -20544,6 +20581,7 @@ async function querySpans(client, schema, plan, timeoutMs) {
20544
20581
  const rows = await tx.any(query.text, query.values);
20545
20582
  const selected = rows.map(({ version: _version, startedAt, endedAt, timeToFirstChunkMs, timeToFirstTokenMs, outcome, stopReason, ...row }) => ({
20546
20583
  ...row,
20584
+ ...(plan.orderBy.field === "model" || plan.orderBy.field === "provider") && { cursorSortValue: row[plan.orderBy.field] },
20547
20585
  ...timeToFirstChunkSupported && { timeToFirstChunkMs },
20548
20586
  ...timeToFirstTokenSupported && { timeToFirstTokenMs },
20549
20587
  ...spanQueryOutcomeFieldsSupported && {
@@ -21939,6 +21977,7 @@ const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? [
21939
21977
  "span-query",
21940
21978
  "span-query-context-fields",
21941
21979
  ...coreStorage.SPAN_QUERY_ORDER_FIELDS?.includes("durationMs") ? ["span-query-duration-sort"] : [],
21980
+ ...coreStorage.SPAN_QUERY_ORDER_FIELDS?.includes("model") && coreStorage.SPAN_QUERY_ORDER_FIELDS?.includes("provider") ? ["span-query-model-provider-sort"] : [],
21942
21981
  ..."model" in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? ["span-query-model-fields"] : []
21943
21982
  ] : [];
21944
21983
  const spanAggregateFeatures = typeof coreStorage.planSpanAggregate === "function" ? ["span-aggregate", ..."model" in (coreStorage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {}) ? ["span-aggregate-model-dimensions"] : []] : [];
@@ -28101,18 +28140,29 @@ var PgFactoryStorageOps = class {
28101
28140
  };
28102
28141
  if (this.#transactionClient) return run(this.#transactionClient);
28103
28142
  const client = await this.#queryable.connect();
28143
+ let releaseError;
28104
28144
  try {
28105
- await client.query("BEGIN");
28145
+ try {
28146
+ await client.query("BEGIN");
28147
+ } catch (error) {
28148
+ releaseError = error instanceof Error ? error : new Error(String(error));
28149
+ throw error;
28150
+ }
28106
28151
  try {
28107
28152
  const result = await run(client);
28108
28153
  await client.query("COMMIT");
28109
28154
  return result;
28110
28155
  } catch (error) {
28111
- await client.query("ROLLBACK");
28156
+ try {
28157
+ await client.query("ROLLBACK");
28158
+ } catch (rollbackError) {
28159
+ releaseError = rollbackError instanceof Error ? rollbackError : new Error(String(rollbackError));
28160
+ throw new AggregateError([error, rollbackError], "Factory transaction and rollback both failed");
28161
+ }
28112
28162
  throw error;
28113
28163
  }
28114
28164
  } finally {
28115
- client.release();
28165
+ client.release(releaseError);
28116
28166
  }
28117
28167
  }
28118
28168
  };
@@ -28169,7 +28219,12 @@ var PgFactoryStorage = class extends FactoryStorage {
28169
28219
  let transactionOpen = false;
28170
28220
  let releaseError;
28171
28221
  try {
28172
- await client.query(options?.isolationLevel === "serializable" ? "BEGIN ISOLATION LEVEL SERIALIZABLE" : "BEGIN");
28222
+ try {
28223
+ await client.query(options?.isolationLevel === "serializable" ? "BEGIN ISOLATION LEVEL SERIALIZABLE" : "BEGIN");
28224
+ } catch (error) {
28225
+ releaseError = error instanceof Error ? error : new Error(String(error));
28226
+ throw error;
28227
+ }
28173
28228
  transactionOpen = true;
28174
28229
  const result = await fn(new PgFactoryStorageOps(client, this.#schemas, client));
28175
28230
  await client.query("COMMIT");
@@ -28371,8 +28426,9 @@ var PostgresStore = class extends MastraCompositeStore {
28371
28426
  this.#ownsWritePool = true;
28372
28427
  }
28373
28428
  this.#readPool = config.readPool ?? this.#writePool;
28374
- this.#db = new RoutingDbClient(new PoolAdapter(this.#writePool));
28375
- this.#readDb = this.#readPool === this.#writePool ? this.#db : new PoolAdapter(this.#readPool);
28429
+ const onRollbackError = (error) => this.logger?.error?.("Transaction rollback failed", { error });
28430
+ this.#db = new RoutingDbClient(new PoolAdapter(this.#writePool, onRollbackError));
28431
+ this.#readDb = this.#readPool === this.#writePool ? this.#db : new PoolAdapter(this.#readPool, onRollbackError);
28376
28432
  const domainConfig = {
28377
28433
  client: this.#db,
28378
28434
  readClient: this.#readDb,
@@ -28441,7 +28497,7 @@ var PostgresStore = class extends MastraCompositeStore {
28441
28497
  let pinned;
28442
28498
  try {
28443
28499
  pinnedClient = await this.#writePool.connect();
28444
- pinned = new PinnedClientAdapter(this.#writePool, pinnedClient);
28500
+ pinned = new PinnedClientAdapter(this.#writePool, pinnedClient, (error) => this.logger?.error?.("Transaction rollback failed", { error }));
28445
28501
  this.#db.pin(pinned);
28446
28502
  for (let attempt = 1; attempt <= 3; attempt++) {
28447
28503
  this.#db.setSchemaSnapshot(await loadSchemaSnapshot(pinned, this.schema));
@@ -28466,7 +28522,7 @@ var PostgresStore = class extends MastraCompositeStore {
28466
28522
  this.#db.setSchemaSnapshot(null);
28467
28523
  if (pinnedClient) {
28468
28524
  this.#db.unpin();
28469
- pinnedClient.release();
28525
+ pinnedClient.release(pinned?.shouldDestroyClient || void 0);
28470
28526
  }
28471
28527
  }
28472
28528
  }