Stockage Google Cloud Spanner
L'implémentation du stockage Google Cloud Spanner fournit à Mastra un backend de stockage à forte capacité, évolutif horizontalement et fortement cohérent. Elle cible le dialecte GoogleSQL de Cloud Spanner.
InstallationLien direct vers Installation
- npm
- pnpm
- Yarn
- Bun
npm install @mastra/spanner@latest
pnpm add @mastra/spanner@latest
yarn add @mastra/spanner@latest
bun add @mastra/spanner@latest
UtilisationLien direct vers Utilisation
import { SpannerStore } from '@mastra/spanner'
const storage = new SpannerStore({
id: 'spanner-storage',
projectId: process.env.SPANNER_PROJECT_ID!,
instanceId: process.env.SPANNER_INSTANCE_ID!,
databaseId: process.env.SPANNER_DATABASE_ID!,
})
L'instance et la base de données doivent déjà exister. L'adaptateur crée les tables requises lors de la première utilisation ; les identifiants fournis au client Spanner doivent donc autoriser les modifications du schéma (vous pouvez aussi exécuter storage.init() une fois pendant une étape de déploiement avec des identifiants disposant de privilèges élevés).
ParamètresLien direct vers Paramètres
id:
projectId?:
database est fourni.instanceId?:
database est fourni.databaseId?:
database est fourni.database?:
spannerOptions?:
@google-cloud/spanner. Utilisez-les pour définir les identifiants, des points de terminaison personnalisés ou l’émulateur local.disableInit?:
storage.init() pendant une étape de déploiement distincte.skipDefaultIndexes?:
indexes?:
initMode?:
'sync' crée les tables, colonnes et index manquants pendant init() (comportement historique). 'validate' n’émet aucune instruction DDL et vérifie à la place que toutes les tables, colonnes et tous les index par défaut ou personnalisés attendus existent déjà. Une erreur utilisateur typée est levée si un élément manque. Cette option est utile lorsqu’un processus externe (Terraform, Liquibase, pipeline de publication, etc.) gère le schéma et que Mastra doit uniquement le vérifier.Exemples de constructeursLien direct vers Exemples de constructeurs
Vous pouvez instancier SpannerStore de plusieurs manières :
import { Spanner } from '@google-cloud/spanner'
import { SpannerStore } from '@mastra/spanner'
// Using projectId / instanceId / databaseId
const store1 = new SpannerStore({
id: 'spanner-storage-1',
projectId: 'my-gcp-project',
instanceId: 'my-instance',
databaseId: 'mastra',
})
// Reusing an existing Spanner Database handle
const spanner = new Spanner({ projectId: 'my-gcp-project' })
const database = spanner.instance('my-instance').database('mastra')
const store2 = new SpannerStore({
id: 'spanner-storage-2',
database,
})
// Using the local Spanner emulator (set the SPANNER_EMULATOR_HOST env var)
process.env.SPANNER_EMULATOR_HOST = 'localhost:9010'
const store3 = new SpannerStore({
id: 'spanner-storage-emulator',
projectId: 'test-project',
instanceId: 'test-instance',
databaseId: 'test-db',
spannerOptions: { servicePath: 'localhost', port: 9010, sslCreds: undefined },
})
Remarques complémentairesLien direct vers Remarques complémentaires
Gestion du schémaLien direct vers Gestion du schéma
L'adaptateur de stockage crée les tables suivantes, toutes à l'aide du dialecte GoogleSQL :
mastra_workflow_snapshot: état du workflow et données d'exécutionmastra_threads: threads de conversationmastra_messages: messages individuelsmastra_resources: mémoire de travail des ressourcesmastra_scorers: scores d'évaluationmastra_background_tasks: état d'exécution des Tools en arrière-planmastra_agents: enregistrements légers des agents (ID, état, version active)mastra_agent_versions: instantanés versionnés de la configuration des agentsmastra_mcp_clients/mastra_mcp_client_versions: configurations des clients MCP et historique de leurs versionsmastra_mcp_servers/mastra_mcp_server_versions: configurations des serveurs MCP et historique de leurs versionsmastra_skills/mastra_skill_versions: enregistrements de Skills et instantanés versionnés des Skills (instructions, références, scripts, ressources, arborescence du contenu)mastra_skill_blobs: stockage de blobs adressables par contenu, indexé par hachage SHA-256 et utilisé pour le contenu des versions de Skillsmastra_prompt_blocks/mastra_prompt_block_versions: enregistrements de blocs de prompt et instantanés versionnés du contenu (contenu du modèle, règles, schéma Request Context)mastra_scorer_definitions/mastra_scorer_definition_versions: enregistrements de définitions de Scorers et instantanés versionnés de la configuration (instructions du juge, modèle, plage de scores, configuration prédéfinie, échantillonnage par défaut)mastra_schedules/mastra_schedule_triggers: planifications de workflows pilotées par cron et historique des déclenchements, utilisés par leWorkflowSchedulerintégré de Mastramastra_workspaces/mastra_workspace_versions: enregistrements de Workspaces et instantanés versionnés de leur configuration (système de fichiers, Sandbox, montages, recherche, Skills, Tools)mastra_datasets/mastra_dataset_items/mastra_dataset_versions: jeux de données d'évaluation, éléments versionnés selon SCD-2 et instantanés de versionsmastra_experiments/mastra_experiment_results: exécutions d'expériences et résultats de chaque élémentmastra_favorites: favoris par utilisateur pour les agents et les Skills, avec le champfavoriteCountdénormalisé conservé dans l'enregistrement parentmastra_channel_installations/mastra_channel_config: installations de canaux multiplateformes et configuration propre à chaque plateformemastra_ai_spans: spans de traçage de l'IA pour l'observabilité (enregistrements par trace et par span, utilisés pour alimenter l'interface des traces de Studio)
Les tables sont créées avec STRING(MAX) pour le texte et les payloads JSON, ainsi qu'avec INT64, FLOAT64, BOOL et TIMESTAMP.
Les tables suivantes contiennent des colonnes générées STORED propres à Spanner. L'adaptateur les renseigne à partir des payloads JSON afin que les filtres courants puissent utiliser un index secondaire standard plutôt qu'une analyse JSON_VALUE :
mastra_workflow_snapshot.snapshotStatus: extrait$.statusdesnapshot. Prend en chargelistWorkflowRuns({ status }).mastra_schedules.target_workflow_id: extrait$.workflowIddetarget. Prend en chargelistSchedules({ workflowId }).
Ces deux colonnes sont ajoutées avec ALTER TABLE ... ADD COLUMN IF NOT EXISTS pendant init() et ignorées lorsque initMode: 'validate' est utilisé (le schéma est alors géré en externe). Si la colonne est absente, l'adaptateur utilise à l'exécution un filtre JSON_VALUE comme solution de repli.
L'adaptateur ne crée ni n'utilise de schémas. Utilisez une base de données dédiée pour garantir l'isolation.
InitialisationLien direct vers Initialisation
Lorsque vous transmettez le stockage à la classe Mastra, init() est appelée automatiquement avant toute opération de stockage :
import { Mastra } from '@mastra/core'
import { SpannerStore } from '@mastra/spanner'
const storage = new SpannerStore({
id: 'spanner-storage',
projectId: process.env.SPANNER_PROJECT_ID!,
instanceId: process.env.SPANNER_INSTANCE_ID!,
databaseId: process.env.SPANNER_DATABASE_ID!,
})
const mastra = new Mastra({
storage, // init() is called automatically
})
Si vous utilisez directement le stockage, appelez init() une fois avant la première opération. Spanner n'autorise pas les modifications simultanées du schéma ; SpannerStore.init() exécute donc séquentiellement la configuration de chaque domaine.
const storage = new SpannerStore({
id: 'spanner-storage',
projectId: process.env.SPANNER_PROJECT_ID!,
instanceId: process.env.SPANNER_INSTANCE_ID!,
databaseId: process.env.SPANNER_DATABASE_ID!,
})
await storage.init()
const memory = await storage.getStore('memory')
const thread = await memory?.getThreadById({ threadId: '...' })
Si init() n'est pas appelée et que disableInit vaut true, les tables requises n'existeront pas et les opérations de stockage échoueront.
Particularités de GoogleSQLLien direct vers Particularités de GoogleSQL
Certains comportements diffèrent de ceux des autres adaptateurs relationnels :
- Les upserts utilisent
INSERT OR UPDATE. Spanner ne fournit aucune clauseRETURNINGpour les upserts ; les appelants qui ont besoin de l'état après l'écriture doivent donc le relire. - Il n'existe pas de commande
TRUNCATE.dangerouslyClearAll()émetDELETE WHERE TRUE. - Les identifiants sont placés entre des accents graves.
- Les instructions DDL sont appliquées avec
database.updateSchema(...), qui est asynchrone (opération de longue durée). NULLS FIRST/LASTn'est pas pris en charge. Le tri avec gestion des valeurs NULL est émulé à l'aide d'une clé de triIS NULL.- La contenance JSON n'est pas prise en charge nativement. Dans
listTraces, les filtresmetadataetscopesont compilés en contrôles d'égalitéJSON_VALUE(...) = @vpar clé, tandis que les filtrestagssont compilés enEXISTSsurJSON_QUERY_ARRAY(...). Ce comportement diffère de l'opérateur de contenance@>de Postgres, qui peut faire correspondre une structure imbriquée en une seule analyse d'index : la plupart des recherches ponctuelles fonctionnent toujours, mais les correspondances structurelles profondément imbriquées ne peuvent pas être exprimées.
Accès direct à la base de donnéesLien direct vers Accès direct à la base de données
SpannerStore expose les objets sous-jacents du client Spanner :
store.database // @google-cloud/spanner Database
store.instance // @google-cloud/spanner Instance (when created internally)
store.spanner // @google-cloud/spanner Spanner client (when created internally)
Ces objets sont destinés aux scénarios avancés, comme les transactions personnalisées ou l'introspection du schéma. Lorsque vous réutilisez directement la base de données, vous contournez la logique de validation et de conversion JSON de l'adaptateur.
Développement local avec l'émulateurLien direct vers Développement local avec l'émulateur
Exécutez localement l'émulateur Cloud Spanner avec Docker :
docker run -p 9010:9010 -p 9020:9020 gcr.io/cloud-spanner-emulator/emulator
Définissez SPANNER_EMULATOR_HOST=localhost:9010, puis créez l'instance et la base de données avant d'exécuter votre application :
gcloud spanner instances create test-instance --config=emulator-config --nodes=1
gcloud spanner databases create test-db --instance=test-instance
Connectez-vous ensuite en définissant la même variable d'environnement dans votre processus Node.js. Le client @google-cloud/spanner détecte automatiquement l'émulateur.