Composite storage
MastraCompositeStore can compose storage domains from different providers. Use it when you need different databases for different purposes. For example, use LibSQL for memory and PostgreSQL for workflows.
InstallationInstallation的直接链接
MastraCompositeStore is included in @mastra/core:
- npm
- pnpm
- Yarn
- Bun
npm install @mastra/core@latest
pnpm add @mastra/core@latest
yarn add @mastra/core@latest
bun add @mastra/core@latest
You'll also need to install the storage providers you want to compose:
- npm
- pnpm
- Yarn
- Bun
npm install @mastra/pg@latest @mastra/libsql@latest @mastra/mongodb@latest
pnpm add @mastra/pg@latest @mastra/libsql@latest @mastra/mongodb@latest
yarn add @mastra/pg@latest @mastra/libsql@latest @mastra/mongodb@latest
bun add @mastra/pg@latest @mastra/libsql@latest @mastra/mongodb@latest
Storage domainsStorage domains的直接链接
Mastra organizes storage into domains, each handling a specific type of data. Each domain can be backed by a different storage adapter, and domain classes are exported from each storage package.
| Domain | Description |
|---|---|
memory | Conversation persistence for agents. Stores threads (conversation sessions), messages, resources (user identities), and working memory (persistent context across conversations). |
workflows | Workflow execution state. When workflows suspend for human input, external events, or scheduled resumption, their state is persisted here to enable resumption after server restarts. |
scores | Evaluation results from Mastra's evals system. Scores and metrics are persisted here for analysis and comparison over time. |
observability | Telemetry data including traces and spans. Agent interactions, tool calls, and LLM requests generate spans collected into traces for debugging and performance analysis. |
agents | Agent configurations for stored agents. Enables agents to be defined and updated at runtime without code deployments. |
datasets | Evaluation datasets used for experiment runs. Stores dataset definitions, schemas, and versioned items. |
experiments | Experiment runs and per-item experiment results linked to datasets and targets. |
MastraCompositeStore accepts all of the domain keys above, but storage adapter support varies by package. You can mix adapters per domain, but only for domains implemented and exported by those adapters. For example, memory: new MemoryLibSQL(...) and workflows: new WorkflowsPG(...) is valid because both packages export those domain classes.
UsageUsage的直接链接
Basic compositionBasic composition的直接链接
Import domain classes directly from each store package and compose them:
import { MastraCompositeStore } from '@mastra/core/storage'
import { WorkflowsPG, ScoresPG } from '@mastra/pg'
import { MemoryLibSQL } from '@mastra/libsql'
import { Mastra } from '@mastra/core'
export const mastra = new Mastra({
storage: new MastraCompositeStore({
id: 'composite',
domains: {
memory: new MemoryLibSQL({ url: 'file:./local.db' }),
workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }),
scores: new ScoresPG({ connectionString: process.env.DATABASE_URL }),
},
}),
})
With a default storageWith a default storage的直接链接
Use default to specify a fallback storage, then override specific domains:
import { MastraCompositeStore } from '@mastra/core/storage'
import { PostgresStore } from '@mastra/pg'
import { MemoryLibSQL } from '@mastra/libsql'
import { Mastra } from '@mastra/core'
const pgStore = new PostgresStore({
id: 'pg',
connectionString: process.env.DATABASE_URL,
})
export const mastra = new Mastra({
storage: new MastraCompositeStore({
id: 'composite',
default: pgStore,
domains: {
memory: new MemoryLibSQL({ url: 'file:./local.db' }),
},
}),
})
Mixed backendsMixed backends的直接链接
Use domain classes from each storage package to route different domains to different backends. The following example stores memory and workflow state in MongoDB, then routes observability to ClickHouse:
import { Mastra } from '@mastra/core'
import { MastraCompositeStore } from '@mastra/core/storage'
import { ObservabilityStorageClickhouse } from '@mastra/clickhouse'
import { MemoryStorageMongoDB, WorkflowsStorageMongoDB } from '@mastra/mongodb'
export const mastra = new Mastra({
storage: new MastraCompositeStore({
id: 'composite',
domains: {
memory: new MemoryStorageMongoDB({
uri: process.env.MONGODB_URI,
dbName: 'mastra_memory',
}),
workflows: new WorkflowsStorageMongoDB({
uri: process.env.MONGODB_URI,
dbName: 'mastra_workflows',
}),
observability: new ObservabilityStorageClickhouse({
url: process.env.CLICKHOUSE_URL,
username: process.env.CLICKHOUSE_USERNAME,
password: process.env.CLICKHOUSE_PASSWORD,
}),
},
}),
})
Disabling a domainDisabling a domain的直接链接
Set a domain to false to disable it. A disabled domain doesn't fall back to default, so data for that domain isn't persisted:
import { MastraCompositeStore } from '@mastra/core/storage'
import { PostgresStore } from '@mastra/pg'
import { Mastra } from '@mastra/core'
const pgStore = new PostgresStore({
id: 'pg',
connectionString: process.env.DATABASE_URL,
})
export const mastra = new Mastra({
storage: new MastraCompositeStore({
id: 'composite',
default: pgStore,
domains: {
// don't persist traces and spans
observability: false,
},
}),
})
OptionsOptions的直接链接
id:
default?:
domains will use this storage's domains as fallbacks.editor?:
disableInit?:
domains?:
editor and default storage. Set a domain to false to disable it entirely; a disabled domain does not fall back to editor or default.memory?:
workflows?:
scores?:
observability?:
agents?:
datasets?:
experiments?:
workflowDefinitions?:
promptBlocks?:
scorerDefinitions?:
mcpClients?:
mcpServers?:
workspaces?:
skills?:
favorites?:
blobs?:
backgroundTasks?:
schedules?:
channels?:
harness?:
toolProviderConnections?:
notifications?:
threadState?:
knowledge?:
InitializationInitialization的直接链接
MastraCompositeStore initializes each configured domain independently. When passed to the Mastra class, init() is called automatically:
import { MastraCompositeStore } from '@mastra/core/storage'
import { MemoryPG, WorkflowsPG, ScoresPG } from '@mastra/pg'
import { Mastra } from '@mastra/core'
const storage = new MastraCompositeStore({
id: 'composite',
domains: {
memory: new MemoryPG({ connectionString: process.env.DATABASE_URL }),
workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }),
scores: new ScoresPG({ connectionString: process.env.DATABASE_URL }),
},
})
export const mastra = new Mastra({
storage, // init() called automatically
})
If using storage directly, call init() explicitly:
import { MastraCompositeStore } from '@mastra/core/storage'
import { MemoryPG } from '@mastra/pg'
const storage = new MastraCompositeStore({
id: 'composite',
domains: {
memory: new MemoryPG({ connectionString: process.env.DATABASE_URL }),
},
})
await storage.init()
// Access domain-specific stores via getStore()
const memoryStore = await storage.getStore('memory')
const thread = await memoryStore?.getThreadById({ threadId: '...' })
Closing connectionsClosing connections的直接链接
close() releases connections for the stores used by a composite, including the default and editor stores and any domain with its own client. Each store closes once even if it backs several domains. When the composite is passed to the Mastra class, shutdown() calls close():
import { MastraCompositeStore } from '@mastra/core/storage'
import { PostgresStore } from '@mastra/pg'
import { Mastra } from '@mastra/core'
const pgStore = new PostgresStore({
id: 'pg-storage',
connectionString: process.env.DATABASE_URL,
})
export const mastra = new Mastra({
storage: new MastraCompositeStore({ id: 'composite', default: pgStore }),
})
process.on('SIGTERM', async () => {
// Releases the Postgres pool, so the process can exit
await mastra.shutdown()
})
A store you construct only to supply a domain isn't reachable through the composite. Keep a reference to it and close it yourself:
import { MastraCompositeStore } from '@mastra/core/storage'
import { ClickhouseStore } from '@mastra/clickhouse'
import { PostgresStore } from '@mastra/pg'
import { Mastra } from '@mastra/core'
const pgStore = new PostgresStore({
id: 'pg-storage',
connectionString: process.env.DATABASE_URL,
})
const clickhouseStore = new ClickhouseStore({
id: 'clickhouse-storage',
url: process.env.CLICKHOUSE_URL,
username: process.env.CLICKHOUSE_USERNAME,
password: process.env.CLICKHOUSE_PASSWORD,
})
export const mastra = new Mastra({
storage: new MastraCompositeStore({
id: 'composite',
default: pgStore,
domains: { observability: clickhouseStore.stores?.observability },
}),
})
process.on('SIGTERM', async () => {
await mastra.shutdown()
await clickhouseStore.close()
})
Use casesUse cases的直接链接
Separate databases for different workloadsSeparate databases for different workloads的直接链接
Use a local database for development while keeping production data in a managed service:
import { MastraCompositeStore } from '@mastra/core/storage'
import { MemoryPG, WorkflowsPG, ScoresPG } from '@mastra/pg'
import { MemoryLibSQL } from '@mastra/libsql'
const storage = new MastraCompositeStore({
id: 'composite',
domains: {
// Use local SQLite for development, PostgreSQL for production
memory:
process.env.NODE_ENV === 'development'
? new MemoryLibSQL({ url: 'file:./dev.db' })
: new MemoryPG({ connectionString: process.env.DATABASE_URL }),
workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }),
scores: new ScoresPG({ connectionString: process.env.DATABASE_URL }),
},
})
Specialized storage for observabilitySpecialized storage for observability的直接链接
Observability data can quickly overwhelm general-purpose databases in production. A single agent interaction can generate hundreds of spans, and high-traffic applications can produce thousands of traces per day.
ClickHouse is recommended for production observability because it's optimized for high-volume, write-heavy analytics workloads. Use composite storage to route observability to ClickHouse while keeping other data in your primary database:
import { MastraCompositeStore } from '@mastra/core/storage'
import { MemoryPG, WorkflowsPG, ScoresPG } from '@mastra/pg'
import { ObservabilityStorageClickhouseVNext } from '@mastra/clickhouse'
const storage = new MastraCompositeStore({
id: 'composite',
domains: {
memory: new MemoryPG({ connectionString: process.env.DATABASE_URL }),
workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }),
scores: new ScoresPG({ connectionString: process.env.DATABASE_URL }),
observability: new ObservabilityStorageClickhouseVNext({
url: process.env.CLICKHOUSE_URL,
username: process.env.CLICKHOUSE_USERNAME,
password: process.env.CLICKHOUSE_PASSWORD,
}),
},
})
ObservabilityStorageClickhouseVNext is the current observability domain implementation. The legacy ObservabilityStorageClickhouse class is also exported and remains supported for projects that haven't migrated. See the ClickHouse storage reference for details.
Replicated ClickHouse for multi-replica clustersReplicated ClickHouse for multi-replica clusters的直接链接
For self-managed ClickHouse clusters with multiple replicas, set replication so Mastra emits ReplicatedMergeTree engines and applies ON CLUSTER to its DDL:
import { MastraCompositeStore } from '@mastra/core/storage'
import { MemoryPG, WorkflowsPG, ScoresPG } from '@mastra/pg'
import { ObservabilityStorageClickhouseVNext } from '@mastra/clickhouse'
const storage = new MastraCompositeStore({
id: 'composite',
domains: {
memory: new MemoryPG({ connectionString: process.env.DATABASE_URL }),
workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }),
scores: new ScoresPG({ connectionString: process.env.DATABASE_URL }),
observability: new ObservabilityStorageClickhouseVNext({
url: process.env.CLICKHOUSE_URL,
username: process.env.CLICKHOUSE_USERNAME,
password: process.env.CLICKHOUSE_PASSWORD,
replication: {
cluster: 'production_cluster',
// Optional (defaults shown):
// zookeeperPath: '/clickhouse/tables/{shard}/{database}/{table}',
// replicaName: '{replica}',
},
}),
},
})
Don't set replication on ClickHouse Cloud. Cloud rewrites MergeTree to SharedMergeTree server-side. See the ClickHouse storage reference for the full config shape and operator notes.
This approach is also required when using storage providers that don't support observability (like Convex, DynamoDB, or Cloudflare). See the MastraStorageExporter documentation for the full list of supported providers.