@mastra/dsql 0.0.0

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.
Files changed (2) hide show
  1. package/README.md +357 -0
  2. package/package.json +69 -0
package/README.md ADDED
@@ -0,0 +1,357 @@
1
+ # @mastra/dsql
2
+
3
+ Amazon Aurora DSQL storage implementation for Mastra, providing thread, message, workflow, and observability storage using Aurora DSQL with IAM authentication.
4
+
5
+ > **Note**
6
+ > Aurora DSQL doesn’t support PostgreSQL extensions (`CREATE EXTENSION`), including `pgvector`.
7
+ > For vector storage, use a separate vector store like `@mastra/s3vectors`.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @mastra/dsql
13
+ ```
14
+
15
+ ## Prerequisites
16
+
17
+ - Amazon Aurora DSQL cluster
18
+ - AWS credentials with access to the DSQL cluster (IAM authentication)
19
+
20
+ ## Usage
21
+
22
+ ### Storage
23
+
24
+ ```typescript
25
+ import { DSQLStore } from '@mastra/dsql';
26
+
27
+ const store = new DSQLStore({
28
+ id: 'my-dsql-store',
29
+ host: 'abc123.dsql.us-east-1.on.aws',
30
+ // region is auto-detected from host, or specify explicitly:
31
+ // region: 'us-east-1',
32
+ // user: 'admin', // default
33
+ // database: 'postgres', // default
34
+ });
35
+
36
+ // Initialize the store (creates tables if needed)
37
+ await store.init();
38
+
39
+ // Create a thread
40
+ await store.saveThread({
41
+ thread: {
42
+ id: 'thread-123',
43
+ resourceId: 'resource-456',
44
+ title: 'My Thread',
45
+ metadata: { key: 'value' },
46
+ createdAt: new Date(),
47
+ },
48
+ });
49
+
50
+ // Add messages to thread
51
+ await store.saveMessages({
52
+ messages: [
53
+ {
54
+ id: 'msg-789',
55
+ threadId: 'thread-123',
56
+ role: 'user',
57
+ content: { content: 'Hello' },
58
+ resourceId: 'resource-456',
59
+ createdAt: new Date(),
60
+ },
61
+ ],
62
+ });
63
+
64
+ // Query threads and messages
65
+ const savedThread = await store.getThreadById({ threadId: 'thread-123' });
66
+ const messages = await store.listMessages({ threadId: 'thread-123' });
67
+ ```
68
+
69
+ ## Configuration
70
+
71
+ ### Connection Methods
72
+
73
+ `DSQLStore` supports multiple connection methods:
74
+
75
+ **1. Host Configuration (Recommended)**
76
+
77
+ ```typescript
78
+ import { DSQLStore } from '@mastra/dsql';
79
+
80
+ const store = new DSQLStore({
81
+ id: 'my-dsql-store',
82
+ host: 'abc123.dsql.us-east-1.on.aws',
83
+ // region is auto-detected from host, or specify explicitly
84
+ // user: 'admin', // default
85
+ // database: 'postgres', // default
86
+ schemaName: 'custom_schema', // optional
87
+ });
88
+ ```
89
+
90
+ **2. Pre-configured pg.Pool**
91
+
92
+ ```typescript
93
+ import { Pool } from 'pg';
94
+ import { AuroraDSQLClient } from '@aws/aurora-dsql-node-postgres-connector';
95
+ import { DSQLStore } from '@mastra/dsql';
96
+
97
+ const pool = new Pool({
98
+ host: 'abc123.dsql.us-east-1.on.aws',
99
+ Client: AuroraDSQLClient,
100
+ region: 'us-east-1',
101
+ });
102
+
103
+ const store = new DSQLStore({
104
+ id: 'my-dsql-store',
105
+ pool,
106
+ });
107
+
108
+ // Use store.pool for other libraries that need a pg.Pool
109
+ ```
110
+
111
+ ### Custom AWS Credentials
112
+
113
+ ```typescript
114
+ import { DSQLStore } from '@mastra/dsql';
115
+ import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
116
+
117
+ const store = new DSQLStore({
118
+ id: 'my-dsql-store',
119
+ host: 'abc123.dsql.us-east-1.on.aws',
120
+ customCredentialsProvider: fromNodeProviderChain(),
121
+ });
122
+ ```
123
+
124
+ ### Connection Pool Settings
125
+
126
+ ```typescript
127
+ const store = new DSQLStore({
128
+ id: 'my-dsql-store',
129
+ host: 'abc123.dsql.us-east-1.on.aws',
130
+
131
+ // Connection pool settings
132
+ max: 10, // maximum connections (default: 10)
133
+ min: 0, // minimum connections (default: 0)
134
+ idleTimeoutMillis: 600000, // 10 minutes (default)
135
+ maxLifetimeSeconds: 3300, // 55 minutes (default, must be < 3600)
136
+ connectionTimeoutMillis: 5000, // 5 seconds (default)
137
+ allowExitOnIdle: true, // default: true
138
+ });
139
+ ```
140
+
141
+ ### Configuration Options
142
+
143
+ | Option | Type | Default | Description |
144
+ | --------------------------- | ----------------------------- | --------------- | --------------------------------------------------------------------------------------------------------- |
145
+ | `id` | string | (required) | Unique identifier for this store instance |
146
+ | `host` | string | (required)\* | DSQL cluster endpoint (e.g., `abc123.dsql.us-east-1.on.aws`) |
147
+ | `pool` | pg.Pool | - | Pre-configured pg.Pool instance (cannot be used with host) |
148
+ | `user` | string | `'admin'` | Database user (Aurora DSQL built-in admin role is `admin`) |
149
+ | `database` | string | `'postgres'` | Database name (Aurora DSQL exposes a single built-in database named `postgres` per cluster) |
150
+ | `region` | string | (auto-detected) | AWS region, extracted from host if not provided |
151
+ | `schemaName` | string | `'public'` | PostgreSQL schema name where Mastra tables/indexes are created |
152
+ | `customCredentialsProvider` | AwsCredentialIdentityProvider | (default chain) | Custom AWS credentials provider |
153
+ | `max` | number | `10` | Maximum connections in the pool |
154
+ | `min` | number | `0` | Minimum connections in the pool |
155
+ | `idleTimeoutMillis` | number | `600000` | Close idle connections after this many milliseconds |
156
+ | `maxLifetimeSeconds` | number | `3300` | Maximum connection lifetime in seconds (must be `< 3600` due to Aurora DSQL’s 60-minute connection limit) |
157
+ | `connectionTimeoutMillis` | number | `5000` | Connection acquisition timeout in milliseconds |
158
+ | `allowExitOnIdle` | boolean | `true` | Allow the process to exit when all connections are idle |
159
+
160
+ \* Either `host` or `pool` is required.
161
+
162
+ ### Default Connection Pool Settings
163
+
164
+ The default pool settings are optimized for Aurora DSQL:
165
+
166
+ - Maximum connections: 10
167
+ - Idle timeout: 10 minutes (600,000 ms)
168
+ - Max connection lifetime: 55 minutes (3,300 seconds)
169
+ - Connection timeout: 5 seconds
170
+
171
+ The `maxLifetimeSeconds` is set to 55 minutes to ensure connections are rotated before Aurora DSQL's 60-minute connection duration limit.
172
+
173
+ ## Aurora DSQL Specifics
174
+
175
+ `@mastra/dsql` is built on top of [Amazon Aurora DSQL](https://docs.aws.amazon.com/aurora-dsql/) using the official Node.js connector.
176
+ You usually interact with it like any other Mastra store, but there are some important Aurora DSQL characteristics to be aware of:
177
+
178
+ - **IAM-only authentication**
179
+ - Connections are authenticated with IAM (no database passwords).
180
+ - `@mastra/dsql` uses `@aws/aurora-dsql-node-postgres-connector` to generate short-lived auth tokens automatically.
181
+ - You can plug in a custom credentials provider via `customCredentialsProvider`.
182
+
183
+ - **Single database, schema-based isolation**
184
+ - Each cluster exposes a single built-in database named `postgres` (no `CREATE DATABASE`).
185
+ - Logical separation is done via schemas; `schemaName` controls where Mastra tables are created.
186
+ - Typical pattern: initialize as `admin`, optionally create an application schema, then connect as a non-admin role using that schema.
187
+
188
+ - **No PostgreSQL extensions**
189
+ - `CREATE EXTENSION` is not supported (including `pgvector`, `PostGIS`, etc.).
190
+ - For vectors or extension-based features, use a separate store (e.g. `@mastra/s3vectors`) alongside `DSQLStore`.
191
+
192
+ - **JSON stored as text**
193
+ - JSON/JSONB are available as _query_ types but not as column types.
194
+ - `@mastra/dsql` stores structured fields (metadata, content, etc.) in `TEXT` columns and casts to JSON at query time as needed.
195
+
196
+ - **Schema & DDL constraints**
197
+ - Some PostgreSQL features are not available (e.g. foreign key constraints, `TRUNCATE`, synchronous `CREATE INDEX`).
198
+ - Indexes are created asynchronously using `CREATE INDEX ASYNC`; there is a limit on the number of indexes per table.
199
+ - The store’s `init()` and index helper APIs are implemented to respect these constraints.
200
+
201
+ - **Transactions & optimistic concurrency**
202
+ - Aurora DSQL uses optimistic concurrency control (OCC) and may return retriable OCC errors under contention.
203
+ - There are limits on transaction duration and size; large bulk operations should be split into smaller batches at the application level.
204
+
205
+ - **Connection lifetime**
206
+ - Individual connections are limited to about 60 minutes.
207
+ - The default `maxLifetimeSeconds: 3300` ensures connections are recycled before hitting this limit.
208
+
209
+ ## Features
210
+
211
+ ### Storage Features
212
+
213
+ - Thread and message storage with JSON support
214
+ - Atomic transactions for data consistency
215
+ - Efficient batch operations
216
+ - Rich metadata support
217
+ - Timestamp tracking
218
+
219
+ ## Storage Methods
220
+
221
+ ### Thread Operations
222
+
223
+ - `saveThread({ thread })`: Create or update a thread
224
+ - `getThreadById({ threadId })`: Get a thread by ID
225
+ - `updateThread({ id, title, metadata })`: Update thread title and/or metadata
226
+ - `deleteThread({ threadId })`: Delete a thread and its messages
227
+ - `listThreadsByResourceId({ resourceId, offset, limit, orderBy? })`: List paginated threads for a resource
228
+
229
+ ### Message Operations
230
+
231
+ - `saveMessages({ messages })`: Save multiple messages in a transaction
232
+ - `listMessages({ threadId, resourceId?, perPage?, page?, orderBy?, filter? })`: Get messages for a thread with pagination
233
+ - `listMessagesById({ messageIds })`: Get specific messages by their IDs
234
+ - `updateMessages({ messages })`: Update existing messages
235
+ - `deleteMessages(messageIds)`: Delete specific messages
236
+
237
+ ### Resource Operations
238
+
239
+ - `getResourceById({ resourceId })`: Get a resource by ID
240
+ - `saveResource({ resource })`: Create or save a resource
241
+ - `updateResource({ resourceId, workingMemory?, metadata? })`: Update resource working memory and/or metadata
242
+
243
+ ### Workflow Operations
244
+
245
+ - `persistWorkflowSnapshot({ workflowName, runId, snapshot })`: Save workflow state
246
+ - `loadWorkflowSnapshot({ workflowName, runId })`: Load workflow state
247
+ - `listWorkflowRuns({ workflowName, pagination })`: List workflow runs with pagination
248
+ - `getWorkflowRunById({ workflowName, runId })`: Get a specific workflow run
249
+ - `updateWorkflowState({ workflowName, runId, state })`: Update workflow state
250
+ - `updateWorkflowResults({ workflowName, runId, results })`: Update workflow results
251
+
252
+ ### Observability Operations
253
+
254
+ - `createSpan(span)`: Create a single span
255
+ - `batchCreateSpans({ records })`: Create multiple spans
256
+ - `updateSpan({ traceId, spanId, updates })`: Update a span
257
+ - `batchUpdateSpans({ records })`: Update multiple spans
258
+ - `getTrace(traceId)`: Get a trace by ID
259
+ - `getTracesPaginated({ ...filters, pagination })`: Get paginated traces with filtering
260
+ - `batchDeleteTraces({ traceIds })`: Delete multiple traces
261
+
262
+ ### Evaluation/Scoring Operations
263
+
264
+ - `getScoreById({ id })`: Get a score by ID
265
+ - `saveScore(score)`: Save an evaluation score
266
+ - `listScoresByScorerId({ scorerId, pagination })`: List scores by scorer with pagination
267
+ - `listScoresByRunId({ runId, pagination })`: List scores by run with pagination
268
+ - `listScoresByEntityId({ entityId, entityType, pagination })`: List scores by entity with pagination
269
+ - `listScoresBySpan({ traceId, spanId, pagination })`: List scores by span with pagination
270
+
271
+ ## Index Management
272
+
273
+ The store creates performance indexes during initialization for common query patterns:
274
+
275
+ - `mastra_threads_resourceid_createdat_idx`: (resourceId, createdAt)
276
+ - `mastra_messages_thread_id_createdat_idx`: (thread_id, createdAt)
277
+ - `mastra_ai_spans_traceid_startedat_idx`: (traceId, startedAt)
278
+ - `mastra_ai_spans_parentspanid_startedat_idx`: (parentSpanId, startedAt)
279
+ - `mastra_ai_spans_name_idx`: (name)
280
+ - `mastra_ai_spans_spantype_startedat_idx`: (spanType, startedAt)
281
+ - `mastra_scores_trace_id_span_id_created_at_idx`: (traceId, spanId, createdAt)
282
+
283
+ Notes:
284
+
285
+ - Aurora DSQL creates these indexes asynchronously using `CREATE INDEX ASYNC`.
286
+ - Because index creation is asynchronous, new indexes may not be immediately available after `init()`. The store will continue to function without them, but queries may be slower until index creation completes.
287
+
288
+ ### Custom Indexes
289
+
290
+ Create additional indexes to optimize specific query patterns:
291
+
292
+ ```typescript
293
+ await store.createIndex({
294
+ name: 'idx_threads_resource',
295
+ table: 'mastra_threads',
296
+ columns: ['resourceId'],
297
+ });
298
+
299
+ await store.createIndex({
300
+ name: 'idx_messages_composite',
301
+ table: 'mastra_messages',
302
+ columns: ['thread_id', 'createdAt'],
303
+ });
304
+ ```
305
+
306
+ Under the hood:
307
+
308
+ - `createIndex` uses `CREATE INDEX ASYNC`.
309
+ - Aurora DSQL doesn’t allow `ASC`/`DESC` in `CREATE INDEX ASYNC`, so `columns` should be plain column names.
310
+
311
+ ### Managing Indexes
312
+
313
+ ```typescript
314
+ // List all indexes
315
+ const allIndexes = await store.listIndexes();
316
+
317
+ // List indexes for a specific table
318
+ const threadIndexes = await store.listIndexes('mastra_threads');
319
+
320
+ // Get detailed statistics for an index
321
+ const stats = await store.describeIndex('idx_threads_resource');
322
+ console.log(stats);
323
+ // {
324
+ // name: 'idx_threads_resource',
325
+ // table: 'mastra_threads',
326
+ // columns: ['resourceId'],
327
+ // unique: false,
328
+ // size: '128 KB',
329
+ // definition: 'CREATE INDEX idx_threads_resource...',
330
+ // method: 'btree',
331
+ // scans: 1542,
332
+ // tuples_read: 45230,
333
+ // tuples_fetched: 12050
334
+ // }
335
+
336
+ // Drop an index
337
+ await store.dropIndex('idx_threads_status');
338
+ ```
339
+
340
+ ### Index Options
341
+
342
+ - `name` (required): Index name
343
+ - `table` (required): Table name
344
+ - `columns` (required): Array of column names (ASC/DESC automatically stripped for Aurora DSQL)
345
+ - `unique`: Create unique index (default: false)
346
+ - `concurrent`: Ignored in Aurora DSQL (indexes are always async)
347
+ - `where`: Partial index condition
348
+ - `method`: Ignored in Aurora DSQL (only btree supported)
349
+
350
+ ## Related Links
351
+
352
+ - [Aurora DSQL Documentation](https://docs.aws.amazon.com/aurora-dsql/)
353
+ - [SQL Reference](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-aurora-dsql-sql.html)
354
+ - [Supported SQL Features](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-postgresql-compatibility-supported-sql-features.html)
355
+ - [Unsupported PostgreSQL Features](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-postgresql-compatibility-unsupported-features.html)
356
+ - [Supported Data Types](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-postgresql-compatibility-supported-data-types.html)
357
+ - [Asynchronous Indexes](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-create-index-async.html)
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@mastra/dsql",
3
+ "version": "0.0.0",
4
+ "description": "Amazon Aurora DSQL storage provider for Mastra",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.cjs"
17
+ }
18
+ },
19
+ "./package.json": "./package.json"
20
+ },
21
+ "scripts": {
22
+ "build": "tsup --silent --config tsup.config.ts",
23
+ "build:watch": "pnpm build --watch",
24
+ "test": "vitest run",
25
+ "test:perf": "NODE_OPTIONS='--max-old-space-size=16384' vitest run -c vitest.perf.config.ts",
26
+ "test:watch": "vitest watch",
27
+ "lint": "eslint ."
28
+ },
29
+ "license": "Apache-2.0",
30
+ "dependencies": {
31
+ "@aws-sdk/types": "^3.731.0",
32
+ "@aws/aurora-dsql-node-postgres-connector": "^0.1.1",
33
+ "pg": "^8.14.0"
34
+ },
35
+ "devDependencies": {
36
+ "@internal/lint": "workspace:*",
37
+ "@internal/storage-test-utils": "workspace:*",
38
+ "@internal/types-builder": "workspace:*",
39
+ "@mastra/core": "workspace:*",
40
+ "@microsoft/api-extractor": "^7.52.8",
41
+ "@types/node": "22.13.17",
42
+ "@types/pg": "^8.11.14",
43
+ "@vitest/coverage-v8": "catalog:",
44
+ "@vitest/ui": "catalog:",
45
+ "eslint": "^9.37.0",
46
+ "tsup": "^8.5.0",
47
+ "typescript": "^5.8.3",
48
+ "vitest": "catalog:"
49
+ },
50
+ "peerDependencies": {
51
+ "@mastra/core": ">=1.0.0-0 <2.0.0-0"
52
+ },
53
+ "files": [
54
+ "dist",
55
+ "CHANGELOG.md"
56
+ ],
57
+ "homepage": "https://mastra.ai",
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "git+https://github.com/mastra-ai/mastra.git",
61
+ "directory": "stores/dsql"
62
+ },
63
+ "bugs": {
64
+ "url": "https://github.com/mastra-ai/mastra/issues"
65
+ },
66
+ "engines": {
67
+ "node": ">=22.13.0"
68
+ }
69
+ }