儲存保留
預設情況下,儲存空間會無限增長。保留是一套可選用、按資料存續時間清理的系統:您可以在 retention 設定中為每個資料表宣告 maxAge 政策,然後呼叫 storage.prune(),刪除超過設定存續時間的資料列。任何未設定的內容都會永久保留,因此在您選用此功能前,行為不會有任何變化。
prune() 會刪除資料列。它可限制增長,並能安全地在大型資料表上執行(分批、有界、可恢復、可取消)。它不會回收磁碟空間:在 SQLite/libSQL 上,釋出的頁面會由日後的寫入操作重用,因此檔案會停止增長;至於把磁碟空間交還作業系統(例如執行 VACUUM),則留待底層資料庫及操作人員管理。
保留功能只涵蓋增長資料表:即在正常操作下,因對話記錄、遙測、工作及執行記錄、排程觸發記錄、事件 Feed 等而不斷累積資料列的資料表。由使用者建立的內容及設定(Agent、Skill、Workspace、提示詞區塊、資料集、排程定義、Channel 安裝項目等)會隨使用者意圖而增長,並由使用者明確編輯或刪除,因此不能作為有效的保留鍵。
參考實作包括 libSQL、PostgreSQL 及 MongoDB。其他 Adapter 在實作保留功能前,會永久保留資料列。
使用範例使用範例 的直接連結
在任何 MastraCompositeStore(或擴充它的 Adapter,例如 LibSQLStore)上宣告 retention,然後從您自己的 scheduler 呼叫 prune()。
import { LibSQLStore } from '@mastra/libsql'
const storage = new LibSQLStore({
id: 'mastra-storage',
url: 'file:./mastra.db',
retention: {
memory: {
messages: { maxAge: '30d' },
threads: { maxAge: '90d', batchSize: 500 },
},
observability: {
spans: { maxAge: '7d' },
},
},
})
// Wire this to your own cron/scheduler: Mastra never runs it for you.
const results = await storage.prune()
retention 具備完整型別。鍵必須是真實的網域鍵,而每個資料表鍵亦必須是該網域宣告為符合保留資格的資料表。將物件直接傳入 Store 設定時會進行型別檢查;如果獨立建立物件,請使用 satisfies RetentionConfig,讓未知網域或資料表成為編譯錯誤:
import type { RetentionConfig } from '@mastra/core/storage'
const retention = {
memory: {
messages: { maxAge: '30d' }, // ok
bogus: { maxAge: '30d' }, // Error: not a memory retention table
},
bogusDomain: {}, // Error: not a storage domain
} satisfies RetentionConfig
保留設定保留設定 的直接連結
在 Store 設定上設定 retention 欄位。
retention?:
[domain]?:
memory、observability)。將該網域符合保留資格的資料表鍵對應至其政策。TableRetentionPolicyTableRetentionPolicy 的直接連結
maxAge:
Date.now() - maxAge 的資料列符合刪除資格。數字表示毫秒,也可使用帶單位後綴的字串:ms、s、m、h、d、w(例如 '30d'、'12h')。batchSize?:
符合保留資格的資料表符合保留資格的資料表 的直接連結
每個網域都會宣告其哪些資料表可以按存續時間修剪,以及由哪個時間戳欄位作為比較錨點。錨點的選擇方式,會讓 maxAge 對該類資料表達符合預期的含義。只附加的記錄會使用建立時間,而即時狀態則使用最後活動時間。工作及執行記錄會使用完成時間,因此進行中的工作永遠不會被修剪。
| 網域 | 資料表鍵 | 錨點欄位 | maxAge 衡量項目 |
|---|---|---|---|
memory | threads | createdAt | Thread 存續時間 |
memory | messages | createdAt | 訊息存續時間 |
memory | resources | createdAt | 資源存續時間 |
threadState | threadState | updatedAt | 閒置時間:仍然活躍的 Thread 狀態會保留 |
observability | spans | startedAt | Span 存續時間 |
observability | metrics | timestamp | Metric 事件存續時間(僅限 v-next) |
observability | logs | timestamp | Log 事件存續時間(僅限 v-next) |
observability | scores | timestamp | Score 事件存續時間(僅限 v-next) |
observability | feedback | timestamp | Feedback 事件存續時間(僅限 v-next) |
scores | scorers | createdAt | Score 記錄存續時間 |
workflows | workflowSnapshot | updatedAt | 閒置時間;已暫停或長時間執行的 Workflow 會保留 |
backgroundTasks | backgroundTasks | completedAt | 完成後經過的時間;進行中的任務(NULL)永遠不會被修剪 |
experiments | experiments | completedAt | 完成後經過的時間;執行中的實驗永遠不會被修剪 |
notifications | notifications | createdAt | 通知存續時間 |
harness | sessions | createdAt | Session 記錄存續時間 |
schedules | triggers | actual_fire_at | 觸發記錄存續時間(epoch-ms 欄位) |
- 記憶體的
observational_memory資料表沒有時間戳錨點,因此不能按存續時間修剪,亦不是有效的保留鍵。 - 實驗會以完整單位修剪:過期實驗的結果資料列會連同實驗一併刪除(結果會隨其 parent 串聯刪除),所以執行記錄不會只被刪除一部分。保留功能沒有獨立的
results鍵。 - 對於
schedules,增長資料表是觸發記錄(schedule_triggers,每次觸發一個資料列);排程定義屬於設定,不會被修剪。 - 在 PostgreSQL 上,時間戳錨點會使用可感知時區的鏡像欄位(例如
createdAtZ、completedAtZ)。 - LibSQL 及 PostgreSQL 支援上述所有網域,但 PostgreSQL 尚未實作
harness。MongoDB 則支援除threadState及harness以外的所有網域。 - v-next PostgreSQL 可觀測性網域會將訊號事件儲存在按日分割的資料表(
spans、metrics、logs、scores、feedback)中。對此,prune()會移除整個完全早於截止時間的日分割區(或 TimescaleDB chunk),而非刪除資料列:實際精細度為一日,只有在分割區涵蓋的一整日都超過maxAge後,才會移除該分割區。PruneResult.deleted會回報已移除分割區中的資料列數目。
方法方法 的直接連結
保留保留 的直接連結
prune(options?)pruneoptions 的直接連結
在 retention 中已設定政策的所有網域,刪除超過所設定 maxAge 的資料列。每個觸及的資料表都會傳回一個 PruneResult。如果未設定 retention,則不會執行任何操作,並傳回 []。
prune() 的設計可安全用於包含數百萬資料列的資料表。它會以有界的分批區塊刪除(每批都是獨立交易),因此不會長時間鎖定或令交易記錄膨脹。它永遠不會執行 VACUUM。
傳入 options.retention 可只在該次呼叫取代已設定的政策:例如略過某個網域(保留對話記錄),或比常設設定更積極地修剪。Store 已設定的 retention 不會改變。
每個有政策的資料表,其錨點欄位索引會在首次呼叫 prune() 時才延遲建立(絕不會在 init() 時建立),因此未設定保留功能的部署不會承擔額外的索引寫入或磁碟開銷。首次修剪現有大型資料表時,需支付一次性的索引建立成本。後續修剪會重用該索引。
const results = await storage.prune({
maxRows: 50_000, // cap work this call
pauseMs: 50, // breathe between batches
})
for (const r of results) {
console.log(`${r.domain}.${r.table}: deleted ${r.deleted}, done=${r.done}`)
}
// One-off pass with different policies (configured retention untouched):
await storage.prune({
retention: {
observability: { spans: { maxAge: '1d' } },
},
})
傳回:Promise<PruneResult[]>
PruneOptionsPruneOptions 的直接連結
maxBatches?:
done: false 傳回。maxRows?:
done: false 傳回。pauseMs?:
signal?:
done: false 傳回部分結果。retention?:
retention 不會改變。PruneResultPruneResult 的直接連結
每項結果都描述一個資料表的進度:
interface PruneResult {
domain: string // e.g. 'memory'
table: string // physical table name, e.g. 'mastra_messages'
deleted: number // rows deleted during this call
done: boolean // false => eligible rows remain; call prune() again
}
按排程執行 prune按排程執行 prune 的直接連結
prune() 沒有內置 scheduler:您可自行決定執行時間。由於操作有界,單次呼叫未必能刪除所有資料。當任何結果為 done: false,即表示仍有符合資格的資料列,您可在下一次 tick 再次呼叫。這樣可讓每次調用保持短暫,並在多次執行中逐步清理大量積壓資料。
// Runs on your own cron (node-cron, a workflow schedule, an external job, etc.).
async function retentionTick() {
const results = await storage.prune({ maxRows: 100_000, pauseMs: 25 })
const incomplete = results.filter(r => !r.done)
if (incomplete.length) {
// Rows remain; the next scheduled tick will continue where this one stopped.
console.log(
'retention still draining:',
incomplete.map(r => `${r.domain}.${r.table}`),
)
}
}
您亦可使用 AbortSignal 取消長時間執行的修剪:循環會在批次之間停止,並以 done: false 傳回部分結果,讓下一次執行可順利恢復。
MongoDB TTL 索引(prune 的替代方案)MongoDB TTL 索引(prune 的替代方案) 的直接連結
MongoDB 提供原生 TTL(Time-To-Live)索引,可自動刪除過期文件,毋須手動呼叫 prune()。這是以背景 Thread 執行的資料庫層級功能。
在以下情況使用 MongoDB TTL 索引:
- 您希望自動刪除,毋須維護
- 保留期限固定(例如「一律為 30 日」)
- 您偏好資料庫原生解決方案
在以下情況使用 prune():
- 您需要精細控制刪除時間
- 您希望在辦公時間限制刪除速率
- 您需要可恢復、可取消的清理操作
- 您使用包含多個資料庫的複合儲存
兩種方式都有效。TTL 較簡單,prune() 則提供更多控制。
在 MongoDB 設定 TTL 索引在 MongoDB 設定 TTL 索引 的直接連結
TTL 索引適用於日期欄位。MongoDB 每 60 秒檢查一次索引,並刪除日期欄位 + TTL 時長小於目前時間的文件。
import { MongoDBStore } from '@mastra/mongodb'
const storage = new MongoDBStore({
id: 'mongodb-storage',
uri: process.env.MONGODB_URI!,
dbName: process.env.MONGODB_DB_NAME!,
indexes: [
// Messages expire after 30 days
{
collection: 'mastra_messages',
keys: { createdAt: 1 },
options: { expireAfterSeconds: 30 * 24 * 60 * 60 }, // 30 days
},
// Threads expire after 90 days
{
collection: 'mastra_threads',
keys: { createdAt: 1 },
options: { expireAfterSeconds: 90 * 24 * 60 * 60 }, // 90 days
},
// Spans expire after 7 days
{
collection: 'mastra_ai_spans',
keys: { startedAt: 1 },
options: { expireAfterSeconds: 7 * 24 * 60 * 60 }, // 7 days
},
],
})
TTL 索引會在文件過期後不久將其刪除(背景 Thread 約每 ~60 秒執行一次),但確實時間並無保證。如需精確、即時的清理,請改用 prune()。
回收磁碟空間回收磁碟空間 的直接連結
prune() 會刪除資料列,但不會縮小資料庫檔案。在 SQLite/libSQL 上,釋出的頁面會放入 freelist,並由日後的寫入操作重用,因此檔案會停止增長;對大部分使用者而言,單憑這一點已能解決無限增長的問題。
將這些可用空間交還作業系統是另一項工作,Mastra 不會代為管理。如果您確實需要縮小檔案,請在維護時段自行執行底層資料庫的壓縮操作(例如在自行託管的 libSQL 上執行 VACUUM)。完整的 VACUUM 會鎖定檔案,並需要約為檔案大小兩倍的可用磁碟空間。在 PostgreSQL 上,autovacuum 會自動回收無效 tuple 以供重用;只有在必須將磁碟空間交還作業系統時,才需要手動執行 VACUUM FULL。
對於 MongoDB,已刪除文件佔用的空間會由日後插入的文件重用。如要回收磁碟空間,請在維護時段執行 db.runCommand({ compact: "collection_name" })。
Turso Cloud 會代您管理儲存壓縮,因此毋須手動回收空間。這只適用於自行託管的 libSQL 檔案。