跳到主要内容

Google Cloud Spanner 存储

Google Cloud Spanner 存储实现为 Mastra 提供水平高容量、强一致性的存储后端。它以 Cloud Spanner 的 GoogleSQL dialect 为目标。

安装
安装的直接链接

npm install @mastra/spanner@latest

使用方法
使用方法的直接链接

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
= false
为 true 时,跳过首次使用时的自动建表。必须在单独的部署步骤中显式调用 storage.init()

skipDefaultIndexes?:

boolean
= false
为 true 时,在初始化期间跳过创建默认索引。

indexes?:

CreateIndexOptions[]
要创建的自定义二级索引。每个索引必须指定其所属表。索引将根据表名路由到适当的域。

initMode?:

'sync' | 'validate'
= 'sync'
控制 schema 初始化行为。'sync' 会在 init() 期间创建缺失的表、列和索引(历史行为)。'validate' 不会发出 DDL,而是验证每个预期表、列和默认/自定义索引是否已存在;若缺少任何项则抛出类型化的用户错误——当外部进程(Terraform、Liquibase、发布 pipeline 等)拥有 schema,而 Mastra 应仅验证它时很有用。

构造函数示例
构造函数示例的直接链接

可以通过多种方式实例化 SpannerStore

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 管理
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),并使用 INT64FLOAT64BOOLTIMESTAMP

以下表包含 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()

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() 会按顺序运行每个域的设置。

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 特性
GoogleSQL 特性的直接链接

一些行为与其他关系 adapter 不同:

  • Upsert 使用 INSERT OR UPDATE。Spanner 不为 upsert 提供 RETURNING clause,因此需要写入后状态的调用方必须将其读回。
  • 不存在 TRUNCATEdangerouslyClearAll() 会发出 DELETE WHERE TRUE
  • 标识符使用反引号引用。
  • DDL 通过异步的 database.updateSchema(...)(长时间运行操作)应用。
  • 不支持 NULLS FIRST/LAST。带 NULL 处理的排序通过 IS NULL 排序键模拟。
  • 原生不支持 JSON containment。listTracesmetadatascope filter 会编译为按键的 JSON_VALUE(...) = @v 相等性检查,tags filter 会编译为对 JSON_QUERY_ARRAY(...)EXISTS。这不同于 Postgres 的 @> containment operator(后者可在单次索引扫描中匹配嵌套结构):大多数一次性查找仍可正常工作,但无法表达深度嵌套的结构性匹配。

直接访问数据库
直接访问数据库的直接链接

SpannerStore 暴露底层 Spanner client 对象:

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 进行本地开发
使用 emulator 进行本地开发的直接链接

使用 Docker 在本地运行 Cloud Spanner emulator:

docker run -p 9010:9010 -p 9020:9020 gcr.io/cloud-spanner-emulator/emulator

设置 SPANNER_EMULATOR_HOST=localhost:9010,并在运行应用前创建实例和数据库:

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。