@mastra/mssql 1.10.6-alpha.0 → 1.10.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
@@ -2162,9 +2162,17 @@ function withRunFence(pool, claimsTable, fence, operation, write) {
2162
2162
  * duplicate key if they don't. On retry the row exists and the claim
2163
2163
  * serializes behind its row lock.
2164
2164
  */
2165
- async function claimTransaction(pool, fn) {
2165
+ function claimTransaction(pool, fn) {
2166
+ return retryWriteRace(() => inTransaction(pool, fn));
2167
+ }
2168
+ /**
2169
+ * Runs `write` again when it lost a race to insert the same row: a deadlock
2170
+ * victim or a duplicate key. `write` must be a whole statement or
2171
+ * transaction, since a deadlock victim's transaction has already ended.
2172
+ */
2173
+ async function retryWriteRace(write) {
2166
2174
  for (let attempt = 1;; attempt++) try {
2167
- return await inTransaction(pool, fn);
2175
+ return await write();
2168
2176
  } catch (error) {
2169
2177
  const number = error.number;
2170
2178
  if (attempt >= MAX_CLAIM_ATTEMPTS || !(number === ERR_DEADLOCK_VICTIM || number === ERR_PRIMARY_KEY || number === ERR_UNIQUE_INDEX)) throw error;
@@ -4636,6 +4644,9 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends WorkflowsStorage {
4636
4644
  this.skipDefaultIndexes = skipDefaultIndexes;
4637
4645
  this.indexes = indexes?.filter((idx) => WorkflowsMSSQL.MANAGED_TABLES.includes(idx.table));
4638
4646
  }
4647
+ supportsGuardedSnapshotWrites() {
4648
+ return true;
4649
+ }
4639
4650
  supportsConcurrentUpdates() {
4640
4651
  return true;
4641
4652
  }
@@ -4945,14 +4956,16 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends WorkflowsStorage {
4945
4956
  }, error);
4946
4957
  }
4947
4958
  }
4948
- async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, fence }) {
4959
+ async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, fence, expectedStatus }) {
4949
4960
  const table = getTableName({
4950
4961
  indexName: TABLE_WORKFLOW_SNAPSHOT,
4951
4962
  schemaName: getSchemaName(this.schema)
4952
4963
  });
4953
4964
  const now = (/* @__PURE__ */ new Date()).toISOString();
4965
+ const expectedStatuses = expectedStatus === void 0 ? void 0 : Array.isArray(expectedStatus) ? expectedStatus : [expectedStatus];
4966
+ const guard = expectedStatuses === void 0 ? "" : expectedStatuses.length === 0 ? " AND 1 = 0" : ` AND JSON_VALUE(target.[snapshot], '$.status') IN (${expectedStatuses.map((_, i) => `@expectedStatus${i}`).join(", ")})`;
4954
4967
  try {
4955
- await withRunFence(this.pool, this.#runOwnersTable, resolveRunFence(this, fence, runId), "persistWorkflowSnapshot", async (q) => {
4968
+ return await retryWriteRace(() => withRunFence(this.pool, this.#runOwnersTable, resolveRunFence(this, fence, runId), "persistWorkflowSnapshot", async (q) => {
4956
4969
  const request = q.request();
4957
4970
  request.input("workflow_name", workflowName);
4958
4971
  request.input("run_id", runId);
@@ -4960,17 +4973,18 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends WorkflowsStorage {
4960
4973
  request.input("snapshot", JSON.stringify(snapshot));
4961
4974
  request.input("createdAt", sql.DateTime2, new Date(now));
4962
4975
  request.input("updatedAt", sql.DateTime2, new Date(now));
4976
+ expectedStatuses?.forEach((status, i) => request.input(`expectedStatus${i}`, status));
4963
4977
  const mergeSql = `MERGE INTO ${table} AS target
4964
4978
  USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
4965
4979
  ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
4966
- WHEN MATCHED THEN UPDATE SET
4980
+ WHEN MATCHED${guard} THEN UPDATE SET
4967
4981
  resourceId = @resourceId,
4968
4982
  snapshot = @snapshot,
4969
4983
  [updatedAt] = @updatedAt
4970
4984
  WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, resourceId, snapshot, [createdAt], [updatedAt])
4971
4985
  VALUES (@workflow_name, @run_id, @resourceId, @snapshot, @createdAt, @updatedAt);`;
4972
- await request.query(mergeSql);
4973
- });
4986
+ return ((await request.query(mergeSql)).rowsAffected[0] ?? 0) > 0;
4987
+ }));
4974
4988
  } catch (error) {
4975
4989
  if (isRunFenceConflictError(error)) throw error;
4976
4990
  throw new MastraError({
@@ -4984,6 +4998,27 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends WorkflowsStorage {
4984
4998
  }, error);
4985
4999
  }
4986
5000
  }
5001
+ async loadWorkflowRunStatus({ workflowName, runId }) {
5002
+ try {
5003
+ const request = this.pool.request();
5004
+ request.input("workflow_name", workflowName);
5005
+ request.input("run_id", runId);
5006
+ return (await request.query(`SELECT TOP 1 JSON_VALUE([snapshot], '$.status') AS status FROM ${getTableName({
5007
+ indexName: TABLE_WORKFLOW_SNAPSHOT,
5008
+ schemaName: getSchemaName(this.schema)
5009
+ })} WHERE workflow_name = @workflow_name AND run_id = @run_id`)).recordset?.[0]?.status ?? null;
5010
+ } catch (error) {
5011
+ throw new MastraError({
5012
+ id: createStorageErrorId("MSSQL", "LOAD_WORKFLOW_RUN_STATUS", "FAILED"),
5013
+ domain: ErrorDomain.STORAGE,
5014
+ category: ErrorCategory.THIRD_PARTY,
5015
+ details: {
5016
+ workflowName,
5017
+ runId
5018
+ }
5019
+ }, error);
5020
+ }
5021
+ }
4987
5022
  async loadWorkflowSnapshot({ workflowName, runId }) {
4988
5023
  try {
4989
5024
  const result = await this.db.load({