@mastra/pg 1.31.0-alpha.3 → 1.31.0

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
@@ -3213,9 +3213,10 @@ var PgDB = class extends _mastra_core_base.MastraBase {
3213
3213
  }
3214
3214
  await flush();
3215
3215
  }
3216
- async insert({ tableName, record }) {
3216
+ /** `client` lets a caller run the insert inside its own transaction. */
3217
+ async insert({ tableName, record }, client = this.client) {
3217
3218
  try {
3218
- await this.executeInsert(this.client, {
3219
+ await this.executeInsert(client, {
3219
3220
  tableName,
3220
3221
  record
3221
3222
  });
@@ -4049,13 +4050,15 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
4049
4050
  indexName: tableName,
4050
4051
  schemaName: getSchemaName$6(this.schemaName)
4051
4052
  });
4053
+ const parsedColumn = `"${(0, _mastra_core_utils.parseSqlIdentifier)(column, "column name")}"`;
4052
4054
  const sql = `
4053
4055
  DELETE FROM ${fullTableName}
4054
4056
  WHERE ctid IN (
4055
4057
  SELECT ctid FROM ${fullTableName}
4056
- WHERE ${`"${(0, _mastra_core_utils.parseSqlIdentifier)(column, "column name")}"`} < $1
4058
+ WHERE ${parsedColumn} < $1
4057
4059
  LIMIT $2
4058
4060
  )
4061
+ AND ${parsedColumn} < $1
4059
4062
  `;
4060
4063
  return (await this.client.query(sql, [cutoff, limit])).rowCount ?? 0;
4061
4064
  }
@@ -4518,6 +4521,7 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
4518
4521
  }
4519
4522
  async create(input) {
4520
4523
  const { agent } = input;
4524
+ let draftInserted = false;
4521
4525
  try {
4522
4526
  const agentsTable = getTableName$5({
4523
4527
  indexName: _mastra_core_storage.TABLE_AGENTS,
@@ -4544,6 +4548,7 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
4544
4548
  nowIso,
4545
4549
  nowIso
4546
4550
  ]);
4551
+ draftInserted = true;
4547
4552
  const { id: _id, authorId: _authorId, visibility: _visibility, metadata: _metadata, ...snapshotConfig } = agent;
4548
4553
  const versionId = crypto.randomUUID();
4549
4554
  await this.createVersion({
@@ -4566,14 +4571,14 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
4566
4571
  updatedAt: now
4567
4572
  };
4568
4573
  } catch (error) {
4569
- if (error instanceof _mastra_core_error.MastraError) throw error;
4570
- try {
4574
+ if (draftInserted) try {
4571
4575
  const agentsTable = getTableName$5({
4572
4576
  indexName: _mastra_core_storage.TABLE_AGENTS,
4573
4577
  schemaName: getSchemaName$5(this.#schema)
4574
4578
  });
4575
4579
  await this.#db.client.none(`DELETE FROM ${agentsTable} WHERE id = $1 AND status = 'draft' AND "activeVersionId" IS NULL`, [agent.id]);
4576
4580
  } catch {}
4581
+ if (error instanceof _mastra_core_error.MastraError) throw error;
4577
4582
  throw new _mastra_core_error.MastraError({
4578
4583
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "CREATE_AGENT", "FAILED"),
4579
4584
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -10569,6 +10574,7 @@ var MCPClientsPG = class MCPClientsPG extends _mastra_core_storage.MCPClientsSto
10569
10574
  }
10570
10575
  async create(input) {
10571
10576
  const { mcpClient } = input;
10577
+ let draftInserted = false;
10572
10578
  try {
10573
10579
  const tableName = getTableName$5({
10574
10580
  indexName: _mastra_core_storage.TABLE_MCP_CLIENTS,
@@ -10591,6 +10597,7 @@ var MCPClientsPG = class MCPClientsPG extends _mastra_core_storage.MCPClientsSto
10591
10597
  nowIso,
10592
10598
  nowIso
10593
10599
  ]);
10600
+ draftInserted = true;
10594
10601
  const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = mcpClient;
10595
10602
  const versionId = crypto.randomUUID();
10596
10603
  await this.createVersion({
@@ -10611,7 +10618,7 @@ var MCPClientsPG = class MCPClientsPG extends _mastra_core_storage.MCPClientsSto
10611
10618
  updatedAt: now
10612
10619
  };
10613
10620
  } catch (error) {
10614
- try {
10621
+ if (draftInserted) try {
10615
10622
  const tableName = getTableName$5({
10616
10623
  indexName: _mastra_core_storage.TABLE_MCP_CLIENTS,
10617
10624
  schemaName: getSchemaName$5(this.#schema)
@@ -11137,6 +11144,7 @@ var MCPServersPG = class MCPServersPG extends _mastra_core_storage.MCPServersSto
11137
11144
  }
11138
11145
  async create(input) {
11139
11146
  const { mcpServer } = input;
11147
+ let draftInserted = false;
11140
11148
  try {
11141
11149
  const tableName = getTableName$5({
11142
11150
  indexName: _mastra_core_storage.TABLE_MCP_SERVERS,
@@ -11158,6 +11166,7 @@ var MCPServersPG = class MCPServersPG extends _mastra_core_storage.MCPServersSto
11158
11166
  nowIso,
11159
11167
  nowIso
11160
11168
  ]);
11169
+ draftInserted = true;
11161
11170
  const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = mcpServer;
11162
11171
  const versionId = crypto.randomUUID();
11163
11172
  await this.createVersion({
@@ -11178,7 +11187,7 @@ var MCPServersPG = class MCPServersPG extends _mastra_core_storage.MCPServersSto
11178
11187
  updatedAt: now
11179
11188
  };
11180
11189
  } catch (error) {
11181
- try {
11190
+ if (draftInserted) try {
11182
11191
  const tableName = getTableName$5({
11183
11192
  indexName: _mastra_core_storage.TABLE_MCP_SERVERS,
11184
11193
  schemaName: getSchemaName$5(this.#schema)
@@ -11602,6 +11611,38 @@ var MCPServersPG = class MCPServersPG extends _mastra_core_storage.MCPServersSto
11602
11611
  }
11603
11612
  };
11604
11613
  //#endregion
11614
+ //#region src/storage/db/run-fencing.ts
11615
+ /**
11616
+ * Epoch milliseconds on the database clock. `clock_timestamp()` rather than
11617
+ * `now()`: a statement can wait on a row lock, and lease math must use the
11618
+ * time it actually runs, not when its transaction started.
11619
+ */
11620
+ const DB_NOW_MS = `(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint`;
11621
+ /**
11622
+ * The check half of a fenced write; call it first inside the write's
11623
+ * transaction. `FOR SHARE` holds the claim row until commit, so a claim that
11624
+ * updates the row waits for the write to finish, and every later write sees
11625
+ * the new claim.
11626
+ */
11627
+ async function assertRunFence(t, claimsTable, fence, operation) {
11628
+ const row = await t.oneOrNone(`SELECT generation, "ownerId" FROM ${claimsTable} WHERE "runId" = $1 FOR SHARE`, [fence.runId]);
11629
+ if (!(0, _mastra_core_storage.matchesRunFence)(row && {
11630
+ generation: Number(row.generation),
11631
+ ownerId: row.ownerId
11632
+ }, fence)) throw new _mastra_core_storage.RunFenceConflictError(fence, operation);
11633
+ }
11634
+ /**
11635
+ * Runs `write` directly when it carries no fence, or in a transaction behind
11636
+ * the fence check when it does.
11637
+ */
11638
+ function withRunFence(client, claimsTable, fence, operation, write) {
11639
+ if (!fence) return write(client);
11640
+ return client.tx(async (t) => {
11641
+ await assertRunFence(t, claimsTable, fence, operation);
11642
+ return write(t);
11643
+ });
11644
+ }
11645
+ //#endregion
11605
11646
  //#region src/storage/domains/memory/index.ts
11606
11647
  /**
11607
11648
  * Local constant for the observational memory table name.
@@ -11702,6 +11743,10 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11702
11743
  * and are indexed for fast batched deletes. Cascade order is enforced in
11703
11744
  * `prune()` (children before threads), not here. Observational memory has no
11704
11745
  * timestamp anchor and is deliberately excluded.
11746
+ *
11747
+ * Run fences anchor on `retiredAt` (epoch ms), which stays NULL until the
11748
+ * execution holding the fence settles, so a running execution's fence is
11749
+ * never pruned.
11705
11750
  */
11706
11751
  static retentionTables = {
11707
11752
  messages: {
@@ -11718,6 +11763,12 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11718
11763
  table: _mastra_core_storage.TABLE_THREADS,
11719
11764
  column: "createdAtZ",
11720
11765
  indexed: true
11766
+ },
11767
+ runFences: {
11768
+ table: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
11769
+ column: "retiredAt",
11770
+ indexed: true,
11771
+ anchorType: "epoch-ms"
11721
11772
  }
11722
11773
  };
11723
11774
  #db;
@@ -11781,6 +11832,10 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11781
11832
  });
11782
11833
  await this.#db.createIndexFromStatement("idx_om_lookup_key", `CREATE INDEX IF NOT EXISTS idx_om_lookup_key ON ${omTableName} ("lookupKey")`);
11783
11834
  }
11835
+ await this.#db.createTable({
11836
+ tableName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
11837
+ schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_MEMORY_RUN_FENCES]
11838
+ });
11784
11839
  await this.createDefaultIndexes();
11785
11840
  await this.createCustomIndexes();
11786
11841
  }
@@ -11858,6 +11913,12 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11858
11913
  statements.push(`CREATE INDEX IF NOT EXISTS "${idxPrefix}idx_om_lookup_key" ON ${fullOmTableName} ("lookupKey");`);
11859
11914
  }
11860
11915
  for (const idx of MemoryPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
11916
+ statements.push(generateTableSQL({
11917
+ tableName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
11918
+ schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_MEMORY_RUN_FENCES],
11919
+ schemaName,
11920
+ includeAllConstraints: true
11921
+ }));
11861
11922
  return statements;
11862
11923
  }
11863
11924
  /**
@@ -11893,6 +11954,61 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11893
11954
  await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_MESSAGES });
11894
11955
  await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_THREADS });
11895
11956
  await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_RESOURCES });
11957
+ await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES });
11958
+ }
11959
+ supportsRunFencing() {
11960
+ return true;
11961
+ }
11962
+ #runFencesTable() {
11963
+ return getTableName$3({
11964
+ indexName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
11965
+ schemaName: getSchemaName$3(this.#schema)
11966
+ });
11967
+ }
11968
+ /** Runs `write` behind the run's fence check when it carries a fence. */
11969
+ #fenced(fence, operation, write) {
11970
+ return withRunFence(this.#db.client, this.#runFencesTable(), (0, _mastra_core_storage.resolveRunFence)(this, fence), operation, write);
11971
+ }
11972
+ async raiseRunFence(fence) {
11973
+ try {
11974
+ return await this.#db.client.oneOrNone(`INSERT INTO ${this.#runFencesTable()} AS f ("runId", generation, "ownerId")
11975
+ VALUES ($1, $2, $3)
11976
+ ON CONFLICT ("runId") DO UPDATE
11977
+ SET generation = EXCLUDED.generation, "ownerId" = EXCLUDED."ownerId", "retiredAt" = NULL
11978
+ WHERE f.generation < EXCLUDED.generation
11979
+ OR (f.generation = EXCLUDED.generation AND f."ownerId" = EXCLUDED."ownerId")
11980
+ RETURNING "runId"`, [
11981
+ fence.runId,
11982
+ fence.generation,
11983
+ fence.ownerId
11984
+ ]) !== null;
11985
+ } catch (error) {
11986
+ throw new _mastra_core_error.MastraError({
11987
+ id: (0, _mastra_core_storage.createStorageErrorId)("PG", "RAISE_RUN_FENCE", "FAILED"),
11988
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
11989
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
11990
+ details: { runId: fence.runId }
11991
+ }, error);
11992
+ }
11993
+ }
11994
+ async retireRunFence(fence) {
11995
+ try {
11996
+ return await this.#db.client.oneOrNone(`UPDATE ${this.#runFencesTable()}
11997
+ SET "retiredAt" = ${DB_NOW_MS}
11998
+ WHERE "runId" = $1 AND generation = $2 AND "ownerId" = $3
11999
+ RETURNING "runId"`, [
12000
+ fence.runId,
12001
+ fence.generation,
12002
+ fence.ownerId
12003
+ ]) !== null;
12004
+ } catch (error) {
12005
+ throw new _mastra_core_error.MastraError({
12006
+ id: (0, _mastra_core_storage.createStorageErrorId)("PG", "RETIRE_RUN_FENCE", "FAILED"),
12007
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
12008
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
12009
+ details: { runId: fence.runId }
12010
+ }, error);
12011
+ }
11896
12012
  }
11897
12013
  /**
11898
12014
  * Deletes rows older than the configured `maxAge` per table, in bounded,
@@ -11913,7 +12029,8 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11913
12029
  order: [
11914
12030
  "messages",
11915
12031
  "resources",
11916
- "threads"
12032
+ "threads",
12033
+ "runFences"
11917
12034
  ]
11918
12035
  });
11919
12036
  const results = await runPrune({
@@ -12171,7 +12288,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12171
12288
  throw mastraError;
12172
12289
  }
12173
12290
  }
12174
- async saveThread({ thread }) {
12291
+ async saveThread({ thread, fence }) {
12175
12292
  try {
12176
12293
  const tableName = getTableName$3({
12177
12294
  indexName: _mastra_core_storage.TABLE_THREADS,
@@ -12180,7 +12297,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12180
12297
  const createdAt = toUtcISOString(thread.createdAt);
12181
12298
  const updatedAt = toUtcISOString(thread.updatedAt);
12182
12299
  const metadataJson = thread.metadata ? toPgJson(thread.metadata) : null;
12183
- await this.#db.client.none(`INSERT INTO ${tableName} (
12300
+ await this.#fenced(fence, "saveThread", (q) => q.none(`INSERT INTO ${tableName} (
12184
12301
  id,
12185
12302
  "resourceId",
12186
12303
  title,
@@ -12206,12 +12323,13 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12206
12323
  createdAt,
12207
12324
  updatedAt,
12208
12325
  updatedAt
12209
- ]);
12326
+ ]));
12210
12327
  return {
12211
12328
  ...thread,
12212
12329
  metadata: metadataJson ? JSON.parse(metadataJson) : thread.metadata
12213
12330
  };
12214
12331
  } catch (error) {
12332
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
12215
12333
  throw new _mastra_core_error.MastraError({
12216
12334
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "SAVE_THREAD", "FAILED"),
12217
12335
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -12220,29 +12338,30 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12220
12338
  }, error);
12221
12339
  }
12222
12340
  }
12223
- async updateThread({ id, title, metadata }) {
12341
+ async updateThread({ id, title, metadata, fence }) {
12224
12342
  const threadTableName = getTableName$3({
12225
12343
  indexName: _mastra_core_storage.TABLE_THREADS,
12226
12344
  schemaName: getSchemaName$3(this.#schema)
12227
12345
  });
12228
- const existingThread = await this.#getThreadById(this.#db.client, { threadId: id });
12229
- if (!existingThread) throw new _mastra_core_error.MastraError({
12230
- id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_THREAD", "FAILED"),
12231
- domain: _mastra_core_error.ErrorDomain.STORAGE,
12232
- category: _mastra_core_error.ErrorCategory.USER,
12233
- text: `Thread ${id} not found`,
12234
- details: {
12235
- threadId: id,
12236
- title: title ?? null
12237
- }
12238
- });
12239
- const mergedMetadata = {
12240
- ...existingThread.metadata,
12241
- ...metadata
12242
- };
12243
12346
  try {
12244
- const nowStr = toUtcISOString(/* @__PURE__ */ new Date());
12245
- const thread = await this.#db.client.one(`UPDATE ${threadTableName}
12347
+ return await this.#fenced(fence, "updateThread", async (q) => {
12348
+ const existingThread = await this.#getThreadById(q, { threadId: id });
12349
+ if (!existingThread) throw new _mastra_core_error.MastraError({
12350
+ id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_THREAD", "FAILED"),
12351
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
12352
+ category: _mastra_core_error.ErrorCategory.USER,
12353
+ text: `Thread ${id} not found`,
12354
+ details: {
12355
+ threadId: id,
12356
+ title: title ?? null
12357
+ }
12358
+ });
12359
+ const mergedMetadata = {
12360
+ ...existingThread.metadata,
12361
+ ...metadata
12362
+ };
12363
+ const nowStr = toUtcISOString(/* @__PURE__ */ new Date());
12364
+ const thread = await q.one(`UPDATE ${threadTableName}
12246
12365
  SET
12247
12366
  title = COALESCE($1, title),
12248
12367
  metadata = $2,
@@ -12251,21 +12370,23 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12251
12370
  WHERE id = $5
12252
12371
  RETURNING *
12253
12372
  `, [
12254
- title ?? null,
12255
- mergedMetadata,
12256
- nowStr,
12257
- nowStr,
12258
- id
12259
- ]);
12260
- return {
12261
- id: thread.id,
12262
- resourceId: thread.resourceId,
12263
- title: thread.title,
12264
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
12265
- createdAt: thread.createdAtZ || thread.createdAt,
12266
- updatedAt: thread.updatedAtZ || thread.updatedAt
12267
- };
12373
+ title ?? null,
12374
+ mergedMetadata,
12375
+ nowStr,
12376
+ nowStr,
12377
+ id
12378
+ ]);
12379
+ return {
12380
+ id: thread.id,
12381
+ resourceId: thread.resourceId,
12382
+ title: thread.title,
12383
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
12384
+ createdAt: thread.createdAtZ || thread.createdAt,
12385
+ updatedAt: thread.updatedAtZ || thread.updatedAt
12386
+ };
12387
+ });
12268
12388
  } catch (error) {
12389
+ if (error instanceof _mastra_core_error.MastraError || (0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
12269
12390
  throw new _mastra_core_error.MastraError({
12270
12391
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_THREAD", "FAILED"),
12271
12392
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -12818,8 +12939,9 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12818
12939
  throw mastraError;
12819
12940
  }
12820
12941
  }
12821
- async saveMessages({ messages }) {
12942
+ async saveMessages({ messages, fence: explicitFence }) {
12822
12943
  if (messages.length === 0) return { messages: [] };
12944
+ const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence);
12823
12945
  const threadId = messages[0]?.threadId;
12824
12946
  if (!threadId) throw new _mastra_core_error.MastraError({
12825
12947
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "SAVE_MESSAGES", "FAILED"),
@@ -12847,6 +12969,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12847
12969
  });
12848
12970
  const messagesToSave = dedupeMessagesForSave(messages);
12849
12971
  await this.#db.client.tx(async (t) => {
12972
+ if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "saveMessages");
12850
12973
  for (let offset = 0; offset < messagesToSave.length; offset += MAX_MESSAGES_PER_INSERT) {
12851
12974
  const batch = messagesToSave.slice(offset, offset + MAX_MESSAGES_PER_INSERT);
12852
12975
  const values = [];
@@ -12893,7 +13016,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12893
13016
  });
12894
13017
  return { messages: new _mastra_core_agent.MessageList().add(messagesWithParsedContent, "memory").get.all.db() };
12895
13018
  } catch (error) {
12896
- if (error instanceof _mastra_core_error.MastraError) throw error;
13019
+ if (error instanceof _mastra_core_error.MastraError || (0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
12897
13020
  throw new _mastra_core_error.MastraError({
12898
13021
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "SAVE_MESSAGES", "FAILED"),
12899
13022
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -12902,8 +13025,9 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12902
13025
  }, error);
12903
13026
  }
12904
13027
  }
12905
- async updateMessages({ messages }) {
13028
+ async updateMessages({ messages, fence: explicitFence }) {
12906
13029
  if (messages.length === 0) return [];
13030
+ const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence);
12907
13031
  const messageIds = messages.map((m) => m.id);
12908
13032
  const selectQuery = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId" FROM ${getTableName$3({
12909
13033
  indexName: _mastra_core_storage.TABLE_MESSAGES,
@@ -12919,6 +13043,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12919
13043
  });
12920
13044
  const threadIdsToUpdate = /* @__PURE__ */ new Set();
12921
13045
  await this.#db.client.tx(async (t) => {
13046
+ if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "updateMessages");
12922
13047
  const queries = [];
12923
13048
  const columnMapping = { threadId: "thread_id" };
12924
13049
  for (const existingMessage of existingMessages) {
@@ -12979,8 +13104,9 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12979
13104
  return message;
12980
13105
  });
12981
13106
  }
12982
- async deleteMessages(messageIds) {
13107
+ async deleteMessages(messageIds, options) {
12983
13108
  if (!messageIds || messageIds.length === 0) return;
13109
+ const fence = (0, _mastra_core_storage.resolveRunFence)(this, options?.fence);
12984
13110
  try {
12985
13111
  const messageTableName = getTableName$3({
12986
13112
  indexName: _mastra_core_storage.TABLE_MESSAGES,
@@ -12991,12 +13117,14 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12991
13117
  schemaName: getSchemaName$3(this.#schema)
12992
13118
  });
12993
13119
  await this.#db.client.tx(async (t) => {
13120
+ if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "deleteMessages");
12994
13121
  const placeholders = messageIds.map((_, idx) => `$${idx + 1}`).join(",");
12995
13122
  const threadIds = (await t.manyOrNone(`SELECT DISTINCT thread_id FROM ${messageTableName} WHERE id IN (${placeholders})`, messageIds))?.map((msg) => msg.thread_id).filter(Boolean) || [];
12996
13123
  await t.none(`DELETE FROM ${messageTableName} WHERE id IN (${placeholders})`, messageIds);
12997
13124
  if (threadIds.length > 0) await t.none(`UPDATE ${threadTableName} SET "updatedAt" = NOW(), "updatedAtZ" = NOW() WHERE id IN (${inPlaceholders(threadIds.length)})`, threadIds);
12998
13125
  });
12999
13126
  } catch (error) {
13127
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
13000
13128
  throw new _mastra_core_error.MastraError({
13001
13129
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "DELETE_MESSAGES", "FAILED"),
13002
13130
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -13024,6 +13152,9 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13024
13152
  };
13025
13153
  }
13026
13154
  async saveResource({ resource }) {
13155
+ return this.#insertResource(this.#db.client, resource);
13156
+ }
13157
+ async #insertResource(q, resource) {
13027
13158
  const createdAt = toUtcISOString(resource.createdAt);
13028
13159
  const updatedAt = toUtcISOString(resource.updatedAt);
13029
13160
  const metadataJson = toPgJson(resource.metadata);
@@ -13035,7 +13166,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13035
13166
  createdAt,
13036
13167
  updatedAt
13037
13168
  }
