跳至主要內容

儲存空間

儲存空間 API 已標準化,所有方法都使用一致的分頁與命名模式。

資料庫遷移
「資料庫遷移」的直接連結

請透過一般遷移流程執行這些 SQL 遷移(例如 Prisma Migrate、Drizzle Kit,或 DBA 審查流程)。

Scorer 資料表欄位重新命名
「Scorer 資料表欄位重新命名」的直接連結

mastra_scorers 中的 runtimeContext 欄位已重新命名為 requestContext

誰需要進行此遷移

只有在使用 @mastra/pg@mastra/libsql 搭配 Evals/評分,而且 runtimeContext 欄位已有現有資料時才需要。

未遷移時會造成什麼問題

現有分數記錄的請求內容資料將無法存取。

部署 v1(初始化時會新增 requestContext 欄位)後,請複製資料並刪除舊欄位:

UPDATE mastra_scorers SET "requestContext" = "runtimeContext" WHERE "runtimeContext" IS NOT NULL;
ALTER TABLE mastra_scorers DROP COLUMN "runtimeContext";

重複 span 遷移
「重複 span 遷移」的直接連結

如果從舊版 Mastra 升級,mastra_spans 資料表中可能有重複的 (traceId, spanId) 項目。V1 會為這些欄位新增唯一條件約束,以確保資料完整性,但若存在重複項目,就無法新增此條件約束。

誰需要進行此遷移

只有在保有 Mastra v1 之前版本的現有 span 資料,而且遇到重複鍵違規或條件約束建立失敗錯誤時才需要。

未遷移時會造成什麼問題

儲存空間初始化可能會在嘗試新增唯一條件約束時失敗,或可能看到「duplicate key value violates unique constraint」等錯誤。

執行遷移命令,自動刪除重複 span 並新增條件約束:

npx mastra migrate

CLI 會封裝專案並連線至已設定的儲存空間,再執行遷移。移除重複項目時,會保留最完整的記錄(依 endTime 與屬性判斷)。

選項 2:手動執行 SQL(PostgreSQL)
「選項 2:手動執行 SQL(PostgreSQL)」的直接連結

如果偏好手動執行遷移:

-- Remove duplicates, keeping the most complete record
DELETE FROM mastra_spans a USING mastra_spans b
WHERE a.ctid < b.ctid
AND a."traceId" = b."traceId"
AND a."spanId" = b."spanId";

-- Add the unique constraint
ALTER TABLE mastra_spans ADD CONSTRAINT mastra_spans_trace_span_unique UNIQUE ("traceId", "spanId");

選項 3:手動遷移(其他資料庫)
「選項 3:手動遷移(其他資料庫)」的直接連結

對於 ClickHouse、LibSQL、MongoDB 或 MSSQL,請使用程式化 API:

const storage = mastra.getStorage()
const observabilityStore = await storage.getStore('observability')

// Check if migration is needed
const status = await observabilityStore?.checkSpansMigrationStatus()
console.log(status)

// Run the migration
const result = await observabilityStore?.migrateSpans()
console.log(result)

JSON 欄位(TEXT → JSONB)
「JSON 欄位(TEXT → JSONB)」的直接連結

僅限 PostgreSQL。mastra_threadsmetadata 欄位與 mastra_workflow_snapshotsnapshot 欄位已從 TEXT 變更為 JSONB。

建議

遷移至 JSONB 後,即可使用 PostgreSQL 原生 JSON 運算子與 GIN 索引,提升 JSON 欄位的查詢效能。

ALTER TABLE mastra_threads
ALTER COLUMN metadata TYPE jsonb
USING metadata::jsonb;

ALTER TABLE mastra_workflow_snapshot
ALTER COLUMN snapshot TYPE jsonb
USING snapshot::jsonb;

已新增
「已新增」的直接連結

MastraCompositeStore 中組合儲存空間
「storage-composition-in-mastracompositestore」的直接連結

MastraCompositeStore 現在可以組合來自不同轉接器的儲存空間領域。不同用途需要使用不同資料庫時可使用它,例如 Memory 與 Workflow 使用 PostgreSQL,但可觀測性使用專用資料庫。

