@mastra/mysql 0.7.1-alpha.0 → 0.7.1-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @mastra/mysql
2
2
 
3
+ ## 0.7.1-alpha.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Cut warm initialization from around 110 client-server round trips to single digits with an init-scoped schema snapshot, on top of the column-probe casing fix that removed the ALTER TABLE storm. Three information_schema reads at the start of init() now answer table, column, and index existence locally; createTable, alterTable, createIndex, and hasColumn consult the snapshot and maintain it as objects are created, and the memory domain's raw CREATE INDEX for idx_om_lookup_key consults it too instead of raising and swallowing ER_DUP_KEYNAME on every boot. The snapshot lives for exactly the init window and is cleared in a finally, so runtime callers keep querying the live catalog. Measured on docker mysql:9.7: warm init 109 to 111 round trips down to 7 (6 excluding measurement scaffolding), cold init 253 down to 153 or 154 across runs, with an identical cold-init table and index census before and after. ([#21634](https://github.com/mastra-ai/mastra/pull/21634))
8
+
9
+ - Updated dependencies [[`6db7a5d`](https://github.com/mastra-ai/mastra/commit/6db7a5dd3dd2b6f7ef75dcd804fcffef5fa83963), [`0cdc5dc`](https://github.com/mastra-ai/mastra/commit/0cdc5dc69024957815da4f51acc4119eb4f447d7)]:
10
+ - @mastra/core@1.60.0-alpha.12
11
+
12
+ ## 0.7.1-alpha.1
13
+
14
+ ### Patch Changes
15
+
16
+ - Added a `durable` option to stored agents so agents created through the Agents API can run with durable execution — no code deployment required. ([#21715](https://github.com/mastra-ai/mastra/pull/21715))
17
+
18
+ ```typescript
19
+ await mastraClient.createStoredAgent({
20
+ id: 'helper',
21
+ name: 'Helper',
22
+ instructions: 'You are a helpful assistant.',
23
+ model: { provider: 'openai', name: 'gpt-5' },
24
+ durable: true,
25
+ });
26
+ ```
27
+
28
+ Pass `true` for defaults, or `{ maxSteps, cleanupTimeoutMs }` to tune the durable loop. Cache and pubsub are inherited from the server's Mastra instance, so configure distributed backends there for durability across replicas. Automatic recovery is still configured in code via `recovery.durableAgents`.
29
+
30
+ - Updated dependencies [[`6223446`](https://github.com/mastra-ai/mastra/commit/6223446ddce6166e96e0ba5e00d628b615dee8ca), [`583e235`](https://github.com/mastra-ai/mastra/commit/583e23519c13af16c1746f9c49722d011216611b), [`a77f8d4`](https://github.com/mastra-ai/mastra/commit/a77f8d4740d2178a74c41e4bf678b4fcd8fa0bb2), [`40d358e`](https://github.com/mastra-ai/mastra/commit/40d358e29d55543803e64b49241122f598ffabc7), [`e80cd7e`](https://github.com/mastra-ai/mastra/commit/e80cd7e7683e7d732e1cc6784bcac1d2640d2ce3), [`20504b2`](https://github.com/mastra-ai/mastra/commit/20504b2ecebd0e077acda3d457ab57480a98ed3e)]:
31
+ - @mastra/core@1.60.0-alpha.11
32
+
3
33
  ## 0.7.1-alpha.0
4
34
 
5
35
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -6,6 +6,46 @@ let _mastra_core_utils = require("@mastra/core/utils");
6
6
  let crypto$1 = require("crypto");
7
7
  let _mastra_core_agent = require("@mastra/core/agent");
8
8
  let _mastra_core_storage_domains_skills = require("@mastra/core/storage/domains/skills");
9
+ //#region src/storage/db/schema-snapshot.ts
10
+ /** Builds the `table.index` presence key the snapshot uses. */
11
+ function indexKey(table, index) {
12
+ return `${table.toLowerCase()}.${index.toLowerCase()}`;
13
+ }
14
+ const lower = (value) => String(value).toLowerCase();
15
+ /**
16
+ * Reads the catalog for `schemaName` in three queries. Returns null when no
17
+ * schema name is available (a pool with no default database): correctness over
18
+ * optimization, callers fall back to today's per-probe behavior.
19
+ */
20
+ async function loadSchemaSnapshot(pool, schemaName) {
21
+ if (!schemaName) return null;
22
+ const [[tableRows], [columnRows], [indexRows]] = await Promise.all([
23
+ pool.execute(`SELECT table_name FROM information_schema.tables WHERE table_schema = ?`, [schemaName]),
24
+ pool.execute(`SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = ?`, [schemaName]),
25
+ pool.execute(`SELECT DISTINCT table_name, index_name FROM information_schema.statistics WHERE table_schema = ?`, [schemaName])
26
+ ]);
27
+ const tables = /* @__PURE__ */ new Set();
28
+ for (const row of tableRows ?? []) tables.add(lower(row.table_name ?? row.TABLE_NAME));
29
+ const columns = /* @__PURE__ */ new Map();
30
+ for (const row of columnRows ?? []) {
31
+ const table = lower(row.table_name ?? row.TABLE_NAME);
32
+ let set = columns.get(table);
33
+ if (!set) {
34
+ set = /* @__PURE__ */ new Set();
35
+ columns.set(table, set);
36
+ }
37
+ set.add(lower(row.column_name ?? row.COLUMN_NAME));
38
+ }
39
+ const indexes = /* @__PURE__ */ new Set();
40
+ for (const row of indexRows ?? []) indexes.add(indexKey(lower(row.table_name ?? row.TABLE_NAME), lower(row.index_name ?? row.INDEX_NAME)));
41
+ return {
42
+ schemaName,
43
+ tables,
44
+ columns,
45
+ indexes
46
+ };
47
+ }
48
+ //#endregion
9
49
  //#region src/storage/domains/utils.ts
10
50
  function quoteIdentifier(value, context) {
11
51
  return `\`${(0, _mastra_core_utils.parseSqlIdentifier)(value, context)}\``;
@@ -145,12 +185,38 @@ var StoreOperationsMySQL = class extends _mastra_core_storage.StoreOperations {
145
185
  pool;
146
186
  database;
147
187
  resolvedDatabase;
188
+ /**
189
+ * Init-scoped catalog snapshot (see db/schema-snapshot.ts). Installed by
190
+ * MySQLStore.init() for exactly the init window and cleared in its finally;
191
+ * null at runtime so non-init callers keep probing the live catalog.
192
+ */
193
+ schemaSnapshot = null;
148
194
  constructor({ pool, database }) {
149
195
  super();
150
196
  this.pool = pool;
151
197
  this.database = database;
152
198
  this.resolvedDatabase = database ?? null;
153
199
  }
200
+ /**
201
+ * Loads and installs the init-scoped snapshot. A null load (no default
202
+ * database) or a failed load leaves the snapshot uninstalled, so init
203
+ * proceeds with today's per-probe behavior: correctness over optimization.
204
+ */
205
+ async loadInitSchemaSnapshot() {
206
+ try {
207
+ this.schemaSnapshot = await loadSchemaSnapshot(this.pool, await this.getDatabase());
208
+ } catch (error) {
209
+ console.warn("Failed to load init schema snapshot, falling back to per-object probing:", error);
210
+ this.schemaSnapshot = null;
211
+ }
212
+ }
213
+ clearInitSchemaSnapshot() {
214
+ this.schemaSnapshot = null;
215
+ }
216
+ /** Read by sibling domains (memory's raw index DDL) so snapshot state lives in exactly one place. */
217
+ getInitSchemaSnapshot() {
218
+ return this.schemaSnapshot;
219
+ }
154
220
  getPool() {
155
221
  return this.pool;
156
222
  }
@@ -166,6 +232,8 @@ var StoreOperationsMySQL = class extends _mastra_core_storage.StoreOperations {
166
232
  return this.resolvedDatabase ?? void 0;
167
233
  }
168
234
  async hasColumn(table, column) {
235
+ const snapshotColumns = this.schemaSnapshot?.columns.get(table.toLowerCase());
236
+ if (snapshotColumns) return snapshotColumns.has(column.toLowerCase());
169
237
  const db = await this.getDatabase();
170
238
  const params = [table, column];
171
239
  let sql = "SELECT COUNT(*) as count FROM information_schema.columns WHERE table_name = ? AND (column_name = ? OR column_name = ? )";
@@ -243,13 +311,23 @@ var StoreOperationsMySQL = class extends _mastra_core_storage.StoreOperations {
243
311
  return false;
244
312
  }
245
313
  async createTable({ tableName, schema }) {
314
+ const snapshot = this.schemaSnapshot;
315
+ if (snapshot?.tables.has(tableName.toLowerCase())) return;
246
316
  const connection = await this.pool.getConnection();
247
317
  try {
248
- const db = await this.getDatabase();
249
- const [t_rows] = await connection.query("SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = ? AND table_name = ?", [db ?? "", tableName]);
250
- if (Array.isArray(t_rows) && t_rows.length > 0 && t_rows[0].count > 0) return;
318
+ if (!snapshot) {
319
+ const db = await this.getDatabase();
320
+ const [t_rows] = await connection.query("SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = ? AND table_name = ?", [db ?? "", tableName]);
321
+ if (Array.isArray(t_rows) && t_rows.length > 0 && t_rows[0].count > 0) return;
322
+ }
251
323
  const sql = this.getCreateTableSQL(tableName, schema);
252
- await connection.execute(sql);
324
+ const [result] = await connection.execute(sql);
325
+ const created = result?.warningStatus === 0;
326
+ if (snapshot && created) {
327
+ const table = tableName.toLowerCase();
328
+ snapshot.tables.add(table);
329
+ snapshot.columns.set(table, new Set(Object.keys(schema).map((column) => column.toLowerCase())));
330
+ }
253
331
  } catch (error) {
254
332
  throw new _mastra_core_error.MastraError({
255
333
  id: "MYSQL_STORE_CREATE_TABLE_FAILED",
@@ -305,17 +383,22 @@ var StoreOperationsMySQL = class extends _mastra_core_storage.StoreOperations {
305
383
  const tableName = formatTableName(table, this.database);
306
384
  const indexName = quoteIdentifier(name, "index name");
307
385
  try {
308
- const db = await this.getDatabase() ?? "";
309
- const [existing] = await this.pool.execute(`SELECT 1 FROM information_schema.STATISTICS
386
+ const snapshot = this.schemaSnapshot;
387
+ if (snapshot) {
388
+ if (snapshot.indexes.has(indexKey(table, name))) return;
389
+ } else {
390
+ const db = await this.getDatabase() ?? "";
391
+ const [existing] = await this.pool.execute(`SELECT 1 FROM information_schema.STATISTICS
310
392
  WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND INDEX_NAME = ?
311
393
  LIMIT 1`, [
312
- db,
313
- table,
314
- name
315
- ]);
316
- if (existing.length > 0) return;
394
+ db,
395
+ table,
396
+ name
397
+ ]);
398
+ if (existing.length > 0) return;
399
+ }
317
400
  const [columnMeta] = await this.pool.execute(`SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.COLUMNS
318
- WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?`, [db, table]);
401
+ WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?`, [await this.getDatabase() ?? "", table]);
319
402
  const dataTypeByColumn = new Map(columnMeta.map((row) => [String(row.COLUMN_NAME).toLowerCase(), String(row.DATA_TYPE).toLowerCase()]));
320
403
  const PREFIX_TYPES = /* @__PURE__ */ new Set([
321
404
  "tinytext",
@@ -341,6 +424,7 @@ var StoreOperationsMySQL = class extends _mastra_core_storage.StoreOperations {
341
424
  }).join(", ");
342
425
  const sql = `CREATE ${unique ? "UNIQUE " : ""}INDEX ${indexName} ON ${tableName} (${columnsStr})`;
343
426
  await this.pool.execute(sql);
427
+ this.schemaSnapshot?.indexes.add(indexKey(table, name));
344
428
  } catch (error) {
345
429
  console.warn(`Failed to create index ${name}:`, error);
346
430
  }
@@ -572,17 +656,22 @@ var StoreOperationsMySQL = class extends _mastra_core_storage.StoreOperations {
572
656
  }
573
657
  async alterTable({ tableName, schema, ifNotExists }) {
574
658
  if (!ifNotExists.length) return;
575
- const db = await this.getDatabase();
576
- const [tableRows] = await this.pool.execute("SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = ? AND table_name = ?", [db ?? "", tableName]);
577
- if (!(Array.isArray(tableRows) && tableRows.length > 0 && tableRows[0].count > 0)) return;
578
- const params = [tableName];
579
- let sql = "SELECT column_name FROM information_schema.columns WHERE table_name = ?";
580
- if (db) {
581
- sql += " AND table_schema = ?";
582
- params.push(db);
659
+ const snapshotColumns = this.schemaSnapshot?.columns.get(tableName.toLowerCase());
660
+ let existing;
661
+ if (snapshotColumns) existing = snapshotColumns;
662
+ else {
663
+ const db = await this.getDatabase();
664
+ const [tableRows] = await this.pool.execute("SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = ? AND table_name = ?", [db ?? "", tableName]);
665
+ if (!(Array.isArray(tableRows) && tableRows.length > 0 && tableRows[0].count > 0)) return;
666
+ const params = [tableName];
667
+ let sql = "SELECT column_name FROM information_schema.columns WHERE table_name = ?";
668
+ if (db) {
669
+ sql += " AND table_schema = ?";
670
+ params.push(db);
671
+ }
672
+ const [rows] = await this.pool.execute(sql, params);
673
+ existing = new Set((rows || []).map((row) => String(row.column_name ?? row.COLUMN_NAME).toLowerCase()));
583
674
  }
584
- const [rows] = await this.pool.execute(sql, params);
585
- const existing = new Set((rows || []).map((row) => String(row.column_name ?? row.COLUMN_NAME).toLowerCase()));
586
675
  for (const columnName of ifNotExists) {
587
676
  if (existing.has(columnName.toLowerCase())) continue;
588
677
  const column = schema[columnName];
@@ -604,6 +693,7 @@ var StoreOperationsMySQL = class extends _mastra_core_storage.StoreOperations {
604
693
  const alterSql = `ALTER TABLE ${formatTableName(tableName, this.database)} ADD COLUMN ${parts.join(" ")}`;
605
694
  try {
606
695
  await this.pool.execute(alterSql);
696
+ existing.add(columnName.toLowerCase());
607
697
  } catch (error) {
608
698
  if (error?.code === "ER_DUP_FIELDNAME") continue;
609
699
  throw new _mastra_core_error.MastraError({
@@ -760,7 +850,8 @@ var AgentsMySQL = class AgentsMySQL extends _mastra_core_storage.AgentsStorage {
760
850
  "requestContextSchema",
761
851
  "workspace",
762
852
  "skills",
763
- "skillsFormat"
853
+ "skillsFormat",
854
+ "durable"
764
855
  ]
765
856
  });
766
857
  await this.createDefaultIndexes();
@@ -1052,6 +1143,7 @@ var AgentsMySQL = class AgentsMySQL extends _mastra_core_storage.AgentsStorage {
1052
1143
  workspace: input.workspace ?? null,
1053
1144
  skills: input.skills ?? null,
1054
1145
  skillsFormat: input.skillsFormat ?? null,
1146
+ durable: input.durable ?? null,
1055
1147
  changedFields: input.changedFields ?? null,
1056
1148
  changeMessage: input.changeMessage ?? null,
1057
1149
  createdAt: now
@@ -1267,6 +1359,7 @@ var AgentsMySQL = class AgentsMySQL extends _mastra_core_storage.AgentsStorage {
1267
1359
  workspace: this.safeParseJSON(row.workspace),
1268
1360
  skills: this.safeParseJSON(row.skills),
1269
1361
  skillsFormat: row.skillsFormat,
1362
+ durable: this.safeParseJSON(row.durable),
1270
1363
  changedFields: this.safeParseJSON(row.changedFields),
1271
1364
  changeMessage: row.changeMessage ?? void 0,
1272
1365
  createdAt: row.createdAt instanceof Date ? row.createdAt : new Date(row.createdAt)
@@ -4833,10 +4926,14 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
4833
4926
  schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_MESSAGES],
4834
4927
  ifNotExists: ["resourceId"]
4835
4928
  });
4836
- if (omSchema) try {
4837
- await this.pool.execute(`CREATE INDEX idx_om_lookup_key ON ${OM_TABLE_QUOTED} (${quoteIdentifier("lookupKey", "column name")}(191))`);
4838
- } catch (err) {
4839
- if (err?.errno !== 1061) throw err;
4929
+ if (omSchema) {
4930
+ const snapshot = this.operations.getInitSchemaSnapshot();
4931
+ if (!snapshot?.indexes.has(indexKey(OM_TABLE, "idx_om_lookup_key"))) try {
4932
+ await this.pool.execute(`CREATE INDEX idx_om_lookup_key ON ${OM_TABLE_QUOTED} (${quoteIdentifier("lookupKey", "column name")}(191))`);
4933
+ snapshot?.indexes.add(indexKey(OM_TABLE, "idx_om_lookup_key"));
4934
+ } catch (err) {
4935
+ if (err?.errno !== 1061) throw err;
4936
+ }
4840
4937
  }
4841
4938
  await this.createDefaultIndexes();
4842
4939
  await this.createCustomIndexes();
@@ -10355,6 +10452,7 @@ function parseConnectionString(connectionString, overrides) {
10355
10452
  }
10356
10453
  var MySQLStore = class extends _mastra_core_storage.MastraCompositeStore {
10357
10454
  pool;
10455
+ operations;
10358
10456
  stores;
10359
10457
  constructor(config) {
10360
10458
  super({
@@ -10369,6 +10467,7 @@ var MySQLStore = class extends _mastra_core_storage.MastraCompositeStore {
10369
10467
  pool: this.pool,
10370
10468
  database
10371
10469
  });
10470
+ this.operations = operations;
10372
10471
  const memory = new MemoryMySQL({
10373
10472
  pool: this.pool,
10374
10473
  operations,
@@ -10511,6 +10610,7 @@ var MySQLStore = class extends _mastra_core_storage.MastraCompositeStore {
10511
10610
  async init() {
10512
10611
  try {
10513
10612
  (await this.pool.getConnection()).release();
10613
+ await this.operations.loadInitSchemaSnapshot();
10514
10614
  await super.init();
10515
10615
  } catch (error) {
10516
10616
  throw new _mastra_core_error.MastraError({
@@ -10518,6 +10618,8 @@ var MySQLStore = class extends _mastra_core_storage.MastraCompositeStore {
10518
10618
  domain: _mastra_core_error.ErrorDomain.STORAGE,
10519
10619
  category: _mastra_core_error.ErrorCategory.THIRD_PARTY
10520
10620
  }, error);
10621
+ } finally {
10622
+ this.operations.clearInitSchemaSnapshot();
10521
10623
  }
10522
10624
  }
10523
10625
  async close() {