13038
- });
13169
+ }, q);
13039
13170
  return {
13040
13171
  ...resource,
13041
13172
  metadata: metadataJson ? JSON.parse(metadataJson) : resource.metadata
@@ -13046,9 +13177,11 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13046
13177
  indexName: _mastra_core_storage.TABLE_RESOURCES,
13047
13178
  schemaName: getSchemaName$3(this.#schema)
13048
13179
  });
13180
+ const fence = (0, _mastra_core_storage.resolveRunFence)(this, void 0);
13049
13181
  const callbackError = {};
13050
13182
  try {
13051
13183
  return await this.#db.client.tx(async (t) => {
13184
+ if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "mergeResourceWorkingMemory");
13052
13185
  const now = (/* @__PURE__ */ new Date()).toISOString();
13053
13186
  await t.none(`INSERT INTO ${tableName} (id, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ")
13054
13187
  VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO NOTHING`, [
@@ -13092,8 +13225,15 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13092
13225
  }, error);
13093
13226
  }
13094
13227
  }
13095
- async updateResource({ resourceId, workingMemory, metadata }) {
13096
- const existingResource = await this.#getResourceById(this.#db.client, resourceId);
13228
+ async updateResource({ resourceId, workingMemory, metadata, fence }) {
13229
+ return this.#fenced(fence, "updateResource", (q) => this.#updateResource(q, {
13230
+ resourceId,
13231
+ workingMemory,
13232
+ metadata
13233
+ }));
13234
+ }
13235
+ async #updateResource(q, { resourceId, workingMemory, metadata }) {
13236
+ const existingResource = await this.#getResourceById(q, resourceId);
13097
13237
  if (!existingResource) {
13098
13238
  const newResource = {
13099
13239
  id: resourceId,
@@ -13102,7 +13242,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13102
13242
  createdAt: /* @__PURE__ */ new Date(),
13103
13243
  updatedAt: /* @__PURE__ */ new Date()
13104
13244
  };
13105
- return this.saveResource({ resource: newResource });
13245
+ return this.#insertResource(q, newResource);
13106
13246
  }