import { MastraCompositeStore } from '@mastra/core/storage'
import { MemoryPG, WorkflowsPG, ScoresPG } from '@mastra/pg'
import { MemoryLibSQL } from '@mastra/libsql'
import { Mastra } from '@mastra/core'

// Compose domains from different stores
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 }),
},
}),
})

詳情請參閱儲存空間組合參考文件

已變更
「已變更」的直接連結

MastraStorage 重新命名為 MastraCompositeStore
「mastrastorage-renamed-to-mastracompositestore」的直接連結

MastraStorage 類別已重新命名為 MastraCompositeStore,更準確反映它作為複合儲存空間實作的角色:將不同領域路由至不同的底層儲存空間。這可避免與一般「Mastra Storage」概念(Mastra 執行個體上的 storage 屬性)混淆。

為維持回溯相容性,舊的 MastraStorage 名稱仍可作為已棄用的別名使用,但將在未來版本移除。

遷移時,請更新匯入與執行個體化方式:

- import { MastraStorage } from "@mastra/core/storage";
+ import { MastraCompositeStore } from "@mastra/core/storage";
import { MemoryLibSQL } from "@mastra/libsql";
import { WorkflowsPG } from "@mastra/pg";

export const mastra = new Mastra({
- storage: new MastraStorage({
+ storage: new MastraCompositeStore({
id: "composite",
domains: {
memory: new MemoryLibSQL({ url: "file:./memory.db" }),
workflows: new WorkflowsPG({ connectionString: process.env.DATABASE_URL }),
},
}),
});
備註

如果直接使用單一儲存空間實作(例如 PostgresStoreLibSQLStore),請保留現有設定。這只會影響明確使用 MastraStorage 建立複合儲存空間的程式碼。

儲存空間執行個體必須使用 id 屬性
「required-id-property-for-storage-instances」的直接連結

儲存空間執行個體現在必須使用 id 屬性。此唯一識別碼用來在 Mastra 中追蹤及管理儲存空間執行個體。應為應用程式中的每個儲存空間執行個體設定具描述性且不重複的 id 字串。

遷移時,請在儲存空間建構函式中新增 id 欄位。

const storage = new PostgresStore({
+ id: 'main-postgres-store',
connectionString: process.env.POSTGRES_CONNECTION_STRING,
schemaName: 'public',
});

const upstashStore = new UpstashStore({
+ id: 'upstash-cache-store',
url: process.env.UPSTASH_REDIS_REST_URL,
token: process.env.UPSTASH_REDIS_REST_TOKEN,
});

分頁從 offset/limit 改為 page/perPage
「pagination-from-offsetlimit-to-pageperpage」的直接連結

所有分頁 API 現在都使用 pageperPage,而非 offsetlimit,以符合以頁面為基礎的網頁分頁方式。

遷移時,請將所有分頁參數從 offset/limit 更新為 page/perPage。請注意,page 從 0 開始。

memoryStore.listMessages({
threadId: 'thread-123',
- offset: 0,
- limit: 20,
+ page: 0,
+ perPage: 20,
});

getMessagesPaginated 改為 listMessages
「getmessagespaginated-to-listmessages」的直接連結

getMessagesPaginated() 方法已由 listMessages() 取代。新方法支援 perPage: false,可在不分頁的情況下擷取所有記錄。這項變更符合 list* 命名慣例,並增加擷取所有記錄的彈性。

遷移時,請重新命名方法並更新分頁參數。現在可以使用 perPage: false 擷取所有記錄。

+ const memoryStore = await storage.getStore('memory');
+
// Paginated
- const result = await storage.getMessagesPaginated({
+ const result = await memoryStore?.listMessages({
threadId: 'thread-123',
- offset: 0,
- limit: 20,
+ page: 0,
+ perPage: 20,
});

// Fetch all records (no pagination limit)
+ const allMessages = await memoryStore?.listMessages({
+ threadId: 'thread-123',
+ page: 0,
+ perPage: false,
+ });
Codemod

你可以使用 Mastra 的 codemod CLI 自動更新程式碼:

npx @mastra/codemod@latest v1/storage-get-messages-paginated .

透過 getStore() 存取特定領域的儲存空間
「domain-specific-storage-access-via-getstore」的直接連結

儲存空間作業現在會透過特定領域的儲存空間存取,而非直接在儲存空間執行個體上存取。

領域包括:

  • memory - 討論串、訊息與資源
  • workflows - Workflow 快照
  • scores - 評估分數
  • observability - Trace 與 span
  • agents - 已儲存的 Agent 資料

遷移時,請使用領域名稱呼叫 getStore(),再呼叫所傳回儲存空間上的方法。

const storage = mastra.getStorage();

// Memory operations (threads, messages, resources)
- const thread = await storage.getThread({ threadId: '123' });
- await storage.saveThread({ thread });
+ const memoryStore = await storage.getStore('memory');
+ const thread = await memoryStore?.getThreadById({ threadId: '123' });
+ await memoryStore?.saveThread({ thread });

// Workflow operations (snapshots)
- const snapshot = await storage.loadWorkflowSnapshot({ runId, workflowName });
- await storage.persistWorkflowSnapshot({ runId, workflowName, snapshot });
+ const workflowStore = await storage.getStore('workflows');
+ const snapshot = await workflowStore?.loadWorkflowSnapshot({ runId, workflowName });
+ await workflowStore?.persistWorkflowSnapshot({ runId, workflowName, snapshot });

// Observability operations (traces, spans)
- const traces = await storage.listTraces({ page: 0, perPage: 20 });
+ const observabilityStore = await storage.getStore('observability');
+ const traces = await observabilityStore?.listTraces({ page: 0, perPage: 20 });

// Score operations (evaluations)
- const scores = await storage.listScoresByScorerId({ scorerId: 'helpfulness' });
+ const scoresStore = await storage.getStore('scores');
+ const scores = await scoresStore?.listScoresByScorerId({ scorerId: 'helpfulness' });

getThreadsByResourceId 改為 listThreads
「getthreadsbyresourceid-to-listthreads」的直接連結

getThreadsByResourceId() 方法已由 listThreads() 取代。新方法新增分頁支援,並可依 resourceIdmetadata 或兩者篩選。

important

舊的 getThreadsByResourceId() 會傳回所有相符討論串,且不分頁。新的 listThreads() 必須提供分頁參數。若要保留擷取所有討論串的舊有行為,請使用 perPage: false

遷移時,請使用 Memory 儲存空間與新的 listThreads() 方法,並提供分頁與選填的篩選物件。

- const threads = await storage.getThreadsByResourceId({
- resourceId: 'res-123',
- });
+ const memoryStore = await storage.getStore('memory');
+
+ // Paginated (recommended for large datasets)
+ const result = await memoryStore?.listThreads({
+ filter: { resourceId: 'res-123' },
+ page: 0,
+ perPage: 20,
+ });
+ const threads = result?.threads;
+
+ // Or fetch all threads like before (use perPage: false)
+ const allResult = await memoryStore?.listThreads({
+ filter: { resourceId: 'res-123' },
+ perPage: false,
+ });
+ const allThreads = allResult?.threads;

新方法也支援:

  • 列出所有討論串(省略篩選條件)
  • 僅依中繼資料篩選
  • 結合 resourceId 與中繼資料篩選條件
// List all threads
await memoryStore?.listThreads({ page: 0, perPage: 20 })

// Filter by metadata only
await memoryStore?.listThreads({
filter: { metadata: { status: 'active' } },
page: 0,
perPage: 20,
})

// Combined filter
await memoryStore?.listThreads({
filter: {
resourceId: 'user-123',
metadata: { category: 'support' },
},
page: 0,
perPage: 20,
})
Codemod

你可以使用 Mastra 的 codemod CLI 自動更新程式碼:

npx @mastra/codemod@latest v1/storage-list-threads-by-resource-to-list-threads .

getWorkflowRuns 改為 listWorkflowRuns
「getworkflowruns-to-listworkflowruns」的直接連結

getWorkflowRuns() 方法已重新命名為 listWorkflowRuns()。這項變更符合 list* 方法傳回集合的慣例。

遷移時,請使用 Workflow 儲存空間、重新命名方法呼叫,並更新分頁參數。

- const runs = await storage.getWorkflowRuns({
+ const workflowStore = await storage.getStore('workflows');
+ const runs = await workflowStore?.listWorkflowRuns({
fromDate,
toDate,
+ page: 0,
+ perPage: 20,
});
Codemod

你可以使用 Mastra 的 codemod CLI 自動更新程式碼:

npx @mastra/codemod@latest v1/storage-list-workflow-runs .

getMessagesById 改為 listMessagesById
「getmessagesbyid-to-listmessagesbyid」的直接連結

getMessagesById() 方法已重新命名為 listMessagesById()。這項變更符合 list* 方法傳回集合的慣例。

遷移時,請使用 Memory 儲存空間並重新命名方法呼叫。

+ const memoryStore = await storage.getStore('memory');
- const result = await storage.getMessagesById({
+ const result = await memoryStore?.listMessagesById({
messageIds: ['msg-1', 'msg-2'],
});
Codemod

你可以使用 Mastra 的 codemod CLI 自動更新程式碼:

npx @mastra/codemod@latest v1/storage-list-messages-by-id .

儲存空間 getMessagessaveMessages 簽章
「storage-getmessages-and-savemessages-signatures」的直接連結

getMessages()saveMessages() 方法的簽章與傳回型別已變更。格式多載已移除,方法現在一律使用 MastraDBMessage。這項變更移除格式變體,簡化 API。

遷移時,請使用 Memory 儲存空間、移除格式參數,並更新程式碼以使用一致的傳回型別。

+ const memoryStore = await storage.getStore('memory');
+
// Always returns { messages: MastraDBMessage[] }
- const v1Messages = await storage.getMessages({ threadId, format: 'v1' });
- const v2Messages = await storage.getMessages({ threadId, format: 'v2' });
+ const result = await memoryStore?.getMessages({ threadId });
+ const messages = result?.messages; // MastraDBMessage[]

// SaveMessages always uses MastraDBMessage
- await storage.saveMessages({ messages: v1Messages, format: 'v1' });
- await storage.saveMessages({ messages: v2Messages, format: 'v2' });
+ const saveResult = await memoryStore?.saveMessages({ messages: mastraDBMessages });
+ const saved = saveResult?.messages; // MastraDBMessage[]

向量儲存 API 從位置引數改為具名引數
「向量儲存 API 從位置引數改為具名引數」的直接連結

所有向量儲存方法現在都使用引數物件,而非位置引數。這讓每個值的用途在呼叫位置清楚可見,也能讓方法簽章在不依賴引數順序的情況下演進。

遷移時,請更新所有向量儲存方法呼叫,改用引數物件。

- await vectorDB.createIndex(indexName, 3, 'cosine');
+ await vectorDB.createIndex({
+ indexName: indexName,
+ dimension: 3,
+ metric: 'cosine',
+ });

- await vectorDB.upsert(indexName, [[1, 2, 3]], [{ test: 'data' }]);
+ await vectorDB.upsert({
+ indexName: indexName,
+ vectors: [[1, 2, 3]],
+ metadata: [{ test: 'data' }],
+ });

- await vectorDB.query(indexName, [1, 2, 3], 5);
+ await vectorDB.query({
+ indexName: indexName,
+ queryVector: [1, 2, 3],
+ topK: 5,
+ });

向量儲存方法重新命名
「向量儲存方法重新命名」的直接連結

updateIndexByIddeleteIndexById 方法已分別重新命名為 updateVectordeleteVector。新名稱指出這些方法會對向量執行作業。

遷移時,請重新命名方法並傳入引數物件。

- await vectorDB.updateIndexById(indexName, id, update);
- await vectorDB.deleteIndexById(indexName, id);
+ await vectorDB.updateVector({ indexName, id, update });
+ await vectorDB.deleteVector({ indexName, id });

PGVector 建構函式從連線字串改為物件
「PGVector 建構函式從連線字串改為物件」的直接連結

PGVector 建構函式現在必須使用物件參數,而非連線字串。這項變更讓所有儲存空間轉接器的 API 更一致。

遷移時,請將連線字串作為物件屬性傳入。

- const pgVector = new PgVector(process.env.POSTGRES_CONNECTION_STRING!);
+ const pgVector = new PgVector({
+ connectionString: process.env.POSTGRES_CONNECTION_STRING,
+ });
Codemod

你可以使用 Mastra 的 codemod CLI 自動更新程式碼:

npx @mastra/codemod@latest v1/vector-pg-constructor .

PGVector 的 defineIndex 改為 buildIndex
「pgvector-defineindex-to-buildindex」的直接連結

defineIndex() 方法已移除,請改用 buildIndex();新名稱指出此方法會建置索引。

遷移時,請重新命名方法並傳入引數物件。

- await vectorDB.defineIndex(indexName, 'cosine', { type: 'flat' });
+ await vectorDB.buildIndex({
+ indexName: indexName,
+ metric: 'cosine',
+ indexConfig: { type: 'flat' },
+ });

PostgresStoreschema 改為 schemaName
「postgresstore-schema-to-schemaname」的直接連結

PostgresStore 建構函式中的 schema 參數已重新命名為 schemaName。新名稱指出此值是資料庫結構描述的名稱。

遷移時,請重新命名參數。

const pgStore = new PostgresStore({
connectionString: process.env.POSTGRES_CONNECTION_STRING,
- schema: customSchema,
+ schemaName: customSchema,
});
Codemod

你可以使用 Mastra 的 codemod CLI 自動更新程式碼:

npx @mastra/codemod@latest v1/storage-postgres-schema-name .

分數儲存方法改為 listScoresBy* 模式
「score-storage-methods-to-listscoresby-pattern」的直接連結

分數儲存 API 已重新命名,以遵循 listScoresBy* 模式。這項變更與更廣泛的 API 命名慣例保持一致。

遷移時,請將方法名稱從 getScores 更新為 listScoresByScorerId 及相關變體。

- const scores = await storage.getScores({ scorerName: 'helpfulness-scorer' });
+ const scores = await storage.listScoresByScorerId({
+ scorerId: 'helpfulness-scorer',
+ });
+ // Also available: listScoresByRunId, listScoresByEntityId, listScoresBySpan

已移除
「已移除」的直接連結

非分頁儲存空間函式
「非分頁儲存空間函式」的直接連結

非分頁儲存空間函式已移除,請改用分頁版本。所有 list 作業現在都使用分頁,但可以使用 perPage: false 擷取所有記錄。這項變更讓 API 保持一致,並避免意外載入大型資料集。

遷移時,請透過領域儲存空間使用分頁方法。若要擷取所有記錄,請使用 perPage: false

- // Non-paginated direct access
- const messages = await storage.getMessages({ threadId });

+ // Use paginated methods via domain stores
+ const memoryStore = await storage.getStore('memory');
+ const result = await memoryStore?.listMessages({ threadId, page: 0, perPage: 20 });
+ // Or fetch all
+ const allMessages = await memoryStore?.listMessages({
+ threadId,
+ page: 0,
+ perPage: false,
+ });

getTracesgetTracesPaginated
「gettraces-and-gettracespaginated」的直接連結

儲存空間已移除 getTraces()getTracesPaginated() 方法。請透過可觀測性套件存取 Trace,而非核心儲存空間。

遷移時,請改用可觀測性儲存方法。

- const traces = await storage.getTraces({ traceId: 'trace-123' });
- const paginated = await storage.getTracesPaginated({ page: 0, perPage: 20 });

+ // Use observability API for traces
+ import { initObservability } from '@mastra/observability';
+ const observability = initObservability({ config: { ... } });
+ // Access traces through observability API

Evals 測試公用程式
「Evals 測試公用程式」的直接連結

@internal/test-utils 已移除 Evals 領域測試公用程式。這項變更反映舊版 Evals 功能已移除。

遷移時,請直接使用儲存空間 API 進行測試,而非專用的 Evals 測試公用程式。

- import { createEvalsTests } from '@internal/test-utils/domains/evals';
- createEvalsTests({ storage });

+ // Use storage APIs directly for testing

MSSQL 儲存空間中的 TABLE_EVALS
「MSSQL 儲存空間中的 TABLE_EVALS」的直接連結

MSSQL 儲存空間實作已移除 TABLE_EVALS 資料表。這項變更反映舊版 Evals 功能已移除。

如果原本使用 MSSQL 儲存空間搭配 Evals,請遷移至其他儲存空間轉接器,或移除 Evals 功能。