@mastra/mssql 1.10.2 → 1.10.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/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +315 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +316 -68
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +2 -1
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/db/run-fencing.d.ts +38 -0
- package/dist/storage/db/run-fencing.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +18 -7
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +17 -5
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/docs/SKILL.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -2127,6 +2127,73 @@ var BackgroundTasksMSSQL = class BackgroundTasksMSSQL extends _mastra_core_stora
|
|
|
2127
2127
|
}
|
|
2128
2128
|
};
|
|
2129
2129
|
//#endregion
|
|
2130
|
+
//#region src/storage/db/run-fencing.ts
|
|
2131
|
+
/**
|
|
2132
|
+
* Epoch milliseconds on the database clock. SQL Server evaluates
|
|
2133
|
+
* `SYSUTCDATETIME()` once when a statement starts, so a statement that waits
|
|
2134
|
+
* on a lock compares against the time it started. That error only makes an
|
|
2135
|
+
* existing lease look longer and a new lease shorter.
|
|
2136
|
+
*/
|
|
2137
|
+
const DB_NOW_MS = `DATEDIFF_BIG(MILLISECOND, '1970-01-01', SYSUTCDATETIME())`;
|
|
2138
|
+
const ERR_DEADLOCK_VICTIM = 1205;
|
|
2139
|
+
const ERR_UNIQUE_INDEX = 2601;
|
|
2140
|
+
const ERR_PRIMARY_KEY = 2627;
|
|
2141
|
+
const MAX_CLAIM_ATTEMPTS = 5;
|
|
2142
|
+
/**
|
|
2143
|
+
* The check half of a fenced write; call it first inside the write's
|
|
2144
|
+
* transaction. `HOLDLOCK` keeps the shared lock on the claim row until commit,
|
|
2145
|
+
* even on databases with read-committed snapshot on, so a claim that updates
|
|
2146
|
+
* the row waits for the write to finish, and every later write sees the new
|
|
2147
|
+
* claim.
|
|
2148
|
+
*/
|
|
2149
|
+
async function assertRunFence(q, claimsTable, fence, operation) {
|
|
2150
|
+
const row = (await q.request().input("runId", fence.runId).query(`SELECT [generation], [ownerId] FROM ${claimsTable} WITH (HOLDLOCK) WHERE [runId] = @runId`)).recordset[0];
|
|
2151
|
+
if (!(0, _mastra_core_storage.matchesRunFence)(row ? {
|
|
2152
|
+
generation: Number(row.generation),
|
|
2153
|
+
ownerId: String(row.ownerId)
|
|
2154
|
+
} : null, fence)) throw new _mastra_core_storage.RunFenceConflictError(fence, operation);
|
|
2155
|
+
}
|
|
2156
|
+
/** Runs `fn` in a transaction. */
|
|
2157
|
+
async function inTransaction(pool, fn) {
|
|
2158
|
+
const transaction = pool.transaction();
|
|
2159
|
+
await transaction.begin();
|
|
2160
|
+
try {
|
|
2161
|
+
const result = await fn(transaction);
|
|
2162
|
+
await transaction.commit();
|
|
2163
|
+
return result;
|
|
2164
|
+
} catch (error) {
|
|
2165
|
+
await transaction.rollback().catch(() => {});
|
|
2166
|
+
throw error;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
/**
|
|
2170
|
+
* Runs `write` directly when it carries no fence, or in a transaction behind
|
|
2171
|
+
* the fence check when it does.
|
|
2172
|
+
*/
|
|
2173
|
+
function withRunFence(pool, claimsTable, fence, operation, write) {
|
|
2174
|
+
if (!fence) return write(pool);
|
|
2175
|
+
return inTransaction(pool, async (transaction) => {
|
|
2176
|
+
await assertRunFence(transaction, claimsTable, fence, operation);
|
|
2177
|
+
return write(transaction);
|
|
2178
|
+
});
|
|
2179
|
+
}
|
|
2180
|
+
/**
|
|
2181
|
+
* Runs a transaction that creates or updates a run's claim row, retrying when
|
|
2182
|
+
* two first claims race to insert it. Claims read the row with
|
|
2183
|
+
* `UPDLOCK, HOLDLOCK`, which locks the key range of a missing row, so racing
|
|
2184
|
+
* first claims normally queue; the retry covers a deadlock victim or a
|
|
2185
|
+
* duplicate key if they don't. On retry the row exists and the claim
|
|
2186
|
+
* serializes behind its row lock.
|
|
2187
|
+
*/
|
|
2188
|
+
async function claimTransaction(pool, fn) {
|
|
2189
|
+
for (let attempt = 1;; attempt++) try {
|
|
2190
|
+
return await inTransaction(pool, fn);
|
|
2191
|
+
} catch (error) {
|
|
2192
|
+
const number = error.number;
|
|
2193
|
+
if (attempt >= MAX_CLAIM_ATTEMPTS || !(number === ERR_DEADLOCK_VICTIM || number === ERR_PRIMARY_KEY || number === ERR_UNIQUE_INDEX)) throw error;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
//#endregion
|
|
2130
2197
|
//#region src/storage/domains/memory/index.ts
|
|
2131
2198
|
function bindMssqlMetadataParams(request, params) {
|
|
2132
2199
|
for (const [paramName, paramValue] of Object.entries(params)) request.input(paramName, paramValue);
|
|
@@ -2216,9 +2283,69 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2216
2283
|
tableName: _mastra_core_storage.TABLE_RESOURCES,
|
|
2217
2284
|
schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_RESOURCES]
|
|
2218
2285
|
});
|
|
2286
|
+
await this.db.createTable({
|
|
2287
|
+
tableName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
|
|
2288
|
+
schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_MEMORY_RUN_FENCES]
|
|
2289
|
+
});
|
|
2219
2290
|
await this.createDefaultIndexes();
|
|
2220
2291
|
await this.createCustomIndexes();
|
|
2221
2292
|
}
|
|
2293
|
+
supportsRunFencing() {
|
|
2294
|
+
return true;
|
|
2295
|
+
}
|
|
2296
|
+
get #runFencesTable() {
|
|
2297
|
+
return getTableName({
|
|
2298
|
+
indexName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
|
|
2299
|
+
schemaName: getSchemaName(this.schema)
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2302
|
+
async raiseRunFence(fence) {
|
|
2303
|
+
const table = this.#runFencesTable;
|
|
2304
|
+
try {
|
|
2305
|
+
return await claimTransaction(this.pool, async (transaction) => {
|
|
2306
|
+
const row = (await transaction.request().input("runId", fence.runId).query(`SELECT [generation], [ownerId] FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE [runId] = @runId`)).recordset[0];
|
|
2307
|
+
if (!row) {
|
|
2308
|
+
await transaction.request().input("runId", fence.runId).input("generation", mssql.default.BigInt, fence.generation).input("ownerId", fence.ownerId).query(`INSERT INTO ${table} ([runId], [generation], [ownerId]) VALUES (@runId, @generation, @ownerId)`);
|
|
2309
|
+
return true;
|
|
2310
|
+
}
|
|
2311
|
+
const generation = Number(row.generation);
|
|
2312
|
+
const raises = generation < fence.generation;
|
|
2313
|
+
const reaffirms = generation === fence.generation && String(row.ownerId) === fence.ownerId;
|
|
2314
|
+
if (!raises && !reaffirms) return false;
|
|
2315
|
+
await transaction.request().input("runId", fence.runId).input("generation", mssql.default.BigInt, fence.generation).input("ownerId", fence.ownerId).query(`UPDATE ${table} SET [generation] = @generation, [ownerId] = @ownerId, [retiredAt] = NULL WHERE [runId] = @runId`);
|
|
2316
|
+
return true;
|
|
2317
|
+
});
|
|
2318
|
+
} catch (error) {
|
|
2319
|
+
throw new _mastra_core_error.MastraError({
|
|
2320
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "RAISE_RUN_FENCE", "FAILED"),
|
|
2321
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
2322
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
2323
|
+
details: { runId: fence.runId }
|
|
2324
|
+
}, error);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
async retireRunFence(fence) {
|
|
2328
|
+
try {
|
|
2329
|
+
return (await this.pool.request().input("runId", fence.runId).input("generation", mssql.default.BigInt, fence.generation).input("ownerId", fence.ownerId).query(`UPDATE ${this.#runFencesTable} SET [retiredAt] = ${DB_NOW_MS}
|
|
2330
|
+
WHERE [runId] = @runId AND [generation] = @generation AND [ownerId] = @ownerId`)).rowsAffected[0] === 1;
|
|
2331
|
+
} catch (error) {
|
|
2332
|
+
throw new _mastra_core_error.MastraError({
|
|
2333
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "RETIRE_RUN_FENCE", "FAILED"),
|
|
2334
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
2335
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
2336
|
+
details: { runId: fence.runId }
|
|
2337
|
+
}, error);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
/** Runs `write` behind its own fence, otherwise the one in scope for this store. */
|
|
2341
|
+
#fenced(fence, operation, write) {
|
|
2342
|
+
return withRunFence(this.pool, this.#runFencesTable, (0, _mastra_core_storage.resolveRunFence)(this, fence), operation, write);
|
|
2343
|
+
}
|
|
2344
|
+
/** The fence check for a write that already runs in its own transaction. */
|
|
2345
|
+
async #assertFence(transaction, fence, operation) {
|
|
2346
|
+
const resolved = (0, _mastra_core_storage.resolveRunFence)(this, fence);
|
|
2347
|
+
if (resolved) await assertRunFence(transaction, this.#runFencesTable, resolved, operation);
|
|
2348
|
+
}
|
|
2222
2349
|
/**
|
|
2223
2350
|
* Returns default index definitions for the memory domain tables.
|
|
2224
2351
|
* IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
|
|
@@ -2261,6 +2388,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2261
2388
|
await this.db.clearTable({ tableName: _mastra_core_storage.TABLE_MESSAGES });
|
|
2262
2389
|
await this.db.clearTable({ tableName: _mastra_core_storage.TABLE_THREADS });
|
|
2263
2390
|
await this.db.clearTable({ tableName: _mastra_core_storage.TABLE_RESOURCES });
|
|
2391
|
+
await this.db.clearTable({ tableName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES });
|
|
2264
2392
|
}
|
|
2265
2393
|
async getThreadById({ threadId, resourceId }) {
|
|
2266
2394
|
try {
|
|
@@ -2411,7 +2539,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2411
2539
|
throw mastraError;
|
|
2412
2540
|
}
|
|
2413
2541
|
}
|
|
2414
|
-
async saveThread({ thread }) {
|
|
2542
|
+
async saveThread({ thread, fence }) {
|
|
2415
2543
|
try {
|
|
2416
2544
|
const mergeSql = `MERGE INTO ${getTableName({
|
|
2417
2545
|
indexName: _mastra_core_storage.TABLE_THREADS,
|
|
@@ -2428,18 +2556,21 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2428
2556
|
WHEN NOT MATCHED THEN
|
|
2429
2557
|
INSERT (id, [resourceId], title, metadata, [createdAt], [updatedAt])
|
|
2430
2558
|
VALUES (@id, @resourceId, @title, @metadata, @createdAt, @updatedAt);`;
|
|
2431
|
-
const req = this.pool.request();
|
|
2432
|
-
req.input("id", thread.id);
|
|
2433
|
-
req.input("resourceId", thread.resourceId);
|
|
2434
|
-
req.input("title", thread.title);
|
|
2435
2559
|
const metadata = thread.metadata ? JSON.stringify(thread.metadata) : null;
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2560
|
+
await this.#fenced(fence, "saveThread", (q) => {
|
|
2561
|
+
const req = q.request();
|
|
2562
|
+
req.input("id", thread.id);
|
|
2563
|
+
req.input("resourceId", thread.resourceId);
|
|
2564
|
+
req.input("title", thread.title);
|
|
2565
|
+
if (metadata === null) req.input("metadata", mssql.default.NVarChar, null);
|
|
2566
|
+
else req.input("metadata", metadata);
|
|
2567
|
+
req.input("createdAt", mssql.default.DateTime2, thread.createdAt);
|
|
2568
|
+
req.input("updatedAt", mssql.default.DateTime2, thread.updatedAt);
|
|
2569
|
+
return req.query(mergeSql);
|
|
2570
|
+
});
|
|
2441
2571
|
return thread;
|
|
2442
2572
|
} catch (error) {
|
|
2573
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
2443
2574
|
throw new _mastra_core_error.MastraError({
|
|
2444
2575
|
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "SAVE_THREAD", "FAILED"),
|
|
2445
2576
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -2451,7 +2582,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2451
2582
|
/**
|
|
2452
2583
|
* Updates a thread's title and metadata, merging with existing metadata. Returns the updated thread.
|
|
2453
2584
|
*/
|
|
2454
|
-
async updateThread({ id, title, metadata }) {
|
|
2585
|
+
async updateThread({ id, title, metadata, fence }) {
|
|
2455
2586
|
const existingThread = await this.getThreadById({ threadId: id });
|
|
2456
2587
|
if (!existingThread) throw new _mastra_core_error.MastraError({
|
|
2457
2588
|
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "UPDATE_THREAD", "NOT_FOUND"),
|
|
@@ -2477,12 +2608,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2477
2608
|
[updatedAt] = @updatedAt
|
|
2478
2609
|
OUTPUT INSERTED.*
|
|
2479
2610
|
WHERE id = @id`;
|
|
2480
|
-
const
|
|
2481
|
-
req.input("id", id);
|
|
2482
|
-
req.input("title", title ?? existingThread.title);
|
|
2483
|
-
req.input("metadata", JSON.stringify(mergedMetadata));
|
|
2484
|
-
req.input("updatedAt", /* @__PURE__ */ new Date());
|
|
2485
|
-
const result = await req.query(sql);
|
|
2611
|
+
const result = await this.#fenced(fence, "updateThread", (q) => q.request().input("id", id).input("title", title ?? existingThread.title).input("metadata", JSON.stringify(mergedMetadata)).input("updatedAt", /* @__PURE__ */ new Date()).query(sql));
|
|
2486
2612
|
let thread = result.recordset && result.recordset[0];
|
|
2487
2613
|
if (thread && "seq_id" in thread) {
|
|
2488
2614
|
const { seq_id, ...rest } = thread;
|
|
@@ -2505,6 +2631,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2505
2631
|
updatedAt: thread.updatedAt
|
|
2506
2632
|
};
|
|
2507
2633
|
} catch (error) {
|
|
2634
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
2508
2635
|
throw new _mastra_core_error.MastraError({
|
|
2509
2636
|
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "UPDATE_THREAD", "FAILED"),
|
|
2510
2637
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -2870,7 +2997,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2870
2997
|
throw mastraError;
|
|
2871
2998
|
}
|
|
2872
2999
|
}
|
|
2873
|
-
async saveMessages({ messages }) {
|
|
3000
|
+
async saveMessages({ messages, fence }) {
|
|
2874
3001
|
if (messages.length === 0) return { messages: [] };
|
|
2875
3002
|
const threadId = messages[0]?.threadId;
|
|
2876
3003
|
if (!threadId) throw new _mastra_core_error.MastraError({
|
|
@@ -2898,6 +3025,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2898
3025
|
const transaction = this.pool.transaction();
|
|
2899
3026
|
await transaction.begin();
|
|
2900
3027
|
try {
|
|
3028
|
+
await this.#assertFence(transaction, fence, "saveMessages");
|
|
2901
3029
|
for (const message of messages) {
|
|
2902
3030
|
if (!message.threadId) throw new Error(`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`);
|
|
2903
3031
|
if (!message.resourceId) throw new Error(`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`);
|
|
@@ -2945,6 +3073,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2945
3073
|
});
|
|
2946
3074
|
return { messages: new _mastra_core_agent.MessageList().add(messagesWithParsedContent, "memory").get.all.db() };
|
|
2947
3075
|
} catch (error) {
|
|
3076
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
2948
3077
|
throw new _mastra_core_error.MastraError({
|
|
2949
3078
|
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "SAVE_MESSAGES", "FAILED"),
|
|
2950
3079
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -2953,7 +3082,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2953
3082
|
}, error);
|
|
2954
3083
|
}
|
|
2955
3084
|
}
|
|
2956
|
-
async updateMessages({ messages }) {
|
|
3085
|
+
async updateMessages({ messages, fence }) {
|
|
2957
3086
|
if (!messages || messages.length === 0) return [];
|
|
2958
3087
|
const messageIds = messages.map((m) => m.id);
|
|
2959
3088
|
const idParams = messageIds.map((_, i) => `@id${i}`).join(", ");
|
|
@@ -2977,6 +3106,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
2977
3106
|
const transaction = this.pool.transaction();
|
|
2978
3107
|
try {
|
|
2979
3108
|
await transaction.begin();
|
|
3109
|
+
await this.#assertFence(transaction, fence, "updateMessages");
|
|
2980
3110
|
for (const existingMessage of existingMessages) {
|
|
2981
3111
|
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
2982
3112
|
if (!updatePayload) continue;
|
|
@@ -3029,6 +3159,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
3029
3159
|
await transaction.commit();
|
|
3030
3160
|
} catch (error) {
|
|
3031
3161
|
await transaction.rollback();
|
|
3162
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
3032
3163
|
throw new _mastra_core_error.MastraError({
|
|
3033
3164
|
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "UPDATE_MESSAGES", "FAILED"),
|
|
3034
3165
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -3044,7 +3175,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
3044
3175
|
return message;
|
|
3045
3176
|
});
|
|
3046
3177
|
}
|
|
3047
|
-
async deleteMessages(messageIds) {
|
|
3178
|
+
async deleteMessages(messageIds, options) {
|
|
3048
3179
|
if (!messageIds || messageIds.length === 0) return;
|
|
3049
3180
|
try {
|
|
3050
3181
|
const messageTableName = getTableName({
|
|
@@ -3064,6 +3195,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
3064
3195
|
const transaction = this.pool.transaction();
|
|
3065
3196
|
await transaction.begin();
|
|
3066
3197
|
try {
|
|
3198
|
+
await this.#assertFence(transaction, options?.fence, "deleteMessages");
|
|
3067
3199
|
const deleteRequest = transaction.request();
|
|
3068
3200
|
messageIds.forEach((id, idx) => {
|
|
3069
3201
|
deleteRequest.input(`p${idx + 1}`, id);
|
|
@@ -3082,6 +3214,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
3082
3214
|
throw error;
|
|
3083
3215
|
}
|
|
3084
3216
|
} catch (error) {
|
|
3217
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
3085
3218
|
throw new _mastra_core_error.MastraError({
|
|
3086
3219
|
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "DELETE_MESSAGES", "FAILED"),
|
|
3087
3220
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -3129,7 +3262,7 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
3129
3262
|
});
|
|
3130
3263
|
return resource;
|
|
3131
3264
|
}
|
|
3132
|
-
async updateResource({ resourceId, workingMemory, metadata }) {
|
|
3265
|
+
async updateResource({ resourceId, workingMemory, metadata, fence }) {
|
|
3133
3266
|
try {
|
|
3134
3267
|
const existingResource = await this.getResourceById({ resourceId });
|
|
3135
3268
|
if (!existingResource) {
|
|
@@ -3140,7 +3273,12 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
3140
3273
|
createdAt: /* @__PURE__ */ new Date(),
|
|
3141
3274
|
updatedAt: /* @__PURE__ */ new Date()
|
|
3142
3275
|
};
|
|
3143
|
-
|
|
3276
|
+
await this.#fenced(fence, "updateResource", (q) => this.db.insert({
|
|
3277
|
+
tableName: _mastra_core_storage.TABLE_RESOURCES,
|
|
3278
|
+
record: newResource,
|
|
3279
|
+
transaction: q
|
|
3280
|
+
}));
|
|
3281
|
+
return newResource;
|
|
3144
3282
|
}
|
|
3145
3283
|
const updatedResource = {
|
|
3146
3284
|
...existingResource,
|
|
@@ -3155,22 +3293,25 @@ var MemoryMSSQL = class MemoryMSSQL extends _mastra_core_storage.MemoryStorage {
|
|
|
3155
3293
|
indexName: _mastra_core_storage.TABLE_RESOURCES,
|
|
3156
3294
|
schemaName: getSchemaName(this.schema)
|
|
3157
3295
|
});
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3296
|
+
await this.#fenced(fence, "updateResource", (q) => {
|
|
3297
|
+
const updates = [];
|
|
3298
|
+
const req = q.request();
|
|
3299
|
+
if (workingMemory !== void 0) {
|
|
3300
|
+
updates.push("workingMemory = @workingMemory");
|
|
3301
|
+
req.input("workingMemory", workingMemory);
|
|
3302
|
+
}
|
|
3303
|
+
if (metadata) {
|
|
3304
|
+
updates.push("metadata = @metadata");
|
|
3305
|
+
req.input("metadata", JSON.stringify(updatedResource.metadata));
|
|
3306
|
+
}
|
|
3307
|
+
updates.push("updatedAt = @updatedAt");
|
|
3308
|
+
req.input("updatedAt", updatedResource.updatedAt.toISOString());
|
|
3309
|
+
req.input("id", resourceId);
|
|
3310
|
+
return req.query(`UPDATE ${tableName} SET ${updates.join(", ")} WHERE id = @id`);
|
|
3311
|
+
});
|
|
3172
3312
|
return updatedResource;
|
|
3173
3313
|
} catch (error) {
|
|
3314
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
3174
3315
|
const mastraError = new _mastra_core_error.MastraError({
|
|
3175
3316
|
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "UPDATE_RESOURCE", "FAILED"),
|
|
3176
3317
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -4447,6 +4588,17 @@ var WorkflowDefinitionsMSSQL = class WorkflowDefinitionsMSSQL extends _mastra_co
|
|
|
4447
4588
|
};
|
|
4448
4589
|
//#endregion
|
|
4449
4590
|
//#region src/storage/domains/workflows/index.ts
|
|
4591
|
+
const RUN_OWNER_COLUMNS = `[generation], [ownerId], [leaseExpiresAt], ${DB_NOW_MS} AS [nowMs]`;
|
|
4592
|
+
function toRunOwnershipRecord(runId, row) {
|
|
4593
|
+
const leaseExpiresAt = row.leaseExpiresAt === null ? null : Number(row.leaseExpiresAt);
|
|
4594
|
+
return {
|
|
4595
|
+
runId,
|
|
4596
|
+
generation: Number(row.generation),
|
|
4597
|
+
ownerId: String(row.ownerId),
|
|
4598
|
+
leaseExpiresAt: leaseExpiresAt === null ? null : new Date(leaseExpiresAt),
|
|
4599
|
+
live: leaseExpiresAt !== null && leaseExpiresAt > Number(row.nowMs)
|
|
4600
|
+
};
|
|
4601
|
+
}
|
|
4450
4602
|
var WorkflowsMSSQL = class WorkflowsMSSQL extends _mastra_core_storage.WorkflowsStorage {
|
|
4451
4603
|
pool;
|
|
4452
4604
|
db;
|
|
@@ -4496,6 +4648,10 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends _mastra_core_storage.Workflows
|
|
|
4496
4648
|
tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
4497
4649
|
schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT]
|
|
4498
4650
|
});
|
|
4651
|
+
await this.db.createTable({
|
|
4652
|
+
tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
|
|
4653
|
+
schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS]
|
|
4654
|
+
});
|
|
4499
4655
|
await this.createDefaultIndexes();
|
|
4500
4656
|
await this.createCustomIndexes();
|
|
4501
4657
|
}
|
|
@@ -4512,6 +4668,98 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends _mastra_core_storage.Workflows
|
|
|
4512
4668
|
}
|
|
4513
4669
|
async dangerouslyClearAll() {
|
|
4514
4670
|
await this.db.clearTable({ tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT });
|
|
4671
|
+
await this.db.clearTable({ tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS });
|
|
4672
|
+
}
|
|
4673
|
+
get #runOwnersTable() {
|
|
4674
|
+
return getTableName({
|
|
4675
|
+
indexName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
|
|
4676
|
+
schemaName: getSchemaName(this.schema)
|
|
4677
|
+
});
|
|
4678
|
+
}
|
|
4679
|
+
async #readRunOwner(q, runId) {
|
|
4680
|
+
const row = (await q.request().input("runId", runId).query(`SELECT ${RUN_OWNER_COLUMNS} FROM ${this.#runOwnersTable} WHERE [runId] = @runId`)).recordset[0];
|
|
4681
|
+
return row ? toRunOwnershipRecord(runId, row) : null;
|
|
4682
|
+
}
|
|
4683
|
+
supportsRunFencing() {
|
|
4684
|
+
return true;
|
|
4685
|
+
}
|
|
4686
|
+
#ownershipError(operation, runId, error) {
|
|
4687
|
+
return new _mastra_core_error.MastraError({
|
|
4688
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", operation, "FAILED"),
|
|
4689
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
4690
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
4691
|
+
details: { runId }
|
|
4692
|
+
}, error);
|
|
4693
|
+
}
|
|
4694
|
+
async claimRunOwnership({ runId, ownerId, leaseMs, force, expectedGeneration }) {
|
|
4695
|
+
const table = this.#runOwnersTable;
|
|
4696
|
+
try {
|
|
4697
|
+
return await claimTransaction(this.pool, async (transaction) => {
|
|
4698
|
+
if ((await transaction.request().input("runId", runId).query(`SELECT [generation] FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE [runId] = @runId`)).recordset.length === 0) {
|
|
4699
|
+
if (expectedGeneration !== void 0 && expectedGeneration !== 0) return {
|
|
4700
|
+
acquired: false,
|
|
4701
|
+
record: null
|
|
4702
|
+
};
|
|
4703
|
+
await transaction.request().input("runId", runId).input("ownerId", ownerId).input("leaseMs", mssql.default.BigInt, leaseMs).query(`INSERT INTO ${table} ([runId], [generation], [ownerId], [leaseExpiresAt], [updatedAt])
|
|
4704
|
+
VALUES (@runId, ${DB_NOW_MS}, @ownerId, ${DB_NOW_MS} + @leaseMs, ${DB_NOW_MS})`);
|
|
4705
|
+
return {
|
|
4706
|
+
acquired: true,
|
|
4707
|
+
record: await this.#readRunOwner(transaction, runId)
|
|
4708
|
+
};
|
|
4709
|
+
}
|
|
4710
|
+
const updated = await transaction.request().input("runId", runId).input("ownerId", ownerId).input("leaseMs", mssql.default.BigInt, leaseMs).input("expectedGeneration", mssql.default.BigInt, expectedGeneration ?? null).input("force", mssql.default.Bit, force === true).query(`UPDATE ${table}
|
|
4711
|
+
SET [generation] = [generation] + 1, [ownerId] = @ownerId, [leaseExpiresAt] = ${DB_NOW_MS} + @leaseMs,
|
|
4712
|
+
[updatedAt] = ${DB_NOW_MS}
|
|
4713
|
+
WHERE [runId] = @runId
|
|
4714
|
+
AND (@expectedGeneration IS NULL OR [generation] = @expectedGeneration)
|
|
4715
|
+
AND (@force = 1 OR [leaseExpiresAt] IS NULL OR [leaseExpiresAt] <= ${DB_NOW_MS})`);
|
|
4716
|
+
const record = await this.#readRunOwner(transaction, runId);
|
|
4717
|
+
return updated.rowsAffected[0] === 1 && record ? {
|
|
4718
|
+
acquired: true,
|
|
4719
|
+
record
|
|
4720
|
+
} : {
|
|
4721
|
+
acquired: false,
|
|
4722
|
+
record
|
|
4723
|
+
};
|
|
4724
|
+
});
|
|
4725
|
+
} catch (error) {
|
|
4726
|
+
throw this.#ownershipError("CLAIM_RUN_OWNERSHIP", runId, error);
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4729
|
+
async renewRunOwnership({ leaseMs, ...fence }) {
|
|
4730
|
+
try {
|
|
4731
|
+
return await inTransaction(this.pool, async (transaction) => {
|
|
4732
|
+
const updated = await transaction.request().input("runId", fence.runId).input("generation", mssql.default.BigInt, fence.generation).input("ownerId", fence.ownerId).input("leaseMs", mssql.default.BigInt, leaseMs).query(`UPDATE ${this.#runOwnersTable}
|
|
4733
|
+
SET [leaseExpiresAt] = ${DB_NOW_MS} + @leaseMs, [updatedAt] = ${DB_NOW_MS}
|
|
4734
|
+
WHERE [runId] = @runId AND [generation] = @generation AND [ownerId] = @ownerId
|
|
4735
|
+
AND [leaseExpiresAt] IS NOT NULL`);
|
|
4736
|
+
const record = await this.#readRunOwner(transaction, fence.runId);
|
|
4737
|
+
return updated.rowsAffected[0] === 1 && record ? {
|
|
4738
|
+
renewed: true,
|
|
4739
|
+
record
|
|
4740
|
+
} : {
|
|
4741
|
+
renewed: false,
|
|
4742
|
+
record
|
|
4743
|
+
};
|
|
4744
|
+
});
|
|
4745
|
+
} catch (error) {
|
|
4746
|
+
throw this.#ownershipError("RENEW_RUN_OWNERSHIP", fence.runId, error);
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
async releaseRunOwnership(fence) {
|
|
4750
|
+
try {
|
|
4751
|
+
return (await this.pool.request().input("runId", fence.runId).input("generation", mssql.default.BigInt, fence.generation).input("ownerId", fence.ownerId).query(`UPDATE ${this.#runOwnersTable} SET [leaseExpiresAt] = NULL, [updatedAt] = ${DB_NOW_MS}
|
|
4752
|
+
WHERE [runId] = @runId AND [generation] = @generation AND [ownerId] = @ownerId`)).rowsAffected[0] === 1;
|
|
4753
|
+
} catch (error) {
|
|
4754
|
+
throw this.#ownershipError("RELEASE_RUN_OWNERSHIP", fence.runId, error);
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
async getRunOwnership({ runId }) {
|
|
4758
|
+
try {
|
|
4759
|
+
return await this.#readRunOwner(this.pool, runId);
|
|
4760
|
+
} catch (error) {
|
|
4761
|
+
throw this.#ownershipError("GET_RUN_OWNERSHIP", runId, error);
|
|
4762
|
+
}
|
|
4515
4763
|
}
|
|
4516
4764
|
parseWorkflowRun(row) {
|
|
4517
4765
|
let parsedSnapshot = row.snapshot;
|
|
@@ -4529,14 +4777,16 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends _mastra_core_storage.Workflows
|
|
|
4529
4777
|
resourceId: row.resourceId
|
|
4530
4778
|
};
|
|
4531
4779
|
}
|
|
4532
|
-
async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state }) {
|
|
4780
|
+
async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state, fence: explicitFence }) {
|
|
4533
4781
|
const table = getTableName({
|
|
4534
4782
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
4535
4783
|
schemaName: getSchemaName(this.schema)
|
|
4536
4784
|
});
|
|
4785
|
+
const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence, runId);
|
|
4537
4786
|
const transaction = this.pool.transaction();
|
|
4538
4787
|
try {
|
|
4539
4788
|
await transaction.begin();
|
|
4789
|
+
if (fence) await assertRunFence(transaction, this.#runOwnersTable, fence, "updateWorkflowResults");
|
|
4540
4790
|
const selectRequest = new mssql.default.Request(transaction);
|
|
4541
4791
|
selectRequest.input("workflow_name", workflowName);
|
|
4542
4792
|
selectRequest.input("run_id", runId);
|
|
@@ -4584,6 +4834,7 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends _mastra_core_storage.Workflows
|
|
|
4584
4834
|
try {
|
|
4585
4835
|
await transaction.rollback();
|
|
4586
4836
|
} catch {}
|
|
4837
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
4587
4838
|
throw new _mastra_core_error.MastraError({
|
|
4588
4839
|
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
|
|
4589
4840
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -4596,14 +4847,16 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends _mastra_core_storage.Workflows
|
|
|
4596
4847
|
}, error);
|
|
4597
4848
|
}
|
|
4598
4849
|
}
|
|
4599
|
-
async updateWorkflowState({ workflowName, runId, opts }) {
|
|
4850
|
+
async updateWorkflowState({ workflowName, runId, opts, fence: explicitFence }) {
|
|
4600
4851
|
const table = getTableName({
|
|
4601
4852
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
4602
4853
|
schemaName: getSchemaName(this.schema)
|
|
4603
4854
|
});
|
|
4855
|
+
const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence, runId);
|
|
4604
4856
|
const transaction = this.pool.transaction();
|
|
4605
4857
|
try {
|
|
4606
4858
|
await transaction.begin();
|
|
4859
|
+
if (fence) await assertRunFence(transaction, this.#runOwnersTable, fence, "updateWorkflowState");
|
|
4607
4860
|
const selectRequest = new mssql.default.Request(transaction);
|
|
4608
4861
|
selectRequest.input("workflow_name", workflowName);
|
|
4609
4862
|
selectRequest.input("run_id", runId);
|
|
@@ -4659,31 +4912,34 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends _mastra_core_storage.Workflows
|
|
|
4659
4912
|
}, error);
|
|
4660
4913
|
}
|
|
4661
4914
|
}
|
|
4662
|
-
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot }) {
|
|
4915
|
+
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, fence }) {
|
|
4663
4916
|
const table = getTableName({
|
|
4664
4917
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
4665
4918
|
schemaName: getSchemaName(this.schema)
|
|
4666
4919
|
});
|
|
4667
4920
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4668
4921
|
try {
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4922
|
+
await withRunFence(this.pool, this.#runOwnersTable, (0, _mastra_core_storage.resolveRunFence)(this, fence, runId), "persistWorkflowSnapshot", async (q) => {
|
|
4923
|
+
const request = q.request();
|
|
4924
|
+
request.input("workflow_name", workflowName);
|
|
4925
|
+
request.input("run_id", runId);
|
|
4926
|
+
request.input("resourceId", resourceId);
|
|
4927
|
+
request.input("snapshot", JSON.stringify(snapshot));
|
|
4928
|
+
request.input("createdAt", mssql.default.DateTime2, new Date(now));
|
|
4929
|
+
request.input("updatedAt", mssql.default.DateTime2, new Date(now));
|
|
4930
|
+
const mergeSql = `MERGE INTO ${table} AS target
|
|
4931
|
+
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
4932
|
+
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
4933
|
+
WHEN MATCHED THEN UPDATE SET
|
|
4934
|
+
resourceId = @resourceId,
|
|
4935
|
+
snapshot = @snapshot,
|
|
4936
|
+
[updatedAt] = @updatedAt
|
|
4937
|
+
WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, resourceId, snapshot, [createdAt], [updatedAt])
|
|
4938
|
+
VALUES (@workflow_name, @run_id, @resourceId, @snapshot, @createdAt, @updatedAt);`;
|
|
4939
|
+
await request.query(mergeSql);
|
|
4940
|
+
});
|
|
4686
4941
|
} catch (error) {
|
|
4942
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
4687
4943
|
throw new _mastra_core_error.MastraError({
|
|
4688
4944
|
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
4689
4945
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -4752,23 +5008,15 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends _mastra_core_storage.Workflows
|
|
|
4752
5008
|
}, error);
|
|
4753
5009
|
}
|
|
4754
5010
|
}
|
|
4755
|
-
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
5011
|
+
async deleteWorkflowRunById({ runId, workflowName, fence }) {
|
|
4756
5012
|
const table = getTableName({
|
|
4757
5013
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
4758
5014
|
schemaName: getSchemaName(this.schema)
|
|
4759
5015
|
});
|
|
4760
|
-
const transaction = this.pool.transaction();
|
|
4761
5016
|
try {
|
|
4762
|
-
await
|
|
4763
|
-
const deleteRequest = new mssql.default.Request(transaction);
|
|
4764
|
-
deleteRequest.input("workflow_name", workflowName);
|
|
4765
|
-
deleteRequest.input("run_id", runId);
|
|
4766
|
-
await deleteRequest.query(`DELETE FROM ${table} WHERE workflow_name = @workflow_name AND run_id = @run_id`);
|
|
4767
|
-
await transaction.commit();
|
|
5017
|
+
await withRunFence(this.pool, this.#runOwnersTable, (0, _mastra_core_storage.resolveRunFence)(this, fence, runId), "deleteWorkflowRunById", (q) => q.request().input("workflow_name", workflowName).input("run_id", runId).query(`DELETE FROM ${table} WHERE workflow_name = @workflow_name AND run_id = @run_id`));
|
|
4768
5018
|
} catch (error) {
|
|
4769
|
-
|
|
4770
|
-
await transaction.rollback();
|
|
4771
|
-
} catch {}
|
|
5019
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
4772
5020
|
throw new _mastra_core_error.MastraError({
|
|
4773
5021
|
id: (0, _mastra_core_storage.createStorageErrorId)("MSSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
4774
5022
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|