13107
13247
  const updatedResource = {
13108
13248
  ...existingResource,
@@ -13138,7 +13278,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13138
13278
  updates.push(`"updatedAtZ" = $${paramIndex++}`);
13139
13279
  values.push(updatedAtStr);
13140
13280
  values.push(resourceId);
13141
- await this.#db.client.none(`UPDATE ${tableName} SET ${updates.join(", ")} WHERE id = $${paramIndex}`, values);
13281
+ await q.none(`UPDATE ${tableName} SET ${updates.join(", ")} WHERE id = $${paramIndex}`, values);
13142
13282
  return metadataJson ? {
13143
13283
  ...updatedResource,
13144
13284
  metadata: JSON.parse(metadataJson)
@@ -13277,9 +13417,11 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13277
13417
  * serializes generation creation across processes instead. It is
13278
13418
  * transaction-scoped so it also works behind transaction-pooling proxies.
13279
13419
  */
13280
- async #withOMLookupKeyLock(tableName, lookupKey, fn) {
13420
+ async #withOMLookupKeyLock(tableName, lookupKey, fn, fencedOperation) {
13421
+ const fence = fencedOperation ? (0, _mastra_core_storage.resolveRunFence)(this, void 0) : void 0;
13281
13422
  return this.#db.client.tx(async (t) => {
13282
13423
  await t.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${tableName}:${lookupKey}`]);
13424
+ if (fence && fencedOperation) await assertRunFence(t, this.#runFencesTable(), fence, fencedOperation);
13283
13425
  return fn(t);
13284
13426
  });
13285
13427
  }
@@ -13576,7 +13718,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13576
13718
  const lastObservedAtStr = input.lastObservedAt.toISOString();
13577
13719
  const nowStr = now.toISOString();
13578
13720
  const observedMessageIdsJson = input.observedMessageIds ? toPgJson(input.observedMessageIds) : null;
13579
- if ((await this.#db.client.query(`UPDATE ${tableName} SET
13721
+ if ((await this.#fenced(void 0, "updateActiveObservations", (q) => q.query(`UPDATE ${tableName} SET
13580
13722
  "activeObservations" = $1,
13581
13723
  "lastObservedAt" = $2,
13582
13724
  "lastObservedAtZ" = $3,
@@ -13596,7 +13738,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13596
13738
  nowStr,
13597
13739
  nowStr,
13598
13740
  input.id
13599
- ])).rowCount === 0) throw new _mastra_core_error.MastraError({
13741
+ ]))).rowCount === 0) throw new _mastra_core_error.MastraError({
13600
13742
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_ACTIVE_OBSERVATIONS", "NOT_FOUND"),
13601
13743
  text: `Observational memory record not found: ${input.id}`,
13602
13744
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -13604,7 +13746,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13604
13746
  details: { id: input.id }
13605
13747
  });
