> Discover all available pages from the documentation index: https://mastra.zisheng.pro/fr/llms.txt # Rétention du Storage Par défaut, le Storage croît sans limite. La rétention est un système de nettoyage facultatif fondé sur l'âge : vous déclarez des politiques `maxAge` par table dans la configuration `retention`, puis appelez `storage.prune()` pour supprimer les lignes plus anciennes que l'âge configuré. Tout ce que vous ne configurez pas est conservé indéfiniment ; aucun comportement ne change donc tant que vous n'activez pas cette fonctionnalité. `prune()` supprime des lignes. Cette méthode limite la croissance et peut être exécutée en toute sécurité sur de grandes tables (par lots, bornée, reprenable et annulable). Elle ne récupère jamais l'espace disque : sur SQLite/libSQL, les pages libérées sont réutilisées par les écritures ultérieures, ce qui arrête la croissance du fichier, mais la restitution de l'espace disque au système d'exploitation (par exemple au moyen d'un `VACUUM`) reste à la charge de la base de données sous-jacente et de l'opérateur. La rétention couvre uniquement les **tables de croissance** : les tables qui accumulent des lignes sans limite du fait de leur fonctionnement normal (historique des conversations, télémétrie, enregistrements de Jobs et de Runs, historique des déclenchements planifiés, flux d'événements). Les artefacts et la configuration créés par les utilisateurs (Agents, Skills, Workspaces, blocs de prompts, datasets, définitions de planifications, installations de Channels, etc.) croissent selon l'intention de l'utilisateur et sont modifiés ou supprimés explicitement ; ils ne constituent donc pas des clés de rétention valides. Les implémentations de référence sont [libSQL](https://mastra.zisheng.pro/fr/reference/storage/libsql), [PostgreSQL](https://mastra.zisheng.pro/fr/reference/storage/postgresql) et [MongoDB](https://mastra.zisheng.pro/fr/reference/storage/mongodb). Les autres adaptateurs conservent les lignes indéfiniment jusqu'à ce qu'ils implémentent la rétention. ## Exemple d'utilisation Déclarez `retention` sur n'importe quel `MastraCompositeStore` (ou sur un adaptateur qui l'étend, tel que `LibSQLStore`), puis appelez `prune()` depuis votre propre planificateur. ```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` est entièrement typé. Les clés doivent correspondre à de véritables clés de domaine et chaque clé de table doit être déclarée par le domaine comme compatible avec la rétention. La transmission directe de l'objet dans la configuration d'un Store déclenche sa vérification de type ; si vous le construisez séparément, utilisez `satisfies RetentionConfig` afin que les domaines ou tables inconnus produisent des erreurs de compilation : ```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 ``` ## Configuration de la rétention Définissez le champ `retention` dans la configuration du Store. **retention** (`RetentionConfig`): Politiques d’âge par domaine et par table. Les domaines et tables non définis sont conservés indéfiniment. **retention.\[domain]** (`Record`): Clé réelle d'un domaine de Storage (par exemple, memory ou observability). Associe les clés de tables de ce domaine compatibles avec la rétention à leurs politiques. ### TableRetentionPolicy **maxAge** (`Duration`): Âge maximal de conservation des lignes. Les lignes dont l'horodatage de référence est strictement antérieur à Date.now() - maxAge peuvent être supprimées. Un nombre représente des millisecondes ; une chaîne peut comporter un suffixe d'unité : ms, s, m, h, d, w (par exemple, '30d' ou '12h'). **batchSize** (`number`): Nombre de lignes supprimées par lot. Chaque lot possède sa propre transaction, ce qui limite la durée du verrouillage et la croissance du WAL sur les grandes tables. (Default: `1000`) ### Tables compatibles avec la rétention Chaque domaine déclare les tables pouvant être nettoyées selon leur âge et la colonne d'horodatage servant de référence à la comparaison. Cette référence est choisie pour que `maxAge` corresponde au sens attendu pour les données concernées. Les journaux en ajout seul utilisent l'heure de création et l'état actif utilise la dernière activité. Les Jobs et les Runs utilisent l'heure de fin ; les travaux en cours ne sont donc jamais supprimés. | Domaine | Clé de table | Colonne de référence | Mesure de `maxAge` | | ----------------- | ------------------ | -------------------- | ----------------------------------------------------------------------------------- | | `memory` | `threads` | `createdAt` | Âge du fil de discussion | | `memory` | `messages` | `createdAt` | Âge du message | | `memory` | `resources` | `createdAt` | Âge de la ressource | | `threadState` | `threadState` | `updatedAt` | Inactivité : l'état des fils encore actifs est conservé | | `observability` | `spans` | `startedAt` | Âge du span | | `observability` | `metrics` | `timestamp` | Âge de l'événement de métrique (v-next uniquement) | | `observability` | `logs` | `timestamp` | Âge de l'événement de journal (v-next uniquement) | | `observability` | `scores` | `timestamp` | Âge de l'événement de score (v-next uniquement) | | `observability` | `feedback` | `timestamp` | Âge de l'événement de feedback (v-next uniquement) | | `scores` | `scorers` | `createdAt` | Âge de l'enregistrement de score | | `workflows` | `workflowSnapshot` | `updatedAt` | Inactivité ; les Workflows suspendus ou de longue durée sont conservés | | `backgroundTasks` | `backgroundTasks` | `completedAt` | Temps écoulé depuis la fin ; les tâches en cours (`NULL`) ne sont jamais supprimées | | `experiments` | `experiments` | `completedAt` | Temps écoulé depuis la fin ; les expériences en cours ne sont jamais supprimées | | `notifications` | `notifications` | `createdAt` | Âge de la notification | | `harness` | `sessions` | `createdAt` | Âge de l'enregistrement de session | | `schedules` | `triggers` | `actual_fire_at` | Âge de l'historique des déclenchements (colonne epoch-ms) | > **Remarque:** > > - La table `observational_memory` de Memory ne possède aucun horodatage de référence ; elle ne peut donc pas être nettoyée selon son âge et ne constitue pas une clé de rétention valide. > - Les expériences sont supprimées comme des unités entières : les lignes de résultats d'une expérience ancienne sont supprimées avec elle (les résultats sont supprimés en cascade avec leur parent), afin qu'un Run ne reste jamais partiellement supprimé. La rétention ne possède pas de clé `results` distincte. > - Pour `schedules`, la table de croissance est l'historique des déclenchements (`schedule_triggers`, une ligne par déclenchement) : les définitions de planifications sont de la configuration et ne sont pas supprimées. > - Sur PostgreSQL, les horodatages de référence utilisent les colonnes miroirs sensibles au fuseau horaire (par exemple `createdAtZ` et `completedAtZ`). > - LibSQL et PostgreSQL prennent en charge tous les domaines ci-dessus à l'exception de `harness`, que PostgreSQL n'implémente pas. MongoDB les prend tous en charge à l'exception de `threadState` et `harness`. > - Le domaine Observability v-next de PostgreSQL stocke les événements de signaux dans des tables partitionnées par jour (`spans`, `metrics`, `logs`, `scores`, `feedback`). Pour ce domaine, `prune()` supprime des partitions quotidiennes entières (ou des chunks TimescaleDB) entièrement antérieures à la date limite au lieu de supprimer des lignes : le niveau de détail effectif est d'un jour et une partition est supprimée uniquement lorsque sa journée entière est antérieure à `maxAge`. `PruneResult.deleted` indique le nombre de lignes des partitions supprimées. ## Méthodes ### Rétention #### `prune(options?)` Supprime les lignes plus anciennes que leur `maxAge` configuré dans chaque domaine possédant une politique dans `retention`. Renvoie un `PruneResult` par table traitée. Si aucune valeur `retention` n'est configurée, la méthode ne fait rien et renvoie `[]`. `prune()` est conçue pour s'exécuter en toute sécurité sur des tables contenant des millions de lignes. Elle effectue la suppression par chunks bornés et regroupés en lots (chaque lot possède sa propre transaction), sans verrouillage prolongé ni gonflement du journal des transactions. Elle n'exécute jamais de `VACUUM`. Transmettez `options.retention` pour remplacer les politiques configurées uniquement pour cet appel : par exemple, pour ignorer un domaine (conserver l'historique des conversations) ou effectuer un nettoyage plus agressif que la configuration permanente. La valeur `retention` configurée du Store reste inchangée. Les index des colonnes de référence sont créés à la demande lors du premier appel à `prune()` pour chaque table possédant une politique (jamais pendant `init()`). Les déploiements qui ne configurent pas la rétention ne subissent donc aucun coût supplémentaire d'écriture d'index ou d'espace disque. Le premier nettoyage d'une grande table existante implique la création ponctuelle de l'index. Les nettoyages suivants réutilisent cet index. ```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' } }, }, }) ``` Renvoie : `Promise` ##### PruneOptions **maxBatches** (`number`): Nombre maximal de lots de suppression par table et par appel. Lorsque cette limite est atteinte, le résultat de la table est renvoyé avec done: false. **maxRows** (`number`): Nombre maximal de lignes supprimées par table et par appel. Lorsque cette limite est atteinte, le résultat de la table est renvoyé avec done: false. **pauseMs** (`number`): Délai en millisecondes entre les lots afin de ne pas priver le trafic actif de ressources. **signal** (`AbortSignal`): Annulation coopérative. La boucle de traitement vérifie ce signal entre les lots et s'arrête proprement en renvoyant des résultats partiels avec done: false. **retention** (`RetentionConfig`): Remplace les politiques de rétention configurées du Store uniquement pour cet appel, par exemple afin d'ignorer un domaine ou d'effectuer un nettoyage plus agressif. La valeur retention configurée reste inchangée. ##### PruneResult Chaque résultat décrit la progression d'une table : ```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 } ``` ## Exécution planifiée de prune `prune()` ne possède aucun planificateur intégré : vous décidez de son exécution. Comme son travail est borné, un seul appel peut ne pas tout supprimer. Lorsqu'un résultat contient `done: false`, il reste des lignes admissibles et vous devez rappeler la méthode au prochain tick. Chaque invocation reste ainsi courte et un backlog important peut être résorbé sur plusieurs Runs. ```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}`), ) } } ``` Vous pouvez également annuler un nettoyage de longue durée avec un `AbortSignal` : la boucle s'arrête entre les lots et renvoie des résultats partiels avec `done: false`, ce qui permet au Run suivant de reprendre proprement. ## Index TTL MongoDB (solution alternative à prune) MongoDB fournit des [index TTL (Time-To-Live)](https://www.mongodb.com/docs/manual/core/index-ttl/) natifs qui suppriment automatiquement les documents expirés sans nécessiter d'appels manuels à `prune()`. Cette fonctionnalité de la base de données s'exécute dans un thread d'arrière-plan. > **Quand utiliser TTL ou prune():** **Utilisez les index TTL MongoDB lorsque :** > > - Vous souhaitez une suppression automatique sans maintenance > - Vos périodes de rétention sont fixes (par exemple, « toujours 30 jours ») > - Vous préférez les solutions natives de la base de données > > **Utilisez `prune()` lorsque :** > > - Vous avez besoin d'un contrôle précis du moment de la suppression > - Vous souhaitez limiter le débit de suppression pendant les heures ouvrées > - Vous avez besoin d'opérations de nettoyage reprenables et annulables > - Vous utilisez un Storage composite avec plusieurs bases de données > > Les deux approches sont valides. TTL est plus simple, tandis que `prune()` offre davantage de contrôle. ### Configuration des index TTL sur MongoDB Les index TTL fonctionnent sur les champs de date. MongoDB vérifie l'index toutes les 60 secondes et supprime les documents pour lesquels le champ de date + la durée TTL est antérieur à l'heure actuelle. ```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 }, ], }) ``` > **Astuce:** Les index TTL suppriment les documents peu après leur expiration (le thread d'arrière-plan s'exécute environ toutes les 60 secondes), mais le moment exact n'est pas garanti. Pour un nettoyage précis et immédiat, utilisez plutôt `prune()`. ## Récupération de l'espace disque `prune()` supprime des lignes, mais ne réduit pas le fichier de base de données. Sur SQLite/libSQL, les pages libérées sont placées dans une liste libre et réutilisées par les écritures ultérieures ; le fichier cesse donc de croître. Pour la plupart des utilisateurs, cela suffit à résoudre le problème de croissance illimitée. La restitution de cet espace libre au système d'exploitation constitue une opération distincte que Mastra ne gère pas. Si vous devez spécifiquement réduire le fichier, exécutez vous-même la compaction de la base de données sous-jacente (par exemple, `VACUUM` sur une instance libSQL auto-hébergée) pendant une fenêtre de maintenance. Un `VACUUM` complet verrouille le fichier et nécessite environ deux fois sa taille en espace disque libre. Sur PostgreSQL, autovacuum récupère automatiquement les tuples morts afin de les réutiliser ; un `VACUUM FULL` manuel est uniquement nécessaire si vous devez rendre l'espace disque au système d'exploitation. Pour MongoDB, les documents supprimés sont réutilisés par les insertions ultérieures. Pour récupérer l'espace disque, exécutez [`db.runCommand({ compact: "collection_name" })`](https://www.mongodb.com/docs/manual/reference/command/compact/) pendant une fenêtre de maintenance. > **LibSQL et Turso:** [Turso Cloud](https://mastra.zisheng.pro/fr/reference/storage/libsql) gère la compaction du Storage pour vous ; aucune récupération manuelle n'est donc nécessaire. Cela s'applique uniquement aux fichiers libSQL auto-hébergés. ## Voir aussi - [Storage libSQL](https://mastra.zisheng.pro/fr/reference/storage/libsql) - [Storage PostgreSQL](https://mastra.zisheng.pro/fr/reference/storage/postgresql) - [Storage composite](https://mastra.zisheng.pro/fr/reference/storage/composite) - [Présentation du Storage](https://mastra.zisheng.pro/fr/reference/storage/overview)