@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 +9 -0
- package/dist/index.cjs +125 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +125 -26
- package/dist/index.js.map +1 -1
- package/dist/storage/db/schema-snapshot.d.ts +55 -0
- package/dist/storage/db/schema-snapshot.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/operations/index.d.ts +16 -0
- package/dist/storage/domains/operations/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +3 -3
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
|
309
|
-
|
|
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
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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 = ?`, [
|
|
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
|
|
576
|
-
|
|
577
|
-
if (
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
params
|
|
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)
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
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() {
|