@mastra/pg 1.20.1-alpha.1 → 1.20.1-alpha.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @mastra/pg
2
2
 
3
+ ## 1.20.1-alpha.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Stop a dropped Postgres connection from killing the process while a client is checked out. ([#21765](https://github.com/mastra-ai/mastra/pull/21765))
8
+
9
+ `PgFactoryStorage` attached an `error` listener to the pool, but pg only routes pool-level errors for _idle_ clients — it hands ownership of a client to the borrower for the duration of a checkout. A backend restart or network blip that landed on a client mid-transaction therefore reached an emitter with nothing listening, and Node escalated it to an uncaughtException that took the whole server down (`Connection terminated unexpectedly` at `pg/lib/client.js`), even though the idle siblings were logged and discarded cleanly.
10
+
11
+ Pools created by `PgFactoryStorage` now attach a listener once per physical connection as it is established, so a client stays covered while borrowed. While the client is idle the pool's own listener already reports the failure, so the extra listener stays quiet and a dropped connection is announced once, as the right thing. The pool still discards the failed connection and reconnects on the next checkout; the failure is now logged instead of fatal. Caller-supplied pools are left untouched, as before.
12
+
13
+ - Updated dependencies [[`6db7a5d`](https://github.com/mastra-ai/mastra/commit/6db7a5dd3dd2b6f7ef75dcd804fcffef5fa83963), [`0cdc5dc`](https://github.com/mastra-ai/mastra/commit/0cdc5dc69024957815da4f51acc4119eb4f447d7)]:
14
+ - @mastra/core@1.60.0-alpha.12
15
+
16
+ ## 1.20.1-alpha.2
17
+
18
+ ### Patch Changes
19
+
20
+ - Added a `durable` option to stored agents so agents created through the Agents API can run with durable execution — no code deployment required. ([#21715](https://github.com/mastra-ai/mastra/pull/21715))
21
+
22
+ ```typescript
23
+ await mastraClient.createStoredAgent({
24
+ id: 'helper',
25
+ name: 'Helper',
26
+ instructions: 'You are a helpful assistant.',
27
+ model: { provider: 'openai', name: 'gpt-5' },
28
+ durable: true,
29
+ });
30
+ ```
31
+
32
+ Pass `true` for defaults, or `{ maxSteps, cleanupTimeoutMs }` to tune the durable loop. Cache and pubsub are inherited from the server's Mastra instance, so configure distributed backends there for durability across replicas. Automatic recovery is still configured in code via `recovery.durableAgents`.
33
+
34
+ - Improved PgVector upsert performance when writing many vectors at once. Batches are now written with a small number of multi-row inserts instead of one insert per vector, which reduces database round trips and connection pool usage during RAG and memory ingestion. ([#21719](https://github.com/mastra-ai/mastra/pull/21719))
35
+
36
+ - Updated dependencies [[`6223446`](https://github.com/mastra-ai/mastra/commit/6223446ddce6166e96e0ba5e00d628b615dee8ca), [`583e235`](https://github.com/mastra-ai/mastra/commit/583e23519c13af16c1746f9c49722d011216611b), [`a77f8d4`](https://github.com/mastra-ai/mastra/commit/a77f8d4740d2178a74c41e4bf678b4fcd8fa0bb2), [`40d358e`](https://github.com/mastra-ai/mastra/commit/40d358e29d55543803e64b49241122f598ffabc7), [`e80cd7e`](https://github.com/mastra-ai/mastra/commit/e80cd7e7683e7d732e1cc6784bcac1d2640d2ce3), [`20504b2`](https://github.com/mastra-ai/mastra/commit/20504b2ecebd0e077acda3d457ab57480a98ed3e)]:
37
+ - @mastra/core@1.60.0-alpha.11
38
+
3
39
  ## 1.20.1-alpha.1
4
40
 
5
41
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-pg
3
3
  description: Documentation for @mastra/pg. Use when working with @mastra/pg APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/pg"
6
- version: "1.20.1-alpha.1"
6
+ version: "1.20.1-alpha.3"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.20.1-alpha.1",
2
+ "version": "1.20.1-alpha.3",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -145,4 +145,5 @@ export const agent = new Agent({
145
145
  - [listThreads](https://mastra.ai/reference/memory/listThreads)
146
146
  - [deleteMessages](https://mastra.ai/reference/memory/deleteMessages)
147
147
  - [cloneThread](https://mastra.ai/reference/memory/cloneThread)
148
+ - [settled](https://mastra.ai/reference/memory/settled)
148
149
  - [Clone Utility Methods](https://mastra.ai/reference/memory/clone-utilities)
package/dist/index.cjs CHANGED
@@ -565,6 +565,7 @@ function buildFilterQuery(filter, minScore, topK) {
565
565
  }
566
566
  //#endregion
567
567
  //#region src/vector/index.ts
568
+ const MAX_UPSERT_ROWS_PER_STATEMENT = Math.floor(65535 / 3);
568
569
  var PgVector = class extends _mastra_core_vector.MastraVector {
569
570
  pool;
570
571
  describeIndexCache = /* @__PURE__ */ new Map();
@@ -957,23 +958,41 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
957
958
  const indexInfo = await this.getIndexInfo({ indexName });
958
959
  const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType, indexInfo.dimension);
959
960
  const ops = this.getVectorOps(indexInfo.vectorType, indexInfo.metric ?? "cosine");
960
- for (let i = 0; i < vectors.length; i++) {
961
+ if (new Set(vectorIds).size !== vectorIds.length) for (let i = 0; i < vectors.length; i++) {
961
962
  const vectorStr = ops.formatVector(vectors[i], indexInfo.dimension);
962
963
  const query = `
963
- INSERT INTO ${tableName} (vector_id, embedding, metadata)
964
- VALUES ($1, $2::${qualifiedVectorType}, $3::jsonb)
965
- ON CONFLICT (vector_id)
966
- DO UPDATE SET
967
- embedding = $2::${qualifiedVectorType},
968
- metadata = $3::jsonb
969
- RETURNING embedding::text
970
- `;
964
+ INSERT INTO ${tableName} (vector_id, embedding, metadata)
965
+ VALUES ($1, $2::${qualifiedVectorType}, $3::jsonb)
966
+ ON CONFLICT (vector_id)
967
+ DO UPDATE SET
968
+ embedding = $2::${qualifiedVectorType},
969
+ metadata = $3::jsonb
970
+ `;
971
971
  await client.query(query, [
972
972
  vectorIds[i],
973
973
  vectorStr,
974
974
  JSON.stringify(metadata?.[i] || {})
975
975
  ]);
976
976
  }
977
+ else for (let start = 0; start < vectors.length; start += MAX_UPSERT_ROWS_PER_STATEMENT) {
978
+ const end = Math.min(start + MAX_UPSERT_ROWS_PER_STATEMENT, vectors.length);
979
+ const rows = [];
980
+ const values = [];
981
+ for (let i = start; i < end; i++) {
982
+ const base = values.length;
983
+ rows.push(`($${base + 1}, $${base + 2}::${qualifiedVectorType}, $${base + 3}::jsonb)`);
984
+ values.push(vectorIds[i], ops.formatVector(vectors[i], indexInfo.dimension), JSON.stringify(metadata?.[i] || {}));
985
+ }
986
+ const query = `
987
+ INSERT INTO ${tableName} (vector_id, embedding, metadata)
988
+ VALUES ${rows.join(", ")}
989
+ ON CONFLICT (vector_id)
990
+ DO UPDATE SET
991
+ embedding = EXCLUDED.embedding,
992
+ metadata = EXCLUDED.metadata
993
+ `;
994
+ await client.query(query, values);
995
+ }
977
996
  await client.query("COMMIT");
978
997
  this.logger?.debug(`Upserted ${vectors.length} vectors to ${indexName}`, {
979
998
  indexName,
@@ -3803,6 +3822,7 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
3803
3822
  "workspace",
3804
3823
  "skills",
3805
3824
  "skillsFormat",
3825
+ "durable",
3806
3826
  "browser",
3807
3827
  "toolProviders"
3808
3828
  ]
@@ -4282,10 +4302,10 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
4282
4302
  "defaultOptions", workflows, agents, "integrationTools", "toolProviders",
4283
4303
  "inputProcessors", "outputProcessors", memory, scorers,
4284
4304
  "mcpClients", "requestContextSchema", workspace, skills, "skillsFormat",
4285
- browser,
4305
+ durable, browser,
4286
4306
  "changedFields", "changeMessage",
4287
4307
  "createdAt", "createdAtZ"
4288
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27)`, [
4308
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28)`, [
4289
4309
  input.id,
4290
4310
  input.agentId,
4291
4311
  input.versionNumber,
@@ -4308,6 +4328,7 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
4308
4328
  input.workspace ? JSON.stringify(input.workspace) : null,
4309
4329
  input.skills ? JSON.stringify(input.skills) : null,
4310
4330
  input.skillsFormat ?? null,
4331
+ input.durable !== void 0 ? JSON.stringify(input.durable) : null,
4311
4332
  input.browser ? JSON.stringify(input.browser) : null,
4312
4333
  input.changedFields ? JSON.stringify(input.changedFields) : null,
4313
4334
  input.changeMessage ?? null,
@@ -4536,6 +4557,7 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
4536
4557
  workspace: parseJsonResilient(row.workspace, "workspace"),
4537
4558
  skills: parseJsonResilient(row.skills, "skills"),
4538
4559
  skillsFormat: row.skillsFormat,
4560
+ durable: parseJsonResilient(row.durable, "durable"),
4539
4561
  browser: parseJsonResilient(row.browser, "browser"),
4540
4562
  changedFields: parseJsonResilient(row.changedFields, "changedFields"),
4541
4563
  changeMessage: row.changeMessage,
@@ -20752,6 +20774,38 @@ var PgFactoryStorageOps = class {
20752
20774
  * Supports serializable app-table transactions for cross-replica invariant
20753
20775
  * enforcement and `authDatabase()` exposing the shared pool.
20754
20776
  */
20777
+ /**
20778
+ * A pool-level `error` listener only covers *idle* clients: pg hands ownership
20779
+ * of a client to the borrower for the duration of a checkout and takes its own
20780
+ * listener off. So a backend restart or network blip that lands on a client
20781
+ * mid-transaction reaches an emitter with no listener, and Node escalates that
20782
+ * to an uncaughtException that kills the process — the pool handler logs the
20783
+ * idle siblings and the borrowed one still brings everything down.
20784
+ *
20785
+ * Attach a listener once per physical connection instead. `connect` fires when
20786
+ * the pool establishes a client, so the client stays covered for its whole life
20787
+ * and there is nothing to remove on release.
20788
+ *
20789
+ * That listener outlives the checkout, so it also sees the failures the pool is
20790
+ * already reporting through its own idle listener. Follow `acquire`/`release`
20791
+ * to tell the two apart and stay quiet while the client is idle — otherwise one
20792
+ * dropped connection is announced twice, the second time as the wrong thing.
20793
+ *
20794
+ * The pool still discards the failed connection; this only keeps the failure
20795
+ * reportable instead of fatal.
20796
+ */
20797
+ function guardCheckedOutClientErrors(pool, warn = console.warn) {
20798
+ const borrowed = /* @__PURE__ */ new WeakSet();
20799
+ pool.on("connect", (client) => {
20800
+ borrowed.add(client);
20801
+ client.on("error", (err) => {
20802
+ if (!borrowed.has(client)) return;
20803
+ warn(`PgFactoryStorage: client error while checked out (the pool discards this connection): ${err instanceof Error ? err.message : String(err)}`);
20804
+ });
20805
+ });
20806
+ pool.on("acquire", (client) => borrowed.add(client));
20807
+ pool.on("release", (_err, client) => borrowed.delete(client));
20808
+ }
20755
20809
  var PgFactoryStorage = class extends _mastra_core_storage.FactoryStorage {
20756
20810
  ops;
20757
20811
  #pool;
@@ -20772,6 +20826,7 @@ var PgFactoryStorage = class extends _mastra_core_storage.FactoryStorage {
20772
20826
  this.#pool.on("error", (err) => {
20773
20827
  console.warn(`PgFactoryStorage: idle pool client error (pool discards the client and reconnects on next checkout): ${err instanceof Error ? err.message : String(err)}`);
20774
20828
  });
20829
+ guardCheckedOutClientErrors(this.#pool);
20775
20830
  }
20776
20831
  this.ops = new PgFactoryStorageOps(this.#pool, this.#schemas);
20777
20832
  }