Google Cloud Spanner 存储
Google Cloud Spanner 存储实现为 Mastra 提供水平高容量、强一致性的存储后端。它以 Cloud Spanner 的 GoogleSQL dialect 为目标。
安装安装的直接链接
- npm
- pnpm
- Yarn
- Bun
npm install @mastra/spanner@latest
pnpm add @mastra/spanner@latest
yarn add @mastra/spanner@latest
bun add @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:
projectId?:
database,否则必填。instanceId?:
database,否则必填。databaseId?:
database,否则必填。database?:
spannerOptions?:
@google-cloud/spanner client constructor 的选项。可用它设置凭据、自定义 endpoint,或指向本地 emulator。disableInit?:
storage.init()。skipDefaultIndexes?:
indexes?:
initMode?:
'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 收藏项,父记录上维护非规范化的favoriteCountmastra_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():
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 提供RETURNINGclause,因此需要写入后状态的调用方必须将其读回。 - 不存在
TRUNCATE。dangerouslyClearAll()会发出DELETE WHERE TRUE。 - 标识符使用反引号引用。
- DDL 通过异步的
database.updateSchema(...)(长时间运行操作)应用。 - 不支持
NULLS FIRST/LAST。带 NULL 处理的排序通过IS NULL排序键模拟。 - 原生不支持 JSON containment。
listTraces的metadata和scopefilter 会编译为按键的JSON_VALUE(...) = @v相等性检查,tagsfilter 会编译为对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。