> Discover all available pages from the documentation index: https://mastra.zisheng.pro/llms.txt # Google Cloud Spanner 存储 Google Cloud Spanner 存储实现为 Mastra 提供水平高容量、强一致性的存储后端。它以 Cloud Spanner 的 GoogleSQL dialect 为目标。 ## 安装 **npm**: ```bash npm install @mastra/spanner@latest ``` **pnpm**: ```bash pnpm add @mastra/spanner@latest ``` **Yarn**: ```bash yarn add @mastra/spanner@latest ``` **Bun**: ```bash bun add @mastra/spanner@latest ``` ## 使用方法 ```typescript 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!, }) ``` 实例和数据库必须已存在。adapter 会在首次使用时创建所需表,因此提供给 Spanner client 的凭据需要具有运行 schema 变更的权限(或在具有提升权限的部署步骤中运行一次 `storage.init()`)。 ## 参数 **id** (`string`): 此 storage 实例的唯一标识符。 **projectId** (`string`): Google Cloud project ID。除非提供 database,否则必填。 **instanceId** (`string`): Cloud Spanner instance ID。除非提供 database,否则必填。 **databaseId** (`string`): Cloud Spanner database ID。除非提供 database,否则必填。 **database** (`@google-cloud/spanner Database`): 预先配置的 Spanner Database handle。当你在其他位置管理 Spanner client 时(例如跨服务共享身份验证或连接选项),请使用此项。 **spannerOptions** (`object`): 转发给 @google-cloud/spanner client constructor 的选项。可用它设置凭据、自定义 endpoint,或指向本地 emulator。 **disableInit** (`boolean`): 为 true 时,跳过首次使用时的自动建表。必须在单独的部署步骤中显式调用 storage.init()。 (Default: `false`) **skipDefaultIndexes** (`boolean`): 为 true 时,在初始化期间跳过创建默认索引。 (Default: `false`) **indexes** (`CreateIndexOptions[]`): 要创建的自定义二级索引。每个索引必须指定其所属表。索引将根据表名路由到适当的域。 **initMode** (`'sync' | 'validate'`): 控制 schema 初始化行为。'sync' 会在 init() 期间创建缺失的表、列和索引(历史行为)。'validate' 不会发出 DDL,而是验证每个预期表、列和默认/自定义索引是否已存在;若缺少任何项则抛出类型化的用户错误——当外部进程(Terraform、Liquibase、发布 pipeline 等)拥有 schema,而 Mastra 应仅验证它时很有用。 (Default: `'sync'`) ## 构造函数示例 可以通过多种方式实例化 `SpannerStore`: ```typescript 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 }, }) ``` ## 补充说明 ### Schema 管理 storage adapter 会创建以下表,全部使用 GoogleSQL dialect: - `mastra_workflow_snapshot`:Workflow 状态和执行数据 - `mastra_threads`:对话线程 - `mastra_messages`:单条消息 - `mastra_resources`:资源工作记忆 - `mastra_scorers`:评估分数 - `mastra_background_tasks`:后台 Tool 执行状态 - `mastra_agents`:精简 Agent 记录(id、状态、活跃版本) - `mastra_agent_versions`:版本化的 Agent 配置快照 - `mastra_mcp_clients` / `mastra_mcp_client_versions`:MCP client 配置及其版本历史 - `mastra_mcp_servers` / `mastra_mcp_server_versions`:MCP server 配置及其版本历史 - `mastra_skills` / `mastra_skill_versions`:Skill 记录和版本化的 Skill 快照(instructions、references、scripts、assets、内容树) - `mastra_skill_blobs`:以 SHA-256 hash 为键的内容寻址 blob 存储,用于 Skill 版本内容 - `mastra_prompt_blocks` / `mastra_prompt_block_versions`:prompt block 记录和版本化内容快照(模板内容、规则、请求上下文 schema) - `mastra_scorer_definitions` / `mastra_scorer_definition_versions`:scorer 定义记录和版本化配置快照(评判 instructions、model、分数范围、预设配置、默认采样) - `mastra_schedules` / `mastra_schedule_triggers`:由 cron 驱动的 Workflow 计划和触发历史,供 Mastra 内置 `WorkflowScheduler` 使用 - `mastra_workspaces` / `mastra_workspace_versions`:Workspace 记录和版本化配置快照(文件系统、Sandbox、mount、搜索、Skill、Tool) - `mastra_datasets` / `mastra_dataset_items` / `mastra_dataset_versions`:评估数据集、SCD-2 版本化项和版本快照 - `mastra_experiments` / `mastra_experiment_results`:实验运行及其逐项结果 - `mastra_favorites`:每用户的 Agent 和 Skill 收藏项,父记录上维护非规范化的 `favoriteCount` - `mastra_channel_installations` / `mastra_channel_config`:多平台 channel 安装及逐平台配置 - `mastra_ai_spans`:用于 observability 的 AI tracing span(逐 Trace 和逐 span 的记录,用于支持 Studio Trace UI) 表会为文本和 JSON payload 使用 `STRING(MAX)`,并使用 `INT64`、`FLOAT64`、`BOOL` 和 `TIMESTAMP`。 以下表包含 Spanner 特有的 `STORED` generated column,adapter 会从 JSON payload 填充这些列,使常见 filter 可使用常规二级索引,而非 `JSON_VALUE` scan: - `mastra_workflow_snapshot.snapshotStatus`:从 `snapshot` 提取 `$.status`。支持 `listWorkflowRuns({ status })`。 - `mastra_schedules.target_workflow_id`:从 `target` 提取 `$.workflowId`。支持 `listSchedules({ workflowId })`。 二者均会在 `init()` 期间通过 `ALTER TABLE ... ADD COLUMN IF NOT EXISTS` 添加,并在 `initMode: 'validate'`(schema 由外部拥有)下跳过。缺少该列时,adapter 会在运行时回退到 `JSON_VALUE` filter。 adapter 不会创建或使用 schema。请使用专用数据库进行隔离。 ### 初始化 将 storage 传递给 `Mastra` class 时,会在任何存储操作之前自动调用 `init()`: ```typescript 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 }) ``` 若直接使用 storage,请在首次操作前调用一次 `init()`。Spanner 不允许并发 schema 变更,因此 `SpannerStore.init()` 会按顺序运行每个域的设置。 ```typescript 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: '...' }) ``` > **注意:** 未调用 `init()` 且 `disableInit` 为 true 时,所需表将不存在,存储操作会失败。 ### GoogleSQL 特性 一些行为与其他关系 adapter 不同: - Upsert 使用 `INSERT OR UPDATE`。Spanner 不为 upsert 提供 `RETURNING` clause,因此需要写入后状态的调用方必须将其读回。 - 不存在 `TRUNCATE`。`dangerouslyClearAll()` 会发出 `DELETE WHERE TRUE`。 - 标识符使用反引号引用。 - DDL 通过异步的 `database.updateSchema(...)`(长时间运行操作)应用。 - 不支持 `NULLS FIRST/LAST`。带 NULL 处理的排序通过 `IS NULL` 排序键模拟。 - 原生不支持 JSON containment。`listTraces` 的 `metadata` 和 `scope` filter 会编译为按键的 `JSON_VALUE(...) = @v` 相等性检查,`tags` filter 会编译为对 `JSON_QUERY_ARRAY(...)` 的 `EXISTS`。这不同于 Postgres 的 `@>` containment operator(后者可在单次索引扫描中匹配嵌套结构):大多数一次性查找仍可正常工作,但无法表达深度嵌套的结构性匹配。 ### 直接访问数据库 `SpannerStore` 暴露底层 Spanner client 对象: ```typescript 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) ``` 它们适用于定制事务或 schema introspection 等高级场景。直接复用 database 时,会绕过 adapter 的验证和 JSON 转换逻辑。 ### 使用 emulator 进行本地开发 使用 Docker 在本地运行 Cloud Spanner emulator: ```bash docker run -p 9010:9010 -p 9020:9020 gcr.io/cloud-spanner-emulator/emulator ``` 设置 `SPANNER_EMULATOR_HOST=localhost:9010`,并在运行应用前创建实例和数据库: ```bash gcloud spanner instances create test-instance --config=emulator-config --nodes=1 gcloud spanner databases create test-db --instance=test-instance ``` 然后,在 Node.js 进程中设置相同 env var 后进行连接。`@google-cloud/spanner` client 会自动检测 emulator。