13606
13748
  } catch (error) {
13607
- if (error instanceof _mastra_core_error.MastraError) throw error;
13749
+ if (error instanceof _mastra_core_error.MastraError || (0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
13608
13750
  throw new _mastra_core_error.MastraError({
13609
13751
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_ACTIVE_OBSERVATIONS", "FAILED"),
13610
13752
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -13624,8 +13766,9 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13624
13766
  const newer = await this.#getNewerOMGeneration(t, tableName, lookupKey, input.currentRecord.generationCount);
13625
13767
  if (newer) return newer;
13626
13768
  return this.#insertReflectionGeneration(t, tableName, lookupKey, input);
13627
- });
13769
+ }, "createReflectionGeneration");
13628
13770
  } catch (error) {
13771
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
13629
13772
  throw new _mastra_core_error.MastraError({
13630
13773
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "CREATE_REFLECTION_GENERATION", "FAILED"),
13631
13774
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -13985,7 +14128,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
13985
14128
  extractionFailures: input.chunk.extractionFailures
13986
14129
  };
13987
14130
  const lastBufferedAtTime = input.lastBufferedAtTime ? input.lastBufferedAtTime.toISOString() : null;
13988
- if ((await this.#db.client.query(`UPDATE ${tableName} SET
14131
+ if ((await this.#fenced(void 0, "updateBufferedObservations", (q) => q.query(`UPDATE ${tableName} SET
13989
14132
  "bufferedObservationChunks" = CASE
13990
14133
  WHEN EXISTS (
13991
14134
  SELECT 1
@@ -14004,7 +14147,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
14004
14147
  nowStr,
14005
14148
  nowStr,
14006
14149
  input.id
14007
- ])).rowCount === 0) throw new _mastra_core_error.MastraError({
14150
+ ]))).rowCount === 0) throw new _mastra_core_error.MastraError({
14008
14151
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
14009
14152
  text: `Observational memory record not found: ${input.id}`,
14010
14153
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -14012,7 +14155,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
14012
14155
  details: { id: input.id }
14013
14156
  });
