@mastra/mysql 0.13.1-alpha.0 → 0.13.1
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 +506 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +507 -109
- package/dist/index.js.map +1 -1
- package/dist/storage/db/run-fencing.d.ts +34 -0
- package/dist/storage/db/run-fencing.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +15 -5
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +16 -5
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -6109,9 +6109,81 @@ function isAppendOnlySince(storedObservations, snapshotObservations) {
|
|
|
6109
6109
|
return stored === snapshot || stored.startsWith(snapshot);
|
|
6110
6110
|
}
|
|
6111
6111
|
//#endregion
|
|
6112
|
+
//#region src/storage/db/run-fencing.ts
|
|
6113
|
+
/**
|
|
6114
|
+
* Epoch milliseconds on the database clock. `UTC_TIMESTAMP` is the time the
|
|
6115
|
+
* statement started and doesn't depend on the session time zone. Every lease
|
|
6116
|
+
* comparison runs in a statement that already holds the run's owner row, so
|
|
6117
|
+
* no lock wait sits between that start time and the comparison.
|
|
6118
|
+
*/
|
|
6119
|
+
const DB_NOW_MS = `(TIMESTAMPDIFF(MICROSECOND, '1970-01-01 00:00:00', UTC_TIMESTAMP(3)) DIV 1000)`;
|
|
6120
|
+
const ER_DUP_ENTRY = 1062;
|
|
6121
|
+
const ER_LOCK_DEADLOCK = 1213;
|
|
6122
|
+
const MAX_CLAIM_ATTEMPTS = 5;
|
|
6123
|
+
/**
|
|
6124
|
+
* The check half of a fenced write; call it first inside the write's
|
|
6125
|
+
* transaction. The shared lock holds the claim row until commit, so a claim
|
|
6126
|
+
* that updates the row waits for the write to finish, and every later write
|
|
6127
|
+
* sees the new claim.
|
|
6128
|
+
*/
|
|
6129
|
+
async function assertRunFence(q, claimsTable, fence, operation) {
|
|
6130
|
+
const [rows] = await q.execute(`SELECT generation, ${quoteIdentifier("ownerId", "column name")} AS ownerId FROM ${claimsTable}
|
|
6131
|
+
WHERE ${quoteIdentifier("runId", "column name")} = ? LOCK IN SHARE MODE`, [fence.runId]);
|
|
6132
|
+
const row = rows[0];
|
|
6133
|
+
if (!(0, _mastra_core_storage.matchesRunFence)(row ? {
|
|
6134
|
+
generation: Number(row.generation),
|
|
6135
|
+
ownerId: String(row.ownerId)
|
|
6136
|
+
} : null, fence)) throw new _mastra_core_storage.RunFenceConflictError(fence, operation);
|
|
6137
|
+
}
|
|
6138
|
+
/** Runs `fn` in a transaction on its own pooled connection. */
|
|
6139
|
+
async function inTransaction(pool, fn) {
|
|
6140
|
+
const connection = await pool.getConnection();
|
|
6141
|
+
try {
|
|
6142
|
+
await connection.beginTransaction();
|
|
6143
|
+
const result = await fn(connection);
|
|
6144
|
+
await connection.commit();
|
|
6145
|
+
return result;
|
|
6146
|
+
} catch (error) {
|
|
6147
|
+
await connection.rollback().catch(() => {});
|
|
6148
|
+
throw error;
|
|
6149
|
+
} finally {
|
|
6150
|
+
connection.release();
|
|
6151
|
+
}
|
|
6152
|
+
}
|
|
6153
|
+
/**
|
|
6154
|
+
* Runs `write` directly when it carries no fence, or in a transaction behind
|
|
6155
|
+
* the fence check when it does.
|
|
6156
|
+
*/
|
|
6157
|
+
function withRunFence(pool, claimsTable, fence, operation, write) {
|
|
6158
|
+
if (!fence) return write(pool);
|
|
6159
|
+
return inTransaction(pool, async (connection) => {
|
|
6160
|
+
await assertRunFence(connection, claimsTable, fence, operation);
|
|
6161
|
+
return write(connection);
|
|
6162
|
+
});
|
|
6163
|
+
}
|
|
6164
|
+
/**
|
|
6165
|
+
* Runs a transaction that creates or updates a run's claim row, retrying when
|
|
6166
|
+
* two first claims race to insert it. Under REPEATABLE READ both lock the gap
|
|
6167
|
+
* where the row would go and one is chosen as a deadlock victim; under READ
|
|
6168
|
+
* COMMITTED the later insert hits the duplicate key. On retry the row exists
|
|
6169
|
+
* and the claim serializes behind its row lock.
|
|
6170
|
+
*/
|
|
6171
|
+
async function claimTransaction(pool, fn) {
|
|
6172
|
+
for (let attempt = 1;; attempt++) try {
|
|
6173
|
+
return await inTransaction(pool, fn);
|
|
6174
|
+
} catch (error) {
|
|
6175
|
+
const errno = error.errno;
|
|
6176
|
+
if (attempt >= MAX_CLAIM_ATTEMPTS || errno !== ER_DUP_ENTRY && errno !== ER_LOCK_DEADLOCK) throw error;
|
|
6177
|
+
}
|
|
6178
|
+
}
|
|
6179
|
+
//#endregion
|
|
6112
6180
|
//#region src/storage/domains/memory/index.ts
|
|
6113
6181
|
const OM_TABLE = "mastra_observational_memory";
|
|
6114
6182
|
const OM_TABLE_QUOTED = quoteIdentifier(OM_TABLE, "table name");
|
|
6183
|
+
const RUN_FENCES_TABLE = formatTableName(_mastra_core_storage.TABLE_MEMORY_RUN_FENCES);
|
|
6184
|
+
const RUN_ID$1 = quoteIdentifier("runId", "column name");
|
|
6185
|
+
const OWNER_ID$1 = quoteIdentifier("ownerId", "column name");
|
|
6186
|
+
const RETIRED_AT = quoteIdentifier("retiredAt", "column name");
|
|
6115
6187
|
/** Canonical head order: newest generation, then earliest createdAt, then lowest id. */
|
|
6116
6188
|
const OM_HEAD_ORDER = `${quoteIdentifier("generationCount", "column name")} DESC, ${quoteIdentifier("createdAt", "column name")} ASC, ${quoteIdentifier("id", "column name")} ASC`;
|
|
6117
6189
|
/** Max `supersededBy` hops when redirecting a write aimed at a retired record. */
|
|
@@ -6218,6 +6290,69 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6218
6290
|
this.#skipDefaultIndexes = skipDefaultIndexes;
|
|
6219
6291
|
this.#indexes = indexes?.filter((idx) => MemoryMySQL.MANAGED_TABLES.includes(idx.table));
|
|
6220
6292
|
}
|
|
6293
|
+
supportsRunFencing() {
|
|
6294
|
+
return true;
|
|
6295
|
+
}
|
|
6296
|
+
async raiseRunFence(fence) {
|
|
6297
|
+
try {
|
|
6298
|
+
return await claimTransaction(this.pool, async (connection) => {
|
|
6299
|
+
const [rows] = await connection.execute(`SELECT generation, ${OWNER_ID$1} AS ownerId FROM ${RUN_FENCES_TABLE} WHERE ${RUN_ID$1} = ? FOR UPDATE`, [fence.runId]);
|
|
6300
|
+
const row = rows[0];
|
|
6301
|
+
if (!row) {
|
|
6302
|
+
await connection.execute(`INSERT INTO ${RUN_FENCES_TABLE} (${RUN_ID$1}, generation, ${OWNER_ID$1}) VALUES (?, ?, ?)`, [
|
|
6303
|
+
fence.runId,
|
|
6304
|
+
fence.generation,
|
|
6305
|
+
fence.ownerId
|
|
6306
|
+
]);
|
|
6307
|
+
return true;
|
|
6308
|
+
}
|
|
6309
|
+
const generation = Number(row.generation);
|
|
6310
|
+
const raises = generation < fence.generation;
|
|
6311
|
+
const reaffirms = generation === fence.generation && String(row.ownerId) === fence.ownerId;
|
|
6312
|
+
if (!raises && !reaffirms) return false;
|
|
6313
|
+
await connection.execute(`UPDATE ${RUN_FENCES_TABLE} SET generation = ?, ${OWNER_ID$1} = ?, ${RETIRED_AT} = NULL WHERE ${RUN_ID$1} = ?`, [
|
|
6314
|
+
fence.generation,
|
|
6315
|
+
fence.ownerId,
|
|
6316
|
+
fence.runId
|
|
6317
|
+
]);
|
|
6318
|
+
return true;
|
|
6319
|
+
});
|
|
6320
|
+
} catch (error) {
|
|
6321
|
+
throw new _mastra_core_error.MastraError({
|
|
6322
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("MYSQL", "RAISE_RUN_FENCE", "FAILED"),
|
|
6323
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
6324
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
6325
|
+
details: { runId: fence.runId }
|
|
6326
|
+
}, error);
|
|
6327
|
+
}
|
|
6328
|
+
}
|
|
6329
|
+
async retireRunFence(fence) {
|
|
6330
|
+
try {
|
|
6331
|
+
return await inTransaction(this.pool, async (connection) => {
|
|
6332
|
+
const [rows] = await connection.execute(`SELECT generation, ${OWNER_ID$1} AS ownerId FROM ${RUN_FENCES_TABLE} WHERE ${RUN_ID$1} = ? FOR UPDATE`, [fence.runId]);
|
|
6333
|
+
const row = rows[0];
|
|
6334
|
+
if (!row || Number(row.generation) !== fence.generation || String(row.ownerId) !== fence.ownerId) return false;
|
|
6335
|
+
await connection.execute(`UPDATE ${RUN_FENCES_TABLE} SET ${RETIRED_AT} = ${DB_NOW_MS} WHERE ${RUN_ID$1} = ?`, [fence.runId]);
|
|
6336
|
+
return true;
|
|
6337
|
+
});
|
|
6338
|
+
} catch (error) {
|
|
6339
|
+
throw new _mastra_core_error.MastraError({
|
|
6340
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("MYSQL", "RETIRE_RUN_FENCE", "FAILED"),
|
|
6341
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
6342
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
6343
|
+
details: { runId: fence.runId }
|
|
6344
|
+
}, error);
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6347
|
+
/** Runs `write` behind its own fence, otherwise the one in scope for this store. */
|
|
6348
|
+
#fenced(fence, operation, write) {
|
|
6349
|
+
return withRunFence(this.pool, RUN_FENCES_TABLE, (0, _mastra_core_storage.resolveRunFence)(this, fence), operation, write);
|
|
6350
|
+
}
|
|
6351
|
+
/** The fence check for a write that already runs in its own transaction. */
|
|
6352
|
+
async #assertFence(connection, fence, operation) {
|
|
6353
|
+
const resolved = (0, _mastra_core_storage.resolveRunFence)(this, fence);
|
|
6354
|
+
if (resolved) await assertRunFence(connection, RUN_FENCES_TABLE, resolved, operation);
|
|
6355
|
+
}
|
|
6221
6356
|
async init() {
|
|
6222
6357
|
await this.operations.createTable({
|
|
6223
6358
|
tableName: _mastra_core_storage.TABLE_THREADS,
|
|
@@ -6231,6 +6366,10 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6231
6366
|
tableName: _mastra_core_storage.TABLE_RESOURCES,
|
|
6232
6367
|
schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_RESOURCES]
|
|
6233
6368
|
});
|
|
6369
|
+
await this.operations.createTable({
|
|
6370
|
+
tableName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
|
|
6371
|
+
schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_MEMORY_RUN_FENCES]
|
|
6372
|
+
});
|
|
6234
6373
|
const omSchema = withObservationalMemoryLifecycleColumns(_mastra_core_storage.OBSERVATIONAL_MEMORY_TABLE_SCHEMA?.[OM_TABLE]);
|
|
6235
6374
|
if (omSchema) {
|
|
6236
6375
|
await this.operations.createTable({
|
|
@@ -6298,6 +6437,10 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6298
6437
|
tableName,
|
|
6299
6438
|
schema: _mastra_core_storage.TABLE_SCHEMAS[tableName]
|
|
6300
6439
|
}));
|
|
6440
|
+
statements.push(generateTableSQL({
|
|
6441
|
+
tableName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
|
|
6442
|
+
schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_MEMORY_RUN_FENCES]
|
|
6443
|
+
}));
|
|
6301
6444
|
for (const idx of MemoryMySQL.getDefaultIndexDefs()) statements.push(generateIndexSQL(idx));
|
|
6302
6445
|
return statements;
|
|
6303
6446
|
}
|
|
@@ -6316,6 +6459,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6316
6459
|
await this.pool.execute(`DELETE FROM ${formatTableName(_mastra_core_storage.TABLE_MESSAGES)}`);
|
|
6317
6460
|
await this.pool.execute(`DELETE FROM ${formatTableName(_mastra_core_storage.TABLE_THREADS)}`);
|
|
6318
6461
|
await this.pool.execute(`DELETE FROM ${formatTableName(_mastra_core_storage.TABLE_RESOURCES)}`);
|
|
6462
|
+
await this.pool.execute(`DELETE FROM ${RUN_FENCES_TABLE}`);
|
|
6319
6463
|
try {
|
|
6320
6464
|
await this.pool.execute(`DELETE FROM ${OM_TABLE_QUOTED}`);
|
|
6321
6465
|
} catch (err) {
|
|
@@ -6496,8 +6640,8 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6496
6640
|
}
|
|
6497
6641
|
return includeMessages;
|
|
6498
6642
|
}
|
|
6499
|
-
async upsertThread(thread) {
|
|
6500
|
-
|
|
6643
|
+
async upsertThread(q, thread) {
|
|
6644
|
+
const statement = prepareStatement({
|
|
6501
6645
|
tableName: _mastra_core_storage.TABLE_THREADS,
|
|
6502
6646
|
record: {
|
|
6503
6647
|
id: thread.id,
|
|
@@ -6508,18 +6652,22 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6508
6652
|
updatedAt: thread.updatedAt
|
|
6509
6653
|
}
|
|
6510
6654
|
});
|
|
6655
|
+
await q.execute(statement.sql, statement.args);
|
|
6656
|
+
}
|
|
6657
|
+
async #readThread(q, threadId, resourceId) {
|
|
6658
|
+
let sql = `SELECT * FROM ${formatTableName(_mastra_core_storage.TABLE_THREADS)} WHERE ${quoteIdentifier("id", "column name")} = ?`;
|
|
6659
|
+
const params = [threadId];
|
|
6660
|
+
if (resourceId !== void 0) {
|
|
6661
|
+
sql += ` AND ${quoteIdentifier("resourceId", "column name")} = ?`;
|
|
6662
|
+
params.push(resourceId);
|
|
6663
|
+
}
|
|
6664
|
+
const [rows] = await q.execute(sql, params);
|
|
6665
|
+
const row = rows[0];
|
|
6666
|
+
return row ? this.mapThread(row) : null;
|
|
6511
6667
|
}
|
|
6512
6668
|
async getThreadById({ threadId, resourceId }) {
|
|
6513
6669
|
try {
|
|
6514
|
-
|
|
6515
|
-
const params = [threadId];
|
|
6516
|
-
if (resourceId !== void 0) {
|
|
6517
|
-
sql += ` AND ${quoteIdentifier("resourceId", "column name")} = ?`;
|
|
6518
|
-
params.push(resourceId);
|
|
6519
|
-
}
|
|
6520
|
-
const [rows] = await this.pool.execute(sql, params);
|
|
6521
|
-
const row = rows[0];
|
|
6522
|
-
return row ? this.mapThread(row) : null;
|
|
6670
|
+
return await this.#readThread(this.pool, threadId, resourceId);
|
|
6523
6671
|
} catch (error) {
|
|
6524
6672
|
throw new _mastra_core_error.MastraError({
|
|
6525
6673
|
id: "MYSQL_MEMORY_GET_THREAD_BY_ID_FAILED",
|
|
@@ -6672,11 +6820,12 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6672
6820
|
} : void 0
|
|
6673
6821
|
});
|
|
6674
6822
|
}
|
|
6675
|
-
async saveThread({ thread }) {
|
|
6823
|
+
async saveThread({ thread, fence }) {
|
|
6676
6824
|
try {
|
|
6677
|
-
await this.upsertThread(thread);
|
|
6825
|
+
await this.#fenced(fence, "saveThread", (q) => this.upsertThread(q, thread));
|
|
6678
6826
|
return thread;
|
|
6679
6827
|
} catch (error) {
|
|
6828
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
6680
6829
|
throw new _mastra_core_error.MastraError({
|
|
6681
6830
|
id: "MYSQL_MEMORY_SAVE_THREAD_FAILED",
|
|
6682
6831
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -6685,36 +6834,39 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6685
6834
|
}, error);
|
|
6686
6835
|
}
|
|
6687
6836
|
}
|
|
6688
|
-
async updateThread({ id, title, metadata }) {
|
|
6837
|
+
async updateThread({ id, title, metadata, fence }) {
|
|
6689
6838
|
try {
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6839
|
+
return await this.#fenced(fence, "updateThread", async (q) => {
|
|
6840
|
+
const existing = await this.#readThread(q, id);
|
|
6841
|
+
if (!existing) throw new _mastra_core_error.MastraError({
|
|
6842
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("MYSQL", "UPDATE_THREAD", "NOT_FOUND"),
|
|
6843
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
6844
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
6845
|
+
text: `Thread ${id} not found`,
|
|
6846
|
+
details: { threadId: id }
|
|
6847
|
+
});
|
|
6848
|
+
const mergedMetadata = {
|
|
6849
|
+
...existing.metadata ?? {},
|
|
6850
|
+
...metadata ?? {}
|
|
6851
|
+
};
|
|
6852
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
6853
|
+
const statement = prepareUpdateStatement({
|
|
6854
|
+
tableName: _mastra_core_storage.TABLE_THREADS,
|
|
6855
|
+
keys: { id },
|
|
6856
|
+
updates: {
|
|
6857
|
+
title: title ?? existing.title,
|
|
6858
|
+
metadata: JSON.stringify(mergedMetadata),
|
|
6859
|
+
updatedAt
|
|
6860
|
+
}
|
|
6861
|
+
});
|
|
6862
|
+
await q.execute(statement.sql, statement.args);
|
|
6863
|
+
return {
|
|
6864
|
+
...existing,
|
|
6707
6865
|
title: title ?? existing.title,
|
|
6708
|
-
metadata:
|
|
6866
|
+
metadata: mergedMetadata,
|
|
6709
6867
|
updatedAt
|
|
6710
|
-
}
|
|
6868
|
+
};
|
|
6711
6869
|
});
|
|
6712
|
-
return {
|
|
6713
|
-
...existing,
|
|
6714
|
-
title: title ?? existing.title,
|
|
6715
|
-
metadata: mergedMetadata,
|
|
6716
|
-
updatedAt
|
|
6717
|
-
};
|
|
6718
6870
|
} catch (error) {
|
|
6719
6871
|
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
6720
6872
|
throw new _mastra_core_error.MastraError({
|
|
@@ -6795,6 +6947,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6795
6947
|
const insertedIds = [];
|
|
6796
6948
|
try {
|
|
6797
6949
|
await connection.beginTransaction();
|
|
6950
|
+
await this.#assertFence(connection, args.fence, "saveMessages");
|
|
6798
6951
|
for (const message of messages) {
|
|
6799
6952
|
if (!message.threadId) throw new _mastra_core_error.MastraError({
|
|
6800
6953
|
id: (0, _mastra_core_storage.createStorageErrorId)("MYSQL", "SAVE_MESSAGES", "INVALID_INPUT"),
|
|
@@ -6888,18 +7041,29 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6888
7041
|
if (update.threadId && update.threadId !== current.threadId && current.threadId) affectedThreads.set(current.threadId, now);
|
|
6889
7042
|
}
|
|
6890
7043
|
if (!updates.length) return existing;
|
|
6891
|
-
await this.
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
7044
|
+
await inTransaction(this.pool, async (connection) => {
|
|
7045
|
+
await this.#assertFence(connection, args.fence, "updateMessages");
|
|
7046
|
+
for (const { keys, data } of updates) {
|
|
7047
|
+
const statement = prepareUpdateStatement({
|
|
7048
|
+
tableName: _mastra_core_storage.TABLE_MESSAGES,
|
|
7049
|
+
updates: data,
|
|
7050
|
+
keys
|
|
7051
|
+
});
|
|
7052
|
+
await connection.execute(statement.sql, statement.args);
|
|
7053
|
+
}
|
|
7054
|
+
for (const [threadId, timestamp] of affectedThreads.entries()) {
|
|
7055
|
+
const statement = prepareUpdateStatement({
|
|
7056
|
+
tableName: _mastra_core_storage.TABLE_THREADS,
|
|
7057
|
+
updates: { updatedAt: timestamp },
|
|
7058
|
+
keys: { id: threadId }
|
|
7059
|
+
});
|
|
7060
|
+
await connection.execute(statement.sql, statement.args);
|
|
7061
|
+
}
|
|
6899
7062
|
});
|
|
6900
7063
|
const { messages: updated } = await this.listMessagesById({ messageIds: messages.map((m) => m.id) });
|
|
6901
7064
|
return updated;
|
|
6902
7065
|
} catch (error) {
|
|
7066
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
6903
7067
|
throw new _mastra_core_error.MastraError({
|
|
6904
7068
|
id: "MYSQL_MEMORY_UPDATE_MESSAGES_FAILED",
|
|
6905
7069
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -6907,19 +7071,22 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
6907
7071
|
}, error);
|
|
6908
7072
|
}
|
|
6909
7073
|
}
|
|
6910
|
-
async deleteMessages(messageIds) {
|
|
7074
|
+
async deleteMessages(messageIds, options) {
|
|
6911
7075
|
if (!messageIds.length) return;
|
|
6912
7076
|
try {
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
7077
|
+
await this.#fenced(options?.fence, "deleteMessages", async (q) => {
|
|
7078
|
+
const placeholders = messageIds.map(() => "?").join(", ");
|
|
7079
|
+
const [rows] = await q.execute(`SELECT DISTINCT thread_id FROM ${formatTableName(_mastra_core_storage.TABLE_MESSAGES)} WHERE id IN (${placeholders})`, messageIds);
|
|
7080
|
+
const threadIds = rows.map((row) => row.thread_id).filter((threadId) => Boolean(threadId));
|
|
7081
|
+
await q.execute(`DELETE FROM ${formatTableName(_mastra_core_storage.TABLE_MESSAGES)} WHERE id IN (${placeholders})`, messageIds);
|
|
7082
|
+
if (threadIds.length) {
|
|
7083
|
+
const threadPlaceholders = threadIds.map(() => "?").join(", ");
|
|
7084
|
+
const timestamp = transformToSqlValue(/* @__PURE__ */ new Date());
|
|
7085
|
+
await q.execute(`UPDATE ${formatTableName(_mastra_core_storage.TABLE_THREADS)} SET ${quoteIdentifier("updatedAt", "column name")} = ? WHERE id IN (${threadPlaceholders})`, [timestamp, ...threadIds]);
|
|
7086
|
+
}
|
|
7087
|
+
});
|
|
6922
7088
|
} catch (error) {
|
|
7089
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
6923
7090
|
throw new _mastra_core_error.MastraError({
|
|
6924
7091
|
id: "MYSQL_MEMORY_DELETE_MESSAGES_FAILED",
|
|
6925
7092
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -7318,19 +7485,23 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7318
7485
|
}, error);
|
|
7319
7486
|
}
|
|
7320
7487
|
}
|
|
7488
|
+
async #insertResource(q, resource) {
|
|
7489
|
+
const metadataValue = resource.metadata === void 0 || resource.metadata === null ? null : JSON.stringify(resource.metadata);
|
|
7490
|
+
const statement = prepareStatement({
|
|
7491
|
+
tableName: _mastra_core_storage.TABLE_RESOURCES,
|
|
7492
|
+
record: {
|
|
7493
|
+
id: resource.id,
|
|
7494
|
+
workingMemory: resource.workingMemory ?? null,
|
|
7495
|
+
metadata: metadataValue,
|
|
7496
|
+
createdAt: resource.createdAt ?? /* @__PURE__ */ new Date(),
|
|
7497
|
+
updatedAt: resource.updatedAt ?? /* @__PURE__ */ new Date()
|
|
7498
|
+
}
|
|
7499
|
+
});
|
|
7500
|
+
await q.execute(statement.sql, statement.args);
|
|
7501
|
+
}
|
|
7321
7502
|
async saveResource({ resource }) {
|
|
7322
7503
|
try {
|
|
7323
|
-
|
|
7324
|
-
await this.operations.insert({
|
|
7325
|
-
tableName: _mastra_core_storage.TABLE_RESOURCES,
|
|
7326
|
-
record: {
|
|
7327
|
-
id: resource.id,
|
|
7328
|
-
workingMemory: resource.workingMemory ?? null,
|
|
7329
|
-
metadata: metadataValue,
|
|
7330
|
-
createdAt: resource.createdAt ?? /* @__PURE__ */ new Date(),
|
|
7331
|
-
updatedAt: resource.updatedAt ?? /* @__PURE__ */ new Date()
|
|
7332
|
-
}
|
|
7333
|
-
});
|
|
7504
|
+
await this.#insertResource(this.pool, resource);
|
|
7334
7505
|
return resource;
|
|
7335
7506
|
} catch (error) {
|
|
7336
7507
|
throw new _mastra_core_error.MastraError({
|
|
@@ -7341,7 +7512,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7341
7512
|
}, error);
|
|
7342
7513
|
}
|
|
7343
7514
|
}
|
|
7344
|
-
async updateResource({ resourceId, workingMemory, metadata }) {
|
|
7515
|
+
async updateResource({ resourceId, workingMemory, metadata, fence }) {
|
|
7345
7516
|
try {
|
|
7346
7517
|
const existing = await this.getResourceById({ resourceId });
|
|
7347
7518
|
if (!existing) {
|
|
@@ -7352,22 +7523,24 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7352
7523
|
createdAt: /* @__PURE__ */ new Date(),
|
|
7353
7524
|
updatedAt: /* @__PURE__ */ new Date()
|
|
7354
7525
|
};
|
|
7355
|
-
|
|
7526
|
+
await this.#fenced(fence, "updateResource", (q) => this.#insertResource(q, newResource));
|
|
7527
|
+
return newResource;
|
|
7356
7528
|
}
|
|
7357
7529
|
const mergedMetadata = metadata !== void 0 ? {
|
|
7358
7530
|
...existing.metadata ?? {},
|
|
7359
7531
|
...metadata ?? {}
|
|
7360
7532
|
} : existing.metadata;
|
|
7361
7533
|
const metadataValue = metadata !== void 0 ? metadata === null ? null : JSON.stringify(mergedMetadata ?? {}) : void 0;
|
|
7362
|
-
|
|
7534
|
+
const statement = prepareUpdateStatement({
|
|
7363
7535
|
tableName: _mastra_core_storage.TABLE_RESOURCES,
|
|
7364
7536
|
keys: { id: resourceId },
|
|
7365
|
-
|
|
7537
|
+
updates: {
|
|
7366
7538
|
...workingMemory !== void 0 ? { workingMemory } : {},
|
|
7367
7539
|
...metadataValue !== void 0 ? { metadata: metadataValue } : {},
|
|
7368
7540
|
updatedAt: /* @__PURE__ */ new Date()
|
|
7369
7541
|
}
|
|
7370
7542
|
});
|
|
7543
|
+
await this.#fenced(fence, "updateResource", (q) => q.execute(statement.sql, statement.args));
|
|
7371
7544
|
const updated = await this.getResourceById({ resourceId });
|
|
7372
7545
|
if (!updated) throw new _mastra_core_error.MastraError({
|
|
7373
7546
|
id: (0, _mastra_core_storage.createStorageErrorId)("MYSQL", "UPDATE_RESOURCE", "NOT_FOUND_AFTER_UPDATE"),
|
|
@@ -7572,7 +7745,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7572
7745
|
input.id
|
|
7573
7746
|
]);
|
|
7574
7747
|
return { applied: true };
|
|
7575
|
-
});
|
|
7748
|
+
}, "commitActiveObservations");
|
|
7576
7749
|
} catch (error) {
|
|
7577
7750
|
rethrowOrWrapOM(error, input.id, "UPDATE_ACTIVE_OBSERVATIONS");
|
|
7578
7751
|
}
|
|
@@ -7595,11 +7768,95 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7595
7768
|
});
|
|
7596
7769
|
if (!plan) return stored;
|
|
7597
7770
|
return this.rollOverOMRow(connection, stored.id, plan.observations, plan.tokenCount, input.newRecordId, false);
|
|
7598
|
-
});
|
|
7771
|
+
}, "createReflectionGeneration");
|
|
7599
7772
|
} catch (error) {
|
|
7600
7773
|
rethrowOrWrapOM(error, input.currentRecord.id, "CREATE_REFLECTION_GENERATION", { currentRecordId: input.currentRecord.id });
|
|
7601
7774
|
}
|
|
7602
7775
|
}
|
|
7776
|
+
async #insertReflectionGeneration(q, input) {
|
|
7777
|
+
const id = globalThis.crypto.randomUUID();
|
|
7778
|
+
const now = /* @__PURE__ */ new Date();
|
|
7779
|
+
const lookupKey = this.getOMKey(input.currentRecord.threadId, input.currentRecord.resourceId);
|
|
7780
|
+
const record = {
|
|
7781
|
+
id,
|
|
7782
|
+
scope: input.currentRecord.scope,
|
|
7783
|
+
threadId: input.currentRecord.threadId,
|
|
7784
|
+
resourceId: input.currentRecord.resourceId,
|
|
7785
|
+
createdAt: now,
|
|
7786
|
+
updatedAt: now,
|
|
7787
|
+
lastObservedAt: input.currentRecord.lastObservedAt,
|
|
7788
|
+
originType: "reflection",
|
|
7789
|
+
generationCount: input.currentRecord.generationCount + 1,
|
|
7790
|
+
activeObservations: input.reflection,
|
|
7791
|
+
totalTokensObserved: input.currentRecord.totalTokensObserved,
|
|
7792
|
+
observationTokenCount: input.tokenCount,
|
|
7793
|
+
pendingMessageTokens: 0,
|
|
7794
|
+
isReflecting: false,
|
|
7795
|
+
isObserving: false,
|
|
7796
|
+
isBufferingObservation: false,
|
|
7797
|
+
isBufferingReflection: false,
|
|
7798
|
+
lastBufferedAtTokens: 0,
|
|
7799
|
+
lastBufferedAtTime: null,
|
|
7800
|
+
config: input.currentRecord.config,
|
|
7801
|
+
metadata: input.currentRecord.metadata,
|
|
7802
|
+
observedTimezone: input.currentRecord.observedTimezone
|
|
7803
|
+
};
|
|
7804
|
+
const nowSql = transformToSqlValue(now);
|
|
7805
|
+
const cols = [
|
|
7806
|
+
"id",
|
|
7807
|
+
"lookupKey",
|
|
7808
|
+
"scope",
|
|
7809
|
+
"resourceId",
|
|
7810
|
+
"threadId",
|
|
7811
|
+
"activeObservations",
|
|
7812
|
+
"activeObservationsPendingUpdate",
|
|
7813
|
+
"originType",
|
|
7814
|
+
"config",
|
|
7815
|
+
"generationCount",
|
|
7816
|
+
"lastObservedAt",
|
|
7817
|
+
"lastReflectionAt",
|
|
7818
|
+
"pendingMessageTokens",
|
|
7819
|
+
"totalTokensObserved",
|
|
7820
|
+
"observationTokenCount",
|
|
7821
|
+
"isObserving",
|
|
7822
|
+
"isReflecting",
|
|
7823
|
+
"isBufferingObservation",
|
|
7824
|
+
"isBufferingReflection",
|
|
7825
|
+
"lastBufferedAtTokens",
|
|
7826
|
+
"lastBufferedAtTime",
|
|
7827
|
+
"observedTimezone",
|
|
7828
|
+
"createdAt",
|
|
7829
|
+
"updatedAt"
|
|
7830
|
+
].map(omCol).join(", ");
|
|
7831
|
+
const placeholders = Array.from({ length: 24 }, () => "?").join(", ");
|
|
7832
|
+
await q.execute(`INSERT INTO ${OM_TABLE_QUOTED} (${cols}) VALUES (${placeholders})`, [
|
|
7833
|
+
id,
|
|
7834
|
+
lookupKey,
|
|
7835
|
+
record.scope,
|
|
7836
|
+
record.resourceId,
|
|
7837
|
+
record.threadId || null,
|
|
7838
|
+
input.reflection,
|
|
7839
|
+
null,
|
|
7840
|
+
"reflection",
|
|
7841
|
+
JSON.stringify(record.config),
|
|
7842
|
+
input.currentRecord.generationCount + 1,
|
|
7843
|
+
record.lastObservedAt ? transformToSqlValue(record.lastObservedAt) : null,
|
|
7844
|
+
nowSql,
|
|
7845
|
+
record.pendingMessageTokens,
|
|
7846
|
+
record.totalTokensObserved,
|
|
7847
|
+
record.observationTokenCount,
|
|
7848
|
+
false,
|
|
7849
|
+
false,
|
|
7850
|
+
false,
|
|
7851
|
+
false,
|
|
7852
|
+
0,
|
|
7853
|
+
null,
|
|
7854
|
+
record.observedTimezone || null,
|
|
7855
|
+
nowSql,
|
|
7856
|
+
nowSql
|
|
7857
|
+
]);
|
|
7858
|
+
return record;
|
|
7859
|
+
}
|
|
7603
7860
|
async setReflectingFlag(id, isReflecting) {
|
|
7604
7861
|
await this.updateOMFlag(id, "isReflecting", isReflecting, "SET_REFLECTING_FLAG");
|
|
7605
7862
|
}
|
|
@@ -7705,7 +7962,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7705
7962
|
persisted: true,
|
|
7706
7963
|
recordId: target.id
|
|
7707
7964
|
};
|
|
7708
|
-
});
|
|
7965
|
+
}, "appendBufferedObservations");
|
|
7709
7966
|
} catch (error) {
|
|
7710
7967
|
rethrowOrWrapOM(error, input.id, "UPDATE_BUFFERED_OBSERVATIONS");
|
|
7711
7968
|
}
|
|
@@ -7817,7 +8074,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7817
8074
|
suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0,
|
|
7818
8075
|
currentTask: latestChunkHints?.currentTask ?? void 0
|
|
7819
8076
|
};
|
|
7820
|
-
});
|
|
8077
|
+
}, "swapBufferedToActive");
|
|
7821
8078
|
} catch (error) {
|
|
7822
8079
|
rethrowOrWrapOM(error, input.id, "SWAP_BUFFERED_TO_ACTIVE");
|
|
7823
8080
|
}
|
|
@@ -7826,7 +8083,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7826
8083
|
try {
|
|
7827
8084
|
const nowSql = transformToSqlValue(/* @__PURE__ */ new Date());
|
|
7828
8085
|
const br = omCol("bufferedReflection");
|
|
7829
|
-
const [result] = await this.
|
|
8086
|
+
const [result] = await this.#fenced(void 0, "updateBufferedReflection", (q) => q.execute(`UPDATE ${OM_TABLE_QUOTED} SET
|
|
7830
8087
|
${br} = CASE
|
|
7831
8088
|
WHEN ${br} IS NOT NULL AND ${br} != ''
|
|
7832
8089
|
THEN CONCAT(${br}, CHAR(10), CHAR(10), ?)
|
|
@@ -7844,7 +8101,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7844
8101
|
input.reflectedObservationLineCount,
|
|
7845
8102
|
nowSql,
|
|
7846
8103
|
input.id
|
|
7847
|
-
]);
|
|
8104
|
+
]));
|
|
7848
8105
|
if (result.affectedRows === 0) throwOMNotFound(input.id, "UPDATE_BUFFERED_REFLECTION");
|
|
7849
8106
|
} catch (error) {
|
|
7850
8107
|
rethrowOrWrapOM(error, input.id, "UPDATE_BUFFERED_REFLECTION");
|
|
@@ -7872,7 +8129,7 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7872
8129
|
const newObservations = unreflectedContent ? `${bufferedReflection}\n\n${unreflectedContent}` : bufferedReflection;
|
|
7873
8130
|
const tokenCount = input.tokenCount + Math.max(0, (stored.observationTokenCount ?? 0) - (currentRecord.observationTokenCount ?? 0));
|
|
7874
8131
|
return this.rollOverOMRow(connection, stored.id, newObservations, tokenCount, input.newRecordId, true);
|
|
7875
|
-
});
|
|
8132
|
+
}, "swapBufferedReflectionToActive");
|
|
7876
8133
|
} catch (error) {
|
|
7877
8134
|
rethrowOrWrapOM(error, input.currentRecord.id, "SWAP_BUFFERED_REFLECTION_TO_ACTIVE");
|
|
7878
8135
|
}
|
|
@@ -7883,11 +8140,12 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
|
|
|
7883
8140
|
* only expected lock conflict is a wait; a deadlock or lock-wait timeout (e.g. against the
|
|
7884
8141
|
* startup backfill's scan) is retried a bounded number of times.
|
|
7885
8142
|
*/
|
|
7886
|
-
async withOMTransaction(fn) {
|
|
8143
|
+
async withOMTransaction(fn, fencedOperation) {
|
|
7887
8144
|
for (let attempt = 1;; attempt++) {
|
|
7888
8145
|
const connection = await this.pool.getConnection();
|
|
7889
8146
|
try {
|
|
7890
8147
|
await connection.beginTransaction();
|
|
8148
|
+
if (fencedOperation) await this.#assertFence(connection, void 0, fencedOperation);
|
|
7891
8149
|
const result = await fn(connection);
|
|
7892
8150
|
await connection.commit();
|
|
7893
8151
|
return result;
|
|
@@ -11064,6 +11322,26 @@ function parseSnapshot(snapshot) {
|
|
|
11064
11322
|
}
|
|
11065
11323
|
return snapshot;
|
|
11066
11324
|
}
|
|
11325
|
+
const RUN_OWNERS_TABLE = formatTableName(_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS);
|
|
11326
|
+
const RUN_ID = quoteIdentifier("runId", "column name");
|
|
11327
|
+
const OWNER_ID = quoteIdentifier("ownerId", "column name");
|
|
11328
|
+
const LEASE_EXPIRES_AT = quoteIdentifier("leaseExpiresAt", "column name");
|
|
11329
|
+
const UPDATED_AT = quoteIdentifier("updatedAt", "column name");
|
|
11330
|
+
const RUN_OWNER_COLUMNS = `generation, ${OWNER_ID} AS ownerId, ${LEASE_EXPIRES_AT} AS leaseExpiresAt, ${DB_NOW_MS} AS nowMs`;
|
|
11331
|
+
function toRunOwnershipRecord(runId, row) {
|
|
11332
|
+
const leaseExpiresAt = row.leaseExpiresAt === null ? null : Number(row.leaseExpiresAt);
|
|
11333
|
+
return {
|
|
11334
|
+
runId,
|
|
11335
|
+
generation: Number(row.generation),
|
|
11336
|
+
ownerId: String(row.ownerId),
|
|
11337
|
+
leaseExpiresAt: leaseExpiresAt === null ? null : new Date(leaseExpiresAt),
|
|
11338
|
+
live: leaseExpiresAt !== null && leaseExpiresAt > Number(row.nowMs)
|
|
11339
|
+
};
|
|
11340
|
+
}
|
|
11341
|
+
async function readRunOwner(q, runId) {
|
|
11342
|
+
const [rows] = await q.execute(`SELECT ${RUN_OWNER_COLUMNS} FROM ${RUN_OWNERS_TABLE} WHERE ${RUN_ID} = ?`, [runId]);
|
|
11343
|
+
return rows[0] ? toRunOwnershipRecord(runId, rows[0]) : null;
|
|
11344
|
+
}
|
|
11067
11345
|
function mapWorkflowRow(row) {
|
|
11068
11346
|
return {
|
|
11069
11347
|
workflowName: row.workflow_name,
|
|
@@ -11096,6 +11374,9 @@ var WorkflowsMySQL = class WorkflowsMySQL extends _mastra_core_storage.Workflows
|
|
|
11096
11374
|
tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
11097
11375
|
schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT],
|
|
11098
11376
|
compositePrimaryKey: ["workflow_name", "run_id"]
|
|
11377
|
+
}), generateTableSQL({
|
|
11378
|
+
tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
|
|
11379
|
+
schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS]
|
|
11099
11380
|
})];
|
|
11100
11381
|
}
|
|
11101
11382
|
constructor({ operations, pool, skipDefaultIndexes, indexes }) {
|
|
@@ -11139,16 +11420,125 @@ var WorkflowsMySQL = class WorkflowsMySQL extends _mastra_core_storage.Workflows
|
|
|
11139
11420
|
schema,
|
|
11140
11421
|
ifNotExists: ["resourceId"]
|
|
11141
11422
|
});
|
|
11423
|
+
await this.operations.createTable({
|
|
11424
|
+
tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
|
|
11425
|
+
schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS]
|
|
11426
|
+
});
|
|
11142
11427
|
await this.createDefaultIndexes();
|
|
11143
11428
|
await this.createCustomIndexes();
|
|
11144
11429
|
}
|
|
11145
11430
|
async dangerouslyClearAll() {
|
|
11146
11431
|
await this.pool.execute(`DELETE FROM ${formatTableName(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT)}`);
|
|
11432
|
+
await this.pool.execute(`DELETE FROM ${RUN_OWNERS_TABLE}`);
|
|
11433
|
+
}
|
|
11434
|
+
supportsRunFencing() {
|
|
11435
|
+
return true;
|
|
11436
|
+
}
|
|
11437
|
+
#ownershipError(operation, runId, error) {
|
|
11438
|
+
return new _mastra_core_error.MastraError({
|
|
11439
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("MYSQL", operation, "FAILED"),
|
|
11440
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
11441
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
11442
|
+
details: { runId }
|
|
11443
|
+
}, error);
|
|
11444
|
+
}
|
|
11445
|
+
async claimRunOwnership({ runId, ownerId, leaseMs, force, expectedGeneration }) {
|
|
11446
|
+
try {
|
|
11447
|
+
return await claimTransaction(this.pool, async (connection) => {
|
|
11448
|
+
const [existing] = await connection.execute(`SELECT generation FROM ${RUN_OWNERS_TABLE} WHERE ${RUN_ID} = ? FOR UPDATE`, [runId]);
|
|
11449
|
+
if (existing.length === 0) {
|
|
11450
|
+
if (expectedGeneration !== void 0 && expectedGeneration !== 0) return {
|
|
11451
|
+
acquired: false,
|
|
11452
|
+
record: null
|
|
11453
|
+
};
|
|
11454
|
+
await connection.execute(`INSERT INTO ${RUN_OWNERS_TABLE} (${RUN_ID}, generation, ${OWNER_ID}, ${LEASE_EXPIRES_AT}, ${UPDATED_AT})
|
|
11455
|
+
VALUES (?, GREATEST(1, ${DB_NOW_MS}), ?, ${DB_NOW_MS} + ?, ${DB_NOW_MS})`, [
|
|
11456
|
+
runId,
|
|
11457
|
+
ownerId,
|
|
11458
|
+
leaseMs
|
|
11459
|
+
]);
|
|
11460
|
+
return {
|
|
11461
|
+
acquired: true,
|
|
11462
|
+
record: await readRunOwner(connection, runId)
|
|
11463
|
+
};
|
|
11464
|
+
}
|
|
11465
|
+
await connection.execute(`UPDATE ${RUN_OWNERS_TABLE}
|
|
11466
|
+
SET generation = generation + 1, ${OWNER_ID} = ?, ${LEASE_EXPIRES_AT} = ${DB_NOW_MS} + ?,
|
|
11467
|
+
${UPDATED_AT} = ${DB_NOW_MS}
|
|
11468
|
+
WHERE ${RUN_ID} = ?
|
|
11469
|
+
AND (? IS NULL OR generation = ?)
|
|
11470
|
+
AND (? OR ${LEASE_EXPIRES_AT} IS NULL OR ${LEASE_EXPIRES_AT} <= ${DB_NOW_MS})`, [
|
|
11471
|
+
ownerId,
|
|
11472
|
+
leaseMs,
|
|
11473
|
+
runId,
|
|
11474
|
+
expectedGeneration ?? null,
|
|
11475
|
+
expectedGeneration ?? null,
|
|
11476
|
+
force === true
|
|
11477
|
+
]);
|
|
11478
|
+
const record = await readRunOwner(connection, runId);
|
|
11479
|
+
return record && record.generation === Number(existing[0].generation) + 1 ? {
|
|
11480
|
+
acquired: true,
|
|
11481
|
+
record
|
|
11482
|
+
} : {
|
|
11483
|
+
acquired: false,
|
|
11484
|
+
record
|
|
11485
|
+
};
|
|
11486
|
+
});
|
|
11487
|
+
} catch (error) {
|
|
11488
|
+
throw this.#ownershipError("CLAIM_RUN_OWNERSHIP", runId, error);
|
|
11489
|
+
}
|
|
11147
11490
|
}
|
|
11148
|
-
async
|
|
11491
|
+
async renewRunOwnership({ leaseMs, ...fence }) {
|
|
11149
11492
|
try {
|
|
11150
|
-
await this.pool
|
|
11493
|
+
return await inTransaction(this.pool, async (connection) => {
|
|
11494
|
+
await connection.execute(`UPDATE ${RUN_OWNERS_TABLE}
|
|
11495
|
+
SET ${LEASE_EXPIRES_AT} = ${DB_NOW_MS} + ?, ${UPDATED_AT} = ${DB_NOW_MS}
|
|
11496
|
+
WHERE ${RUN_ID} = ? AND generation = ? AND ${OWNER_ID} = ? AND ${LEASE_EXPIRES_AT} IS NOT NULL`, [
|
|
11497
|
+
leaseMs,
|
|
11498
|
+
fence.runId,
|
|
11499
|
+
fence.generation,
|
|
11500
|
+
fence.ownerId
|
|
11501
|
+
]);
|
|
11502
|
+
const record = await readRunOwner(connection, fence.runId);
|
|
11503
|
+
return record && (0, _mastra_core_storage.matchesRunFence)(record, fence) && record.leaseExpiresAt !== null ? {
|
|
11504
|
+
renewed: true,
|
|
11505
|
+
record
|
|
11506
|
+
} : {
|
|
11507
|
+
renewed: false,
|
|
11508
|
+
record
|
|
11509
|
+
};
|
|
11510
|
+
});
|
|
11151
11511
|
} catch (error) {
|
|
11512
|
+
throw this.#ownershipError("RENEW_RUN_OWNERSHIP", fence.runId, error);
|
|
11513
|
+
}
|
|
11514
|
+
}
|
|
11515
|
+
async releaseRunOwnership(fence) {
|
|
11516
|
+
try {
|
|
11517
|
+
return await inTransaction(this.pool, async (connection) => {
|
|
11518
|
+
await connection.execute(`UPDATE ${RUN_OWNERS_TABLE} SET ${LEASE_EXPIRES_AT} = NULL, ${UPDATED_AT} = ${DB_NOW_MS}
|
|
11519
|
+
WHERE ${RUN_ID} = ? AND generation = ? AND ${OWNER_ID} = ?`, [
|
|
11520
|
+
fence.runId,
|
|
11521
|
+
fence.generation,
|
|
11522
|
+
fence.ownerId
|
|
11523
|
+
]);
|
|
11524
|
+
return (0, _mastra_core_storage.matchesRunFence)(await readRunOwner(connection, fence.runId), fence);
|
|
11525
|
+
});
|
|
11526
|
+
} catch (error) {
|
|
11527
|
+
throw this.#ownershipError("RELEASE_RUN_OWNERSHIP", fence.runId, error);
|
|
11528
|
+
}
|
|
11529
|
+
}
|
|
11530
|
+
async getRunOwnership({ runId }) {
|
|
11531
|
+
try {
|
|
11532
|
+
return await readRunOwner(this.pool, runId);
|
|
11533
|
+
} catch (error) {
|
|
11534
|
+
throw this.#ownershipError("GET_RUN_OWNERSHIP", runId, error);
|
|
11535
|
+
}
|
|
11536
|
+
}
|
|
11537
|
+
async deleteWorkflowRunById({ runId, workflowName, fence }) {
|
|
11538
|
+
try {
|
|
11539
|
+
await withRunFence(this.pool, RUN_OWNERS_TABLE, (0, _mastra_core_storage.resolveRunFence)(this, fence, runId), "deleteWorkflowRunById", (q) => q.execute(`DELETE FROM ${formatTableName(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT)} WHERE ${quoteIdentifier("workflow_name", "column name")} = ? AND ${quoteIdentifier("run_id", "column name")} = ?`, [workflowName, runId]));
|
|
11540
|
+
} catch (error) {
|
|
11541
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
11152
11542
|
throw new _mastra_core_error.MastraError({
|
|
11153
11543
|
id: "MYSQL_WORKFLOWS_DELETE_RUN_BY_ID_FAILED",
|
|
11154
11544
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -11160,10 +11550,12 @@ var WorkflowsMySQL = class WorkflowsMySQL extends _mastra_core_storage.Workflows
|
|
|
11160
11550
|
}, error);
|
|
11161
11551
|
}
|
|
11162
11552
|
}
|
|
11163
|
-
async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state }) {
|
|
11553
|
+
async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state, fence: explicitFence }) {
|
|
11554
|
+
const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence, runId);
|
|
11164
11555
|
const connection = await this.pool.getConnection();
|
|
11165
11556
|
try {
|
|
11166
11557
|
await connection.beginTransaction();
|
|
11558
|
+
if (fence) await assertRunFence(connection, RUN_OWNERS_TABLE, fence, "updateWorkflowResults");
|
|
11167
11559
|
const [rows] = await connection.execute(`SELECT snapshot FROM ${formatTableName(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT)} WHERE ${quoteIdentifier("workflow_name", "column name")} = ? AND ${quoteIdentifier("run_id", "column name")} = ? FOR UPDATE`, [workflowName, runId]);
|
|
11168
11560
|
if (!Array.isArray(rows) || rows.length === 0) {
|
|
11169
11561
|
await connection.rollback();
|
|
@@ -11214,10 +11606,12 @@ var WorkflowsMySQL = class WorkflowsMySQL extends _mastra_core_storage.Workflows
|
|
|
11214
11606
|
connection.release();
|
|
11215
11607
|
}
|
|
11216
11608
|
}
|
|
11217
|
-
async updateWorkflowState({ workflowName, runId, opts }) {
|
|
11609
|
+
async updateWorkflowState({ workflowName, runId, opts, fence: explicitFence }) {
|
|
11610
|
+
const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence, runId);
|
|
11218
11611
|
const connection = await this.pool.getConnection();
|
|
11219
11612
|
try {
|
|
11220
11613
|
await connection.beginTransaction();
|
|
11614
|
+
if (fence) await assertRunFence(connection, RUN_OWNERS_TABLE, fence, "updateWorkflowState");
|
|
11221
11615
|
const [rows] = await connection.execute(`SELECT snapshot FROM ${formatTableName(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT)} WHERE ${quoteIdentifier("workflow_name", "column name")} = ? AND ${quoteIdentifier("run_id", "column name")} = ? FOR UPDATE`, [workflowName, runId]);
|
|
11222
11616
|
if (!Array.isArray(rows) || rows.length === 0) {
|
|
11223
11617
|
await connection.rollback();
|
|
@@ -11243,6 +11637,7 @@ var WorkflowsMySQL = class WorkflowsMySQL extends _mastra_core_storage.Workflows
|
|
|
11243
11637
|
return updatedSnapshot;
|
|
11244
11638
|
} catch (error) {
|
|
11245
11639
|
await connection.rollback();
|
|
11640
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
11246
11641
|
throw new _mastra_core_error.MastraError({
|
|
11247
11642
|
id: "MYSQL_WORKFLOWS_UPDATE_STATE_FAILED",
|
|
11248
11643
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -11256,35 +11651,38 @@ var WorkflowsMySQL = class WorkflowsMySQL extends _mastra_core_storage.Workflows
|
|
|
11256
11651
|
connection.release();
|
|
11257
11652
|
}
|
|
11258
11653
|
}
|
|
11259
|
-
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt }) {
|
|
11654
|
+
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
|
|
11260
11655
|
const now = /* @__PURE__ */ new Date();
|
|
11261
11656
|
const createdAtValue = createdAt ?? now;
|
|
11262
11657
|
const updatedAtValue = updatedAt ?? now;
|
|
11658
|
+
const tableName = formatTableName(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT);
|
|
11263
11659
|
try {
|
|
11264
|
-
|
|
11265
|
-
|
|
11266
|
-
|
|
11267
|
-
|
|
11268
|
-
|
|
11269
|
-
|
|
11270
|
-
|
|
11271
|
-
|
|
11272
|
-
|
|
11273
|
-
|
|
11274
|
-
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11286
|
-
|
|
11660
|
+
await withRunFence(this.pool, RUN_OWNERS_TABLE, (0, _mastra_core_storage.resolveRunFence)(this, fence, runId), "persistWorkflowSnapshot", async (q) => {
|
|
11661
|
+
const [updateResult] = await q.execute(`UPDATE ${tableName}
|
|
11662
|
+
SET ${quoteIdentifier("resourceId", "column name")} = ?,
|
|
11663
|
+
${quoteIdentifier("snapshot", "column name")} = ?,
|
|
11664
|
+
${quoteIdentifier("updatedAt", "column name")} = ?
|
|
11665
|
+
WHERE ${quoteIdentifier("workflow_name", "column name")} = ? AND ${quoteIdentifier("run_id", "column name")} = ?`, [
|
|
11666
|
+
resourceId ?? null,
|
|
11667
|
+
JSON.stringify(snapshot),
|
|
11668
|
+
transformToSqlValue(updatedAtValue),
|
|
11669
|
+
workflowName,
|
|
11670
|
+
runId
|
|
11671
|
+
]);
|
|
11672
|
+
if (updateResult.affectedRows > 0) return;
|
|
11673
|
+
await q.execute(`INSERT INTO ${tableName} (${quoteIdentifier("workflow_name", "column name")}, ${quoteIdentifier("run_id", "column name")}, ${quoteIdentifier("resourceId", "column name")}, ${quoteIdentifier("snapshot", "column name")}, ${quoteIdentifier("createdAt", "column name")}, ${quoteIdentifier("updatedAt", "column name")})
|
|
11674
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
11675
|
+
ON DUPLICATE KEY UPDATE ${quoteIdentifier("resourceId", "column name")} = VALUES(${quoteIdentifier("resourceId", "column name")}), ${quoteIdentifier("snapshot", "column name")} = VALUES(${quoteIdentifier("snapshot", "column name")}), ${quoteIdentifier("updatedAt", "column name")} = VALUES(${quoteIdentifier("updatedAt", "column name")})`, [
|
|
11676
|
+
workflowName,
|
|
11677
|
+
runId,
|
|
11678
|
+
resourceId ?? null,
|
|
11679
|
+
JSON.stringify(snapshot),
|
|
11680
|
+
transformToSqlValue(createdAtValue),
|
|
11681
|
+
transformToSqlValue(updatedAtValue)
|
|
11682
|
+
]);
|
|
11683
|
+
});
|
|
11287
11684
|
} catch (error) {
|
|
11685
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
11288
11686
|
throw new _mastra_core_error.MastraError({
|
|
11289
11687
|
id: "MYSQL_WORKFLOWS_PERSIST_SNAPSHOT_FAILED",
|
|
11290
11688
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|