@mastra/libsql 1.10.1-alpha.2 → 1.10.1-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @mastra/libsql
2
2
 
3
+ ## 1.10.1-alpha.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Improved local LibSQL startup performance by applying conservative local SQLite performance settings before initialization, exposing local PRAGMA overrides, and reducing schema initialization contention. ([#16513](https://github.com/mastra-ai/mastra/pull/16513))
8
+
9
+ - Added LibSQL indexes for thread message history queries to speed up recent-message and observational-memory loading. ([#16513](https://github.com/mastra-ai/mastra/pull/16513))
10
+
11
+ - Updated dependencies [[`4999667`](https://github.com/mastra-ai/mastra/commit/49996678b68356cad7f088430009690406c50fbd)]:
12
+ - @mastra/core@1.33.0-alpha.17
13
+
3
14
  ## 1.10.1-alpha.2
4
15
 
5
16
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-libsql
3
3
  description: Documentation for @mastra/libsql. Use when working with @mastra/libsql APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/libsql"
6
- version: "1.10.1-alpha.2"
6
+ version: "1.10.1-alpha.3"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.10.1-alpha.2",
2
+ "version": "1.10.1-alpha.3",
3
3
  "package": "@mastra/libsql",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -242,4 +242,4 @@ const storage = new MastraCompositeStore({
242
242
 
243
243
  > **Note:** `ObservabilityStorageClickhouseVNext` is the current observability domain implementation. The legacy `ObservabilityStorageClickhouse` class is also exported and remains supported for projects that have not migrated. See the [ClickHouse storage reference](https://mastra.ai/reference/storage/clickhouse) for details.
244
244
 
245
- > **Info:** This approach is also required when using storage providers that don't support observability (like Convex, DynamoDB, or Cloudflare). See the [DefaultExporter documentation](https://mastra.ai/docs/observability/tracing/exporters/default) for the full list of supported providers.
245
+ > **Info:** This approach is also required when using storage providers that don't support observability (like Convex, DynamoDB, or Cloudflare). See the [MastraStorageExporter documentation](https://mastra.ai/docs/observability/tracing/exporters/mastra-storage) for the full list of supported providers.
@@ -2,7 +2,7 @@
2
2
 
3
3
  The DynamoDB storage implementation provides a scalable and performant NoSQL database solution for Mastra, leveraging a single-table design pattern with [ElectroDB](https://electrodb.dev/).
4
4
 
5
- > **Observability Not Supported:** DynamoDB storage **doesn't support the observability domain**. Traces from the `DefaultExporter` can't be persisted to DynamoDB, and [Studio's](https://mastra.ai/docs/studio/overview) observability features won't work with DynamoDB as your only storage provider. To enable observability, use [composite storage](https://mastra.ai/reference/storage/composite) to route observability data to a supported provider like ClickHouse or PostgreSQL.
5
+ > **Observability Not Supported:** DynamoDB storage **doesn't support the observability domain**. Traces from the `MastraStorageExporter` can't be persisted to DynamoDB, and [Studio's](https://mastra.ai/docs/studio/overview) observability features won't work with DynamoDB as your only storage provider. To enable observability, use [composite storage](https://mastra.ai/reference/storage/composite) to route observability data to a supported provider like ClickHouse.
6
6
 
7
7
  > **Item Size Limit:** DynamoDB enforces a **400 KB maximum item size**. This limit can be exceeded when storing messages with base64-encoded attachments such as images. See [Handling large attachments](https://mastra.ai/docs/memory/storage) for workarounds including uploading attachments to external storage.
8
8
 
@@ -130,6 +130,6 @@ const thread = await memoryStore?.getThreadById({ threadId: '...' })
130
130
 
131
131
  ## Observability
132
132
 
133
- libSQL supports observability and is ideal for local development. Use the `realtime` [tracing strategy](https://mastra.ai/docs/observability/tracing/exporters/default) for immediate visibility while debugging.
133
+ libSQL supports observability and is ideal for local development. Use the `realtime` [tracing strategy](https://mastra.ai/docs/observability/tracing/exporters/mastra-storage) for immediate visibility while debugging.
134
134
 
135
135
  For production environments with higher trace volumes, consider using [PostgreSQL](https://mastra.ai/reference/storage/postgresql) or [ClickHouse via composite storage](https://mastra.ai/reference/storage/composite).
package/dist/index.cjs CHANGED
@@ -1934,15 +1934,21 @@ var LibSQLDB = class extends base.MastraBase {
1934
1934
  async migrateSpansTable() {
1935
1935
  const schema = storage.TABLE_SCHEMAS[storage.TABLE_SPANS];
1936
1936
  try {
1937
+ const existingColumnsRaw = await this.getTableColumns(storage.TABLE_SPANS);
1938
+ const existingColumns = new Set([...existingColumnsRaw].map((column) => column.toLowerCase()));
1939
+ let addedColumns = false;
1937
1940
  for (const [columnName, columnDef] of Object.entries(schema)) {
1938
- const columnExists = await this.hasColumn(storage.TABLE_SPANS, columnName);
1939
- if (!columnExists) {
1941
+ if (!existingColumns.has(columnName.toLowerCase())) {
1940
1942
  const sqlType = this.getSqlType(columnDef.type);
1941
1943
  const alterSql = `ALTER TABLE "${storage.TABLE_SPANS}" ADD COLUMN "${columnName}" ${sqlType}`;
1942
1944
  await this.client.execute(alterSql);
1945
+ addedColumns = true;
1943
1946
  this.logger.debug(`LibSQLDB: Added column '${columnName}' to ${storage.TABLE_SPANS}`);
1944
1947
  }
1945
1948
  }
1949
+ if (addedColumns) {
1950
+ this.tableColumnsCache.delete(storage.TABLE_SPANS);
1951
+ }
1946
1952
  const indexExists = await this.spansUniqueIndexExists();
1947
1953
  if (!indexExists) {
1948
1954
  const duplicateInfo = await this.checkForDuplicateSpans();
@@ -3351,11 +3357,18 @@ var ChannelsLibSQL = class extends storage.ChannelsStorage {
3351
3357
  tableName: storage.TABLE_CHANNEL_CONFIG,
3352
3358
  schema: storage.TABLE_SCHEMAS[storage.TABLE_CHANNEL_CONFIG]
3353
3359
  });
3354
- await this.#client.execute(
3355
- `CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_installations_webhook ON "${storage.TABLE_CHANNEL_INSTALLATIONS}" ("webhookId")`
3356
- );
3357
- await this.#client.execute(
3358
- `CREATE INDEX IF NOT EXISTS idx_channel_installations_platform_agent ON "${storage.TABLE_CHANNEL_INSTALLATIONS}" ("platform", "agentId")`
3360
+ await this.#client.batch(
3361
+ [
3362
+ {
3363
+ sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_installations_webhook ON "${storage.TABLE_CHANNEL_INSTALLATIONS}" ("webhookId")`,
3364
+ args: []
3365
+ },
3366
+ {
3367
+ sql: `CREATE INDEX IF NOT EXISTS idx_channel_installations_platform_agent ON "${storage.TABLE_CHANNEL_INSTALLATIONS}" ("platform", "agentId")`,
3368
+ args: []
3369
+ }
3370
+ ],
3371
+ "write"
3359
3372
  );
3360
3373
  }
3361
3374
  async dangerouslyClearAll() {
@@ -3499,26 +3512,31 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3499
3512
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "requestContext", "TEXT");
3500
3513
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "source", "TEXT");
3501
3514
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "expectedTrajectory", "TEXT");
3502
- await this.#client.execute({
3503
- sql: `CREATE INDEX IF NOT EXISTS idx_dataset_items_dataset_validto ON "${storage.TABLE_DATASET_ITEMS}" ("datasetId", "validTo")`,
3504
- args: []
3505
- });
3506
- await this.#client.execute({
3507
- sql: `CREATE INDEX IF NOT EXISTS idx_dataset_items_dataset_version ON "${storage.TABLE_DATASET_ITEMS}" ("datasetId", "datasetVersion")`,
3508
- args: []
3509
- });
3510
- await this.#client.execute({
3511
- sql: `CREATE INDEX IF NOT EXISTS idx_dataset_items_dataset_validto_deleted ON "${storage.TABLE_DATASET_ITEMS}" ("datasetId", "validTo", "isDeleted")`,
3512
- args: []
3513
- });
3514
- await this.#client.execute({
3515
- sql: `CREATE INDEX IF NOT EXISTS idx_dataset_versions_dataset_version ON "${storage.TABLE_DATASET_VERSIONS}" ("datasetId", "version")`,
3516
- args: []
3517
- });
3518
- await this.#client.execute({
3519
- sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_dataset_versions_dataset_version_unique ON "${storage.TABLE_DATASET_VERSIONS}" ("datasetId", "version")`,
3520
- args: []
3521
- });
3515
+ await this.#client.batch(
3516
+ [
3517
+ {
3518
+ sql: `CREATE INDEX IF NOT EXISTS idx_dataset_items_dataset_validto ON "${storage.TABLE_DATASET_ITEMS}" ("datasetId", "validTo")`,
3519
+ args: []
3520
+ },
3521
+ {
3522
+ sql: `CREATE INDEX IF NOT EXISTS idx_dataset_items_dataset_version ON "${storage.TABLE_DATASET_ITEMS}" ("datasetId", "datasetVersion")`,
3523
+ args: []
3524
+ },
3525
+ {
3526
+ sql: `CREATE INDEX IF NOT EXISTS idx_dataset_items_dataset_validto_deleted ON "${storage.TABLE_DATASET_ITEMS}" ("datasetId", "validTo", "isDeleted")`,
3527
+ args: []
3528
+ },
3529
+ {
3530
+ sql: `CREATE INDEX IF NOT EXISTS idx_dataset_versions_dataset_version ON "${storage.TABLE_DATASET_VERSIONS}" ("datasetId", "version")`,
3531
+ args: []
3532
+ },
3533
+ {
3534
+ sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_dataset_versions_dataset_version_unique ON "${storage.TABLE_DATASET_VERSIONS}" ("datasetId", "version")`,
3535
+ args: []
3536
+ }
3537
+ ],
3538
+ "write"
3539
+ );
3522
3540
  }
3523
3541
  async #addColumnIfNotExists(table, column, sqlType) {
3524
3542
  const exists = await this.#db.hasColumn(table, column);
@@ -4419,18 +4437,23 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
4419
4437
  schema: storage.EXPERIMENT_RESULTS_SCHEMA,
4420
4438
  ifNotExists: ["status", "tags"]
4421
4439
  });
4422
- await this.#client.execute({
4423
- sql: `CREATE INDEX IF NOT EXISTS idx_experiments_datasetid ON "${storage.TABLE_EXPERIMENTS}" ("datasetId")`,
4424
- args: []
4425
- });
4426
- await this.#client.execute({
4427
- sql: `CREATE INDEX IF NOT EXISTS idx_experiment_results_experimentid ON "${storage.TABLE_EXPERIMENT_RESULTS}" ("experimentId")`,
4428
- args: []
4429
- });
4430
- await this.#client.execute({
4431
- sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_experiment_results_exp_item ON "${storage.TABLE_EXPERIMENT_RESULTS}" ("experimentId", "itemId")`,
4432
- args: []
4433
- });
4440
+ await this.#client.batch(
4441
+ [
4442
+ {
4443
+ sql: `CREATE INDEX IF NOT EXISTS idx_experiments_datasetid ON "${storage.TABLE_EXPERIMENTS}" ("datasetId")`,
4444
+ args: []
4445
+ },
4446
+ {
4447
+ sql: `CREATE INDEX IF NOT EXISTS idx_experiment_results_experimentid ON "${storage.TABLE_EXPERIMENT_RESULTS}" ("experimentId")`,
4448
+ args: []
4449
+ },
4450
+ {
4451
+ sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_experiment_results_exp_item ON "${storage.TABLE_EXPERIMENT_RESULTS}" ("experimentId", "itemId")`,
4452
+ args: []
4453
+ }
4454
+ ],
4455
+ "write"
4456
+ );
4434
4457
  }
4435
4458
  async dangerouslyClearAll() {
4436
4459
  await this.#db.deleteData({ tableName: storage.TABLE_EXPERIMENT_RESULTS });
@@ -5970,6 +5993,19 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
5970
5993
  schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES],
5971
5994
  ifNotExists: ["resourceId"]
5972
5995
  });
5996
+ await this.#client.batch(
5997
+ [
5998
+ {
5999
+ sql: `CREATE INDEX IF NOT EXISTS idx_messages_thread_created_at ON ${storage.TABLE_MESSAGES} (thread_id, "createdAt")`,
6000
+ args: []
6001
+ },
6002
+ {
6003
+ sql: `CREATE INDEX IF NOT EXISTS idx_messages_thread_resource_created_at ON ${storage.TABLE_MESSAGES} (thread_id, "resourceId", "createdAt")`,
6004
+ args: []
6005
+ }
6006
+ ],
6007
+ "write"
6008
+ );
5973
6009
  if (omSchema) {
5974
6010
  await this.#client.execute({
5975
6011
  sql: `CREATE INDEX IF NOT EXISTS idx_om_lookup_key ON "${OM_TABLE}" ("lookupKey")`,
@@ -11403,10 +11439,15 @@ var WorkspacesLibSQL = class extends storage.WorkspacesStorage {
11403
11439
  };
11404
11440
 
11405
11441
  // src/storage/index.ts
11442
+ var DEFAULT_LOCAL_CACHE_SIZE = -16e3;
11443
+ var DEFAULT_LOCAL_MMAP_SIZE = 134217728;
11406
11444
  var LibSQLStore = class extends storage.MastraCompositeStore {
11407
11445
  client;
11408
11446
  maxRetries;
11409
11447
  initialBackoffMs;
11448
+ pragmasReady;
11449
+ isLocalDb;
11450
+ localPragmas;
11410
11451
  stores;
11411
11452
  constructor(config) {
11412
11453
  if (!config.id || typeof config.id !== "string" || config.id.trim() === "") {
@@ -11415,20 +11456,24 @@ var LibSQLStore = class extends storage.MastraCompositeStore {
11415
11456
  super({ id: config.id, name: `LibSQLStore`, disableInit: config.disableInit });
11416
11457
  this.maxRetries = config.maxRetries ?? 5;
11417
11458
  this.initialBackoffMs = config.initialBackoffMs ?? 100;
11459
+ this.localPragmas = {
11460
+ cacheSize: config.localPragmas?.cacheSize ?? DEFAULT_LOCAL_CACHE_SIZE,
11461
+ mmapSize: config.localPragmas?.mmapSize ?? DEFAULT_LOCAL_MMAP_SIZE
11462
+ };
11418
11463
  if ("url" in config) {
11419
- if (config.url.endsWith(":memory:")) {
11464
+ if (config.url.includes(":memory:")) {
11420
11465
  this.shouldCacheInit = false;
11421
11466
  }
11422
11467
  this.client = client.createClient({
11423
11468
  url: config.url,
11424
11469
  ...config.authToken ? { authToken: config.authToken } : {}
11425
11470
  });
11426
- if (config.url.startsWith("file:") || config.url.includes(":memory:")) {
11427
- this.client.execute("PRAGMA journal_mode=WAL;").then(() => this.logger.debug("LibSQLStore: PRAGMA journal_mode=WAL set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA journal_mode=WAL.", err));
11428
- this.client.execute("PRAGMA busy_timeout = 5000;").then(() => this.logger.debug("LibSQLStore: PRAGMA busy_timeout=5000 set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA busy_timeout.", err));
11429
- }
11471
+ this.isLocalDb = config.url.startsWith("file:") || config.url.includes(":memory:");
11472
+ this.pragmasReady = this.isLocalDb ? this.applyLocalPragmas() : Promise.resolve();
11430
11473
  } else {
11431
11474
  this.client = config.client;
11475
+ this.isLocalDb = false;
11476
+ this.pragmasReady = Promise.resolve();
11432
11477
  }
11433
11478
  const domainConfig = {
11434
11479
  client: this.client,
@@ -11472,6 +11517,63 @@ var LibSQLStore = class extends storage.MastraCompositeStore {
11472
11517
  schedules
11473
11518
  };
11474
11519
  }
11520
+ async applyLocalPragmas() {
11521
+ const pragmas = [
11522
+ ["journal_mode=WAL", "PRAGMA journal_mode=WAL;"],
11523
+ ["busy_timeout=5000", "PRAGMA busy_timeout=5000;"],
11524
+ ["synchronous=NORMAL", "PRAGMA synchronous=NORMAL;"],
11525
+ ["temp_store=MEMORY", "PRAGMA temp_store=MEMORY;"],
11526
+ [`cache_size=${this.localPragmas.cacheSize}`, `PRAGMA cache_size=${this.localPragmas.cacheSize};`],
11527
+ [`mmap_size=${this.localPragmas.mmapSize}`, `PRAGMA mmap_size=${this.localPragmas.mmapSize};`]
11528
+ ];
11529
+ for (const [label, sql] of pragmas) {
11530
+ try {
11531
+ await this.client.execute(sql);
11532
+ this.logger.debug(`LibSQLStore: PRAGMA ${label} set.`);
11533
+ } catch (err) {
11534
+ this.logger.warn(`LibSQLStore: Failed to set PRAGMA ${label}.`, err);
11535
+ }
11536
+ }
11537
+ }
11538
+ getStoresToInit() {
11539
+ return Object.values(this.stores).filter(Boolean);
11540
+ }
11541
+ async initDomainsSequentially() {
11542
+ for (const store of this.getStoresToInit()) {
11543
+ await store.init();
11544
+ }
11545
+ return true;
11546
+ }
11547
+ async initDomainsInParallel() {
11548
+ await Promise.all(this.getStoresToInit().map((store) => store.init()));
11549
+ return true;
11550
+ }
11551
+ async init() {
11552
+ await this.pragmasReady;
11553
+ if (!this.isLocalDb) {
11554
+ if (this.shouldCacheInit) {
11555
+ if (this.hasInitialized) {
11556
+ await this.hasInitialized;
11557
+ return;
11558
+ }
11559
+ this.hasInitialized = this.initDomainsInParallel();
11560
+ await this.hasInitialized;
11561
+ return;
11562
+ }
11563
+ await this.initDomainsInParallel();
11564
+ return;
11565
+ }
11566
+ if (this.shouldCacheInit) {
11567
+ if (this.hasInitialized) {
11568
+ await this.hasInitialized;
11569
+ return;
11570
+ }
11571
+ this.hasInitialized = this.initDomainsSequentially();
11572
+ await this.hasInitialized;
11573
+ return;
11574
+ }
11575
+ await this.initDomainsSequentially();
11576
+ }
11475
11577
  };
11476
11578
 
11477
11579
  // src/vector/prompt.ts