14014
14157
  } catch (error) {
14015
- if (error instanceof _mastra_core_error.MastraError) throw error;
14158
+ if (error instanceof _mastra_core_error.MastraError || (0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
14016
14159
  throw new _mastra_core_error.MastraError({
14017
14160
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_BUFFERED_OBSERVATIONS", "FAILED"),
14018
14161
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -14094,7 +14237,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
14094
14237
  const lastObservedAt = input.lastObservedAt ?? (latestChunk?.lastObservedAt ? new Date(latestChunk.lastObservedAt) : /* @__PURE__ */ new Date());
14095
14238
  const lastObservedAtStr = lastObservedAt.toISOString();
14096
14239
  const boundary = `\n\n--- message boundary (${lastObservedAt.toISOString()}) ---\n\n`;
14097
- if ((await this.#db.client.query(`UPDATE ${tableName} SET
14240
+ if ((await this.#fenced(void 0, "swapBufferedToActive", (q) => q.query(`UPDATE ${tableName} SET
14098
14241
  "activeObservations" = CASE
14099
14242
  WHEN "activeObservations" IS NOT NULL AND "activeObservations" != ''
14100
14243
  THEN "activeObservations" || $10 || $1
@@ -14120,7 +14263,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
14120
14263
  nowStr,
14121
14264
  input.id,
14122
14265
  boundary
14123
- ])).rowCount === 0) return {
14266
+ ]))).rowCount === 0) return {
14124
14267
  chunksActivated: 0,
14125
14268
  messageTokensActivated: 0,
14126
14269
  observationTokensActivated: 0,
@@ -14148,7 +14291,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
14148
14291
  currentTask: latestChunkHints?.currentTask ?? void 0
14149
14292
  };
14150
14293
  } catch (error) {
14151
- if (error instanceof _mastra_core_error.MastraError) throw error;
14294
+ if (error instanceof _mastra_core_error.MastraError || (0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
14152
14295
  throw new _mastra_core_error.MastraError({
14153
14296
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "SWAP_BUFFERED_TO_ACTIVE", "FAILED"),
14154
14297
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -14164,7 +14307,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
14164
14307
  schemaName: getSchemaName$3(this.#schema)
14165
14308
  });
14166
14309
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
14167
- if ((await this.#db.client.query(`UPDATE ${tableName} SET
14310
+ if ((await this.#fenced(void 0, "updateBufferedReflection", (q) => q.query(`UPDATE ${tableName} SET
14168
14311
  "bufferedReflection" = CASE
14169
14312
  WHEN "bufferedReflection" IS NOT NULL AND "bufferedReflection" != ''
14170
14313
  THEN "bufferedReflection" || E'\\n\\n' || $1
@@ -14183,7 +14326,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
14183
14326
  nowStr,
14184
14327
  nowStr,
14185
14328
  input.id
14186
- ])).rowCount === 0) throw new _mastra_core_error.MastraError({
14329
+ ]))).rowCount === 0) throw new _mastra_core_error.MastraError({
14187
14330
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_BUFFERED_REFLECTION", "NOT_FOUND"),
14188
14331
  text: `Observational memory record not found: ${input.id}`,
14189
14332
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -14191,7 +14334,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
14191
14334
  details: { id: input.id }
14192
14335
  });
