@mastra/mysql 0.7.1-alpha.1 → 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,14 @@
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
+
3
12
  ## 0.7.1-alpha.1
4
13
 
5
14
  ### 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({
@@ -4836,10 +4926,14 @@ var MemoryMySQL = class MemoryMySQL extends _mastra_core_storage.MemoryStorage {
4836
4926
  schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_MESSAGES],
4837
4927
  ifNotExists: ["resourceId"]
4838
4928
  });
4839
- if (omSchema) try {
4840
- await this.pool.execute(`CREATE INDEX idx_om_lookup_key ON ${OM_TABLE_QUOTED} (${quoteIdentifier("lookupKey", "column name")}(191))`);
4841
- } catch (err) {
4842
- 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
+ }
4843
4937
  }
4844
4938
  await this.createDefaultIndexes();
4845
4939
  await this.createCustomIndexes();
@@ -10358,6 +10452,7 @@ function parseConnectionString(connectionString, overrides) {
10358
10452
  }
10359
10453
  var MySQLStore = class extends _mastra_core_storage.MastraCompositeStore {
10360
10454
  pool;
10455
+ operations;
10361
10456
  stores;
10362
10457
  constructor(config) {
10363
10458
  super({
@@ -10372,6 +10467,7 @@ var MySQLStore = class extends _mastra_core_storage.MastraCompositeStore {
10372
10467
  pool: this.pool,
10373
10468
  database
10374
10469
  });
10470
+ this.operations = operations;
10375
10471
  const memory = new MemoryMySQL({
10376
10472
  pool: this.pool,
10377
10473
  operations,
@@ -10514,6 +10610,7 @@ var MySQLStore = class extends _mastra_core_storage.MastraCompositeStore {
10514
10610
  async init() {
10515
10611
  try {
10516
10612
  (await this.pool.getConnection()).release();
10613
+ await this.operations.loadInitSchemaSnapshot();
10517
10614
  await super.init();
10518
10615
  } catch (error) {
10519
10616
  throw new _mastra_core_error.MastraError({
@@ -10521,6 +10618,8 @@ var MySQLStore = class extends _mastra_core_storage.MastraCompositeStore {
10521
10618
  domain: _mastra_core_error.ErrorDomain.STORAGE,
10522
10619
  category: _mastra_core_error.ErrorCategory.THIRD_PARTY
10523
10620
  }, error);
10621
+ } finally {
10622
+ this.operations.clearInitSchemaSnapshot();
10524
10623
  }
10525
10624
  }
10526
10625
  async close() {