> Discover all available pages from the documentation index: https://mastra.zisheng.pro/zh-HK/llms.txt # 儲存保留 預設情況下,儲存空間會無限增長。保留是一套可選用、按資料存續時間清理的系統:您可以在 `retention` 設定中為每個資料表宣告 `maxAge` 政策,然後呼叫 `storage.prune()`,刪除超過設定存續時間的資料列。任何未設定的內容都會永久保留,因此在您選用此功能前,行為不會有任何變化。 `prune()` 會刪除資料列。它可限制增長,並能安全地在大型資料表上執行(分批、有界、可恢復、可取消)。它不會回收磁碟空間:在 SQLite/libSQL 上,釋出的頁面會由日後的寫入操作重用,因此檔案會停止增長;至於把磁碟空間交還作業系統(例如執行 `VACUUM`),則留待底層資料庫及操作人員管理。 保留功能只涵蓋**增長資料表**:即在正常操作下,因對話記錄、遙測、工作及執行記錄、排程觸發記錄、事件 Feed 等而不斷累積資料列的資料表。由使用者建立的內容及設定(Agent、Skill、Workspace、提示詞區塊、資料集、排程定義、Channel 安裝項目等)會隨使用者意圖而增長,並由使用者明確編輯或刪除,因此不能作為有效的保留鍵。 參考實作包括 [libSQL](https://mastra.zisheng.pro/zh-HK/reference/storage/libsql)、[PostgreSQL](https://mastra.zisheng.pro/zh-HK/reference/storage/postgresql) 及 [MongoDB](https://mastra.zisheng.pro/zh-HK/reference/storage/mongodb)。其他 Adapter 在實作保留功能前,會永久保留資料列。 ## 使用範例 在任何 `MastraCompositeStore`(或擴充它的 Adapter,例如 `LibSQLStore`)上宣告 `retention`,然後從您自己的 scheduler 呼叫 `prune()`。 ```typescript 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`,讓未知網域或資料表成為編譯錯誤: ```typescript 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** (`RetentionConfig`): 按網域及資料表設定的存續時間政策。未設定的網域及資料表會永久保留。 **retention.\[domain]** (`Record`): 真實的儲存網域鍵(例如 memory、observability)。將該網域符合保留資格的資料表鍵對應至其政策。 ### TableRetentionPolicy **maxAge** (`Duration`): 資料列可保留的最長存續時間。錨點時間戳嚴格早於 Date.now() - maxAge 的資料列符合刪除資格。數字表示毫秒,也可使用帶單位後綴的字串:ms、s、m、h、d、w(例如 '30d'、'12h')。 **batchSize** (`number`): 每批刪除的資料列數目。每一批都是獨立交易,可限制大型資料表的鎖定時間及 WAL 增長。 (Default: `1000`) ### 符合保留資格的資料表 每個網域都會宣告其哪些資料表可以按存續時間修剪,以及由哪個時間戳欄位作為比較錨點。錨點的選擇方式,會讓 `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?)` 在 `retention` 中已設定政策的所有網域,刪除超過所設定 `maxAge` 的資料列。每個觸及的資料表都會傳回一個 `PruneResult`。如果未設定 `retention`,則不會執行任何操作,並傳回 `[]`。 `prune()` 的設計可安全用於包含數百萬資料列的資料表。它會以有界的分批區塊刪除(每批都是獨立交易),因此不會長時間鎖定或令交易記錄膨脹。它永遠不會執行 `VACUUM`。 傳入 `options.retention` 可只在該次呼叫取代已設定的政策:例如略過某個網域(保留對話記錄),或比常設設定更積極地修剪。Store 已設定的 `retention` 不會改變。 每個有政策的資料表,其錨點欄位索引會在首次呼叫 `prune()` 時才延遲建立(絕不會在 `init()` 時建立),因此未設定保留功能的部署不會承擔額外的索引寫入或磁碟開銷。首次修剪現有大型資料表時,需支付一次性的索引建立成本。後續修剪會重用該索引。 ```typescript 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` ##### PruneOptions **maxBatches** (`number`): 每個資料表在每次呼叫中的刪除批次上限。達到上限時,該資料表的結果會以 done: false 傳回。 **maxRows** (`number`): 每個資料表在每次呼叫中的刪除資料列上限。達到上限時,該資料表的結果會以 done: false 傳回。 **pauseMs** (`number`): 批次之間的延遲(以毫秒計),避免即時流量得不到資源。 **signal** (`AbortSignal`): 協作式取消。批次循環會在批次之間檢查此項,並乾淨地停止,以 done: false 傳回部分結果。 **retention** (`RetentionConfig`): 只在該次呼叫取代 Store 已設定的保留政策,例如略過某個網域或更積極地修剪。已設定的 retention 不會改變。 ##### PruneResult 每項結果都描述一個資料表的進度: ```typescript 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()` 沒有內置 scheduler:您可自行決定執行時間。由於操作有界,單次呼叫未必能刪除所有資料。當任何結果為 `done: false`,即表示仍有符合資格的資料列,您可在下一次 tick 再次呼叫。這樣可讓每次調用保持短暫,並在多次執行中逐步清理大量積壓資料。 ```typescript // 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(Time-To-Live)索引](https://www.mongodb.com/docs/manual/core/index-ttl/),可自動刪除過期文件,毋須手動呼叫 `prune()`。這是以背景 Thread 執行的資料庫層級功能。 > **何時使用 TTL 或 prune():** **在以下情況使用 MongoDB TTL 索引:** > > - 您希望自動刪除,毋須維護 > - 保留期限固定(例如「一律為 30 日」) > - 您偏好資料庫原生解決方案 > > **在以下情況使用 `prune()`:** > > - 您需要精細控制刪除時間 > - 您希望在辦公時間限制刪除速率 > - 您需要可恢復、可取消的清理操作 > - 您使用包含多個資料庫的複合儲存 > > 兩種方式都有效。TTL 較簡單,`prune()` 則提供更多控制。 ### 在 MongoDB 設定 TTL 索引 TTL 索引適用於日期欄位。MongoDB 每 60 秒檢查一次索引,並刪除日期欄位 + TTL 時長小於目前時間的文件。 ```typescript 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" })`](https://www.mongodb.com/docs/manual/reference/command/compact/)。 > **LibSQL 及 Turso:** [Turso Cloud](https://mastra.zisheng.pro/zh-HK/reference/storage/libsql) 會代您管理儲存壓縮,因此毋須手動回收空間。這只適用於自行託管的 libSQL 檔案。 ## 相關內容 - [libSQL 儲存](https://mastra.zisheng.pro/zh-HK/reference/storage/libsql) - [PostgreSQL 儲存](https://mastra.zisheng.pro/zh-HK/reference/storage/postgresql) - [複合儲存](https://mastra.zisheng.pro/zh-HK/reference/storage/composite) - [儲存概覽](https://mastra.zisheng.pro/zh-HK/reference/storage/overview)