14193
14336
  } catch (error) {
14194
- if (error instanceof _mastra_core_error.MastraError) throw error;
14337
+ if (error instanceof _mastra_core_error.MastraError || (0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
14195
14338
  throw new _mastra_core_error.MastraError({
14196
14339
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_BUFFERED_REFLECTION", "FAILED"),
14197
14340
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -14248,9 +14391,9 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
14248
14391
  input.currentRecord.id
14249
14392
  ]);
14250
14393
  return newRecord;
14251
- });
14394
+ }, "swapBufferedReflectionToActive");
14252
14395
  } catch (error) {
14253
- if (error instanceof _mastra_core_error.MastraError) throw error;
14396
+ if (error instanceof _mastra_core_error.MastraError || (0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
14254
14397
  throw new _mastra_core_error.MastraError({
14255
14398
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "FAILED"),
14256
14399
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -21246,6 +21389,7 @@ var PromptBlocksPG = class PromptBlocksPG extends _mastra_core_storage.PromptBlo
21246
21389
  }
21247
21390
  async create(input) {
21248
21391
  const { promptBlock } = input;
21392
+ let draftInserted = false;
21249
21393
  try {
21250
21394
  const tableName = getTableName$5({
21251
21395
  indexName: _mastra_core_storage.TABLE_PROMPT_BLOCKS,
@@ -21267,6 +21411,7 @@ var PromptBlocksPG = class PromptBlocksPG extends _mastra_core_storage.PromptBlo
21267
21411
  nowIso,
21268
21412
  nowIso
21269
21413
  ]);
21414
+ draftInserted = true;
21270
21415
  const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = promptBlock;
21271
21416
  const versionId = crypto.randomUUID();
21272
21417
  await this.createVersion({
@@ -21287,14 +21432,14 @@ var PromptBlocksPG = class PromptBlocksPG extends _mastra_core_storage.PromptBlo
21287
21432
  updatedAt: now
21288
21433
  };
21289
21434
  } catch (error) {
21290
- if (error instanceof _mastra_core_error.MastraError) throw error;
21291
- try {
21435
+ if (draftInserted) try {
21292
21436
  const tableName = getTableName$5({
21293
21437
  indexName: _mastra_core_storage.TABLE_PROMPT_BLOCKS,
21294
21438
  schemaName: getSchemaName$5(this.#schema)
21295
21439
  });
21296
21440
  await this.#db.client.none(`DELETE FROM ${tableName} WHERE id = $1 AND status = 'draft' AND "activeVersionId" IS NULL`, [promptBlock.id]);
21297
21441
  } catch {}
21442
+ if (error instanceof _mastra_core_error.MastraError) throw error;
21298
21443
  throw new _mastra_core_error.MastraError({
21299
21444
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "CREATE_PROMPT_BLOCK", "FAILED"),
21300
21445
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -22190,6 +22335,7 @@ var ScorerDefinitionsPG = class ScorerDefinitionsPG extends _mastra_core_storage
22190
22335
  }
22191
22336
  async create(input) {
22192
22337
  const { scorerDefinition } = input;
22338
+ let draftInserted = false;
22193
22339
  try {
22194
22340
  const tableName = getTableName$5({
22195
22341
  indexName: _mastra_core_storage.TABLE_SCORER_DEFINITIONS,
@@ -22213,6 +22359,7 @@ var ScorerDefinitionsPG = class ScorerDefinitionsPG extends _mastra_core_storage
22213
22359
  nowIso,
22214
22360
  nowIso
22215
22361
  ]);
22362
+ draftInserted = true;
22216
22363
  const { id: _id, authorId: _authorId, organizationId: _organizationId, projectId: _projectId, metadata: _metadata, ...snapshotConfig } = scorerDefinition;
22217
22364
  const versionId = crypto.randomUUID();
22218
22365
  await this.createVersion({
@@ -22235,7 +22382,7 @@ var ScorerDefinitionsPG = class ScorerDefinitionsPG extends _mastra_core_storage
22235
22382
  updatedAt: now
22236
22383
  };
22237
22384
  } catch (error) {
22238
- try {
22385
+ if (draftInserted) try {
22239
22386
  const tableName = getTableName$5({
22240
22387
  indexName: _mastra_core_storage.TABLE_SCORER_DEFINITIONS,
22241
22388
  schemaName: getSchemaName$5(this.#schema)
@@ -23253,6 +23400,7 @@ var SkillsPG = class SkillsPG extends _mastra_core_storage.SkillsStorage {
23253
23400
  }
23254
23401
  async create(input) {
23255
23402
  const { skill } = input;
23403
+ let draftInserted = false;
23256
23404
  try {
23257
23405
  const tableName = getTableName$5({
23258
23406
  indexName: _mastra_core_storage.TABLE_SKILLS,
@@ -23276,6 +23424,7 @@ var SkillsPG = class SkillsPG extends _mastra_core_storage.SkillsStorage {
23276
23424
  nowIso,
23277
23425
  nowIso
23278
23426
  ]);
23427
+ draftInserted = true;
23279
23428
  const { id: _id, authorId: _authorId, visibility: _visibility, ...snapshotConfig } = skill;
23280
23429
  const versionId = crypto.randomUUID();
23281
23430
  await this.createVersion({
@@ -23297,7 +23446,7 @@ var SkillsPG = class SkillsPG extends _mastra_core_storage.SkillsStorage {
23297
23446
  updatedAt: now
23298
23447
  };
23299
23448
  } catch (error) {
23300
- try {
23449
+ if (draftInserted) try {
23301
23450
  const tableName = getTableName$5({
23302
23451
  indexName: _mastra_core_storage.TABLE_SKILLS,
23303
23452
  schemaName: getSchemaName$5(this.#schema)
@@ -24442,6 +24591,17 @@ function getTableName({ indexName, schemaName }) {
24442
24591
  const quotedIndexName = `"${indexName}"`;
24443
24592
  return schemaName ? `${schemaName}.${quotedIndexName}` : quotedIndexName;
24444
24593
  }
24594
+ const RUN_OWNER_COLUMNS = `generation, "ownerId", "leaseExpiresAt", ${DB_NOW_MS} AS "nowMs"`;
24595
+ function toRunOwnershipRecord(runId, row) {
24596
+ const leaseExpiresAt = row.leaseExpiresAt === null ? null : Number(row.leaseExpiresAt);
24597
+ return {
24598
+ runId,
24599
+ generation: Number(row.generation),
24600
+ ownerId: row.ownerId,
24601
+ leaseExpiresAt: leaseExpiresAt === null ? null : new Date(leaseExpiresAt),
24602
+ live: leaseExpiresAt !== null && leaseExpiresAt > Number(row.nowMs)
24603
+ };
24604
+ }
24445
24605
  /** Base name (before any schema prefix) of the expression index backing the status filter. */
24446
24606
  const WORKFLOW_SNAPSHOT_STATUS_INDEX = "mastra_workflow_snapshot_name_status_createdat_idx";
24447
24607
  /**
@@ -24526,12 +24686,24 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
24526
24686
  * Workflow run snapshots accumulate as runs execute. Anchored on the
24527
24687
  * timezone-aware `updatedAtZ` mirror column (last activity) so suspended or
24528
24688
  * long-running runs are not pruned by start age.
24689
+ *
24690
+ * Run ownership rows are anchored on `updatedAt` (epoch ms), which every
24691
+ * claim, renewal and release refreshes, so a live execution's row is never
24692
+ * older than its renew interval.
24529
24693
  */
24530
- static retentionTables = { workflowSnapshot: {
24531
- table: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
24532
- column: "updatedAtZ",
24533
- indexed: true
24534
- } };
24694
+ static retentionTables = {
24695
+ workflowSnapshot: {
24696
+ table: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
24697
+ column: "updatedAtZ",
24698
+ indexed: true
24699
+ },
24700
+ runOwnership: {
24701
+ table: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
24702
+ column: "updatedAt",
24703
+ indexed: true,
24704
+ anchorType: "epoch-ms"
24705
+ }
24706
+ };
24535
24707
  constructor(config) {
24536
24708
  super();
24537
24709
  const { client, readClient, schemaName, skipDefaultIndexes, indexes } = resolvePgConfig(config);
@@ -24588,6 +24760,12 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
24588
24760
  for (const idx of WorkflowsPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
24589
24761
  statements.push(`${workflowSnapshotStatusIndexSQL(workflowSnapshotStatusIndexName(parsedSchema), schemaName)};`);
24590
24762
  statements.push(`${workflowSnapshotThreadIdIndexSQL(workflowSnapshotThreadIdIndexName(parsedSchema), schemaName)};`);
24763
+ statements.push(generateTableSQL({
24764
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
24765
+ schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS],
24766
+ schemaName,
24767
+ includeAllConstraints: true
24768
+ }));
24591
24769
  return statements;
24592
24770
  }
24593
24771
  /**
@@ -24631,6 +24809,10 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
24631
24809
  schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT],
24632
24810
  ifNotExists: ["resourceId"]
24633
24811
  });
24812
+ await this.#db.createTable({
24813
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
24814
+ schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS]
24815
+ });
24634
24816
  await this.createDefaultIndexes();
24635
24817
  await this.createCustomIndexes();
24636
24818
  }
@@ -24658,13 +24840,13 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
24658
24840
  }
24659
24841
  }
24660
24842
  }
24661
- /** Delete workflow run snapshots older than the `workflowSnapshot` policy's `maxAge`, batched. */
24843
+ /** Delete workflow run snapshots and run ownership rows older than their policy's `maxAge`, batched. */
24662
24844
  async prune(policies, options) {
24663
24845
  await this.ensureRetentionIndexes(policies);
24664
24846
  const targets = resolveTargets({
24665
24847
  policies,
24666
24848
  descriptor: WorkflowsPG.retentionTables,
24667
- order: ["workflowSnapshot"]
24849
+ order: ["workflowSnapshot", "runOwnership"]
24668
24850
  });
24669
24851
  return runPrune({
24670
24852
  db: this.#db,
@@ -24686,10 +24868,126 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
24686
24868
  }
24687
24869
  async dangerouslyClearAll() {
24688
24870
  await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT });
24871
+ await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS });
24689
24872
  }
24690
- async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state }) {
24873
+ supportsRunFencing() {
24874
+ return true;
24875
+ }
24876
+ #runOwnersTable() {
24877
+ return getTableName({
24878
+ indexName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
24879
+ schemaName: getSchemaName(this.#schema)
24880
+ });
24881
+ }
24882
+ async #readRunOwner(q, runId) {
24883
+ const row = await q.oneOrNone(`SELECT ${RUN_OWNER_COLUMNS} FROM ${this.#runOwnersTable()} WHERE "runId" = $1`, [runId]);
24884
+ return row ? toRunOwnershipRecord(runId, row) : null;
24885
+ }
24886
+ #ownershipError(operation, runId, error) {
24887
+ return new _mastra_core_error.MastraError({
24888
+ id: (0, _mastra_core_storage.createStorageErrorId)("PG", operation, "FAILED"),
24889
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
24890
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
24891
+ details: { runId }
24892
+ }, error);
24893
+ }
24894
+ async claimRunOwnership({ runId, ownerId, leaseMs, force, expectedGeneration }) {
24895
+ const table = this.#runOwnersTable();
24896
+ try {
24897
+ return await this.#db.client.tx(async (t) => {
24898
+ if (!await t.oneOrNone(`SELECT 1 FROM ${table} WHERE "runId" = $1 FOR UPDATE`, [runId])) {
24899
+ if (expectedGeneration !== void 0 && expectedGeneration !== 0) return {
24900
+ acquired: false,
24901
+ record: null
24902
+ };
24903
+ const inserted = await t.oneOrNone(`INSERT INTO ${table} ("runId", generation, "ownerId", "leaseExpiresAt", "updatedAt")
24904
+ VALUES ($1, GREATEST(1, ${DB_NOW_MS}), $2, ${DB_NOW_MS} + $3, ${DB_NOW_MS})
24905
+ ON CONFLICT ("runId") DO NOTHING
24906
+ RETURNING ${RUN_OWNER_COLUMNS}`, [
24907
+ runId,
24908
+ ownerId,
24909
+ leaseMs
24910
+ ]);
24911
+ if (inserted) return {
24912
+ acquired: true,
24913
+ record: toRunOwnershipRecord(runId, inserted)
24914
+ };
24915
+ }
24916
+ const claimed = await t.oneOrNone(`UPDATE ${table}
24917
+ SET generation = generation + 1, "ownerId" = $2, "leaseExpiresAt" = ${DB_NOW_MS} + $3,
24918
+ "updatedAt" = ${DB_NOW_MS}
24919
+ WHERE "runId" = $1
24920
+ AND ($4::bigint IS NULL OR generation = $4)
24921
+ AND ($5::boolean OR "leaseExpiresAt" IS NULL OR "leaseExpiresAt" <= ${DB_NOW_MS})
24922
+ RETURNING ${RUN_OWNER_COLUMNS}`, [
24923
+ runId,
24924
+ ownerId,
24925
+ leaseMs,
24926
+ expectedGeneration ?? null,
24927
+ force === true
24928
+ ]);
24929
+ if (claimed) return {
24930
+ acquired: true,
24931
+ record: toRunOwnershipRecord(runId, claimed)
24932
+ };
24933
+ return {
24934
+ acquired: false,
24935
+ record: await this.#readRunOwner(t, runId)
24936
+ };
24937
+ });
24938
+ } catch (error) {
24939
+ throw this.#ownershipError("CLAIM_RUN_OWNERSHIP", runId, error);
24940
+ }
24941
+ }
24942
+ async renewRunOwnership({ leaseMs, ...fence }) {
24943
+ try {
24944
+ const renewed = await this.#db.client.oneOrNone(`UPDATE ${this.#runOwnersTable()}
24945
+ SET "leaseExpiresAt" = ${DB_NOW_MS} + $4, "updatedAt" = ${DB_NOW_MS}
24946
+ WHERE "runId" = $1 AND generation = $2 AND "ownerId" = $3 AND "leaseExpiresAt" IS NOT NULL
24947
+ RETURNING ${RUN_OWNER_COLUMNS}`, [
24948
+ fence.runId,
24949
+ fence.generation,
24950
+ fence.ownerId,
24951
+ leaseMs
24952
+ ]);
24953
+ if (renewed) return {
24954
+ renewed: true,
24955
+ record: toRunOwnershipRecord(fence.runId, renewed)
24956
+ };
24957
+ return {
24958
+ renewed: false,
24959
+ record: await this.#readRunOwner(this.#db.client, fence.runId)
24960
+ };
24961
+ } catch (error) {
24962
+ throw this.#ownershipError("RENEW_RUN_OWNERSHIP", fence.runId, error);
24963
+ }
24964
+ }
24965
+ async releaseRunOwnership(fence) {
24966
+ try {
24967
+ return await this.#db.client.oneOrNone(`UPDATE ${this.#runOwnersTable()}
24968
+ SET "leaseExpiresAt" = NULL, "updatedAt" = ${DB_NOW_MS}
24969
+ WHERE "runId" = $1 AND generation = $2 AND "ownerId" = $3
24970
+ RETURNING "runId"`, [
24971
+ fence.runId,
24972
+ fence.generation,
24973
+ fence.ownerId
24974
+ ]) !== null;
24975
+ } catch (error) {
24976
+ throw this.#ownershipError("RELEASE_RUN_OWNERSHIP", fence.runId, error);
24977
+ }
24978
+ }
24979
+ async getRunOwnership({ runId }) {
24980
+ try {
24981
+ return await this.#readRunOwner(this.#db.client, runId);
24982
+ } catch (error) {
24983
+ throw this.#ownershipError("GET_RUN_OWNERSHIP", runId, error);
24984
+ }
24985
+ }
24986
+ async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state, fence: explicitFence }) {
24987
+ const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence, runId);
24691
24988
  try {
24692
24989
  return await this.#db.client.tx(async (t) => {
24990
+ if (fence) await assertRunFence(t, this.#runOwnersTable(), fence, "updateWorkflowResults");
24693
24991
  const tableName = getTableName({
24694
24992
  indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
24695
24993
  schemaName: getSchemaName(this.#schema)
@@ -24739,6 +25037,7 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
24739
25037
  return snapshot.context;
24740
25038
  });
24741
25039
  } catch (error) {
25040
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
24742
25041
  throw new _mastra_core_error.MastraError({
24743
25042
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
24744
25043
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -24751,9 +25050,11 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
24751
25050
  }, error);
24752
25051
  }
24753
25052
  }
24754
- async updateWorkflowState({ workflowName, runId, opts }) {
25053
+ async updateWorkflowState({ workflowName, runId, opts, fence: explicitFence }) {
25054
+ const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence, runId);
24755
25055
  try {
24756
25056
  return await this.#db.client.tx(async (t) => {
25057
+ if (fence) await assertRunFence(t, this.#runOwnersTable(), fence, "updateWorkflowState");
24757
25058
  const tableName = getTableName({
24758
25059
  indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
24759
25060
  schemaName: getSchemaName(this.#schema)
@@ -24783,6 +25084,7 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
24783
25084
  return updatedSnapshot;
24784
25085
  });
24785
25086
  } catch (error) {
25087
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
24786
25088
  throw new _mastra_core_error.MastraError({
24787
25089
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_WORKFLOW_STATE", "FAILED"),
24788
25090
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -24794,13 +25096,13 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
24794
25096
  }, error);
24795
25097
  }
24796
25098
  }
24797
- async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt }) {
25099
+ async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
24798
25100
  try {
24799
25101
  const now = /* @__PURE__ */ new Date();
24800
25102
  const createdAtValue = createdAt ? createdAt : now;
24801
25103
  const updatedAtValue = updatedAt ? updatedAt : now;
24802
25104
  const sanitizedSnapshot = toPgJson(snapshot);
24803
- await this.#db.client.none(`INSERT INTO ${getTableName({
25105
+ await withRunFence(this.#db.client, this.#runOwnersTable(), (0, _mastra_core_storage.resolveRunFence)(this, fence, runId), "persistWorkflowSnapshot", (q) => q.none(`INSERT INTO ${getTableName({
24804
25106
  indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
24805
25107
  schemaName: getSchemaName(this.#schema)
24806
25108
  })} AS t
@@ -24816,8 +25118,9 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
24816
25118
  updatedAtValue,
24817
25119
  createdAtValue,
24818
25120
  updatedAtValue
24819
- ]);
25121
+ ]));
24820
25122
  } catch (error) {
25123
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
24821
25124
  throw new _mastra_core_error.MastraError({
24822
25125
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
24823
25126
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -24883,13 +25186,14 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
24883
25186
  }, error);
24884
25187
  }
24885
25188
  }
24886
- async deleteWorkflowRunById({ runId, workflowName }) {
25189
+ async deleteWorkflowRunById({ runId, workflowName, fence }) {
24887
25190
  try {
24888
- await this.#db.client.none(`DELETE FROM ${getTableName({
25191
+ await withRunFence(this.#db.client, this.#runOwnersTable(), (0, _mastra_core_storage.resolveRunFence)(this, fence, runId), "deleteWorkflowRunById", (q) => q.none(`DELETE FROM ${getTableName({
24889
25192
  indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
24890
25193
  schemaName: getSchemaName(this.#schema)
24891
- })} WHERE run_id = $1 AND workflow_name = $2`, [runId, workflowName]);
25194
+ })} WHERE run_id = $1 AND workflow_name = $2`, [runId, workflowName]));
24892
25195
  } catch (error) {
25196
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
24893
25197
  throw new _mastra_core_error.MastraError({
24894
25198
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
24895
25199
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -25101,6 +25405,7 @@ var WorkspacesPG = class WorkspacesPG extends _mastra_core_storage.WorkspacesSto
25101
25405
  }
25102
25406
  async create(input) {
25103
25407
  const { workspace } = input;
25408
+ let draftInserted = false;
25104
25409
  try {
25105
25410
  const tableName = getTableName$5({
25106
25411
  indexName: _mastra_core_storage.TABLE_WORKSPACES,
@@ -25122,6 +25427,7 @@ var WorkspacesPG = class WorkspacesPG extends _mastra_core_storage.WorkspacesSto
25122
25427
  nowIso,
25123
25428
  nowIso
25124
25429
  ]);
25430
+ draftInserted = true;
25125
25431
  const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = workspace;
25126
25432
  const versionId = crypto.randomUUID();
25127
25433
  await this.createVersion({
@@ -25142,7 +25448,7 @@ var WorkspacesPG = class WorkspacesPG extends _mastra_core_storage.WorkspacesSto
25142
25448
  updatedAt: now
25143
25449
  };
25144
25450
  } catch (error) {
25145
- try {
25451
+ if (draftInserted) try {
25146
25452
  const tableName = getTableName$5({
25147
25453
  indexName: _mastra_core_storage.TABLE_WORKSPACES,
25148
25454
  schemaName: getSchemaName$5(this.#schema)