跳至主要內容

Google Cloud Spanner 儲存空間

Google Cloud Spanner 儲存空間實作為 Mastra 提供可水平擴充容量、具備強一致性的儲存 backend。它以 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 的 credential 需要執行 schema 變更的權限(或在部署步驟中使用較高權限的 credential 執行一次 storage.init())。

參數
「參數」的直接連結

id:

string
此儲存空間執行個體的唯一識別碼。

projectId?:

string
Google Cloud 專案 ID。除非提供 database,否則為必填。

instanceId?:

string
Cloud Spanner 執行個體 ID。除非提供 database,否則為必填。

databaseId?:

string
Cloud Spanner 資料庫 ID。除非提供 database,否則為必填。

database?:

@google-cloud/spanner Database
預先設定的 Spanner Database handle。在其他位置管理 Spanner client 時使用(例如跨服務共用驗證或連線選項)。

spannerOptions?:

object
轉送至 @google-cloud/spanner client 建構函式的選項。可用來設定 credential、自訂 endpoint,或指向本機 emulator。

disableInit?:

boolean
= false
設為 true 時,會略過首次使用時自動建立資料表。你必須在個別部署步驟中明確呼叫 storage.init()。

skipDefaultIndexes?:

boolean
= false
設為 true 時,略過初始化期間建立預設索引。

indexes?:

CreateIndexOptions[]
要建立的自訂 secondary index。每個索引都必須指定所屬資料表。索引會根據資料表名稱路由至適當的 domain。

initMode?:

'sync' | 'validate'
= 'sync'
控制 schema 初始化行為。'sync' 會在 init() 期間建立缺少的資料表、欄位與索引(既有行為)。'validate' 不會發出 DDL,而是確認每個預期的資料表、欄位與預設/自訂索引都已存在;若有任何缺漏,就擲回有型別的使用者錯誤。當外部處理程序(Terraform、Liquibase、release 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 管理」的直接連結

儲存 adapter 會建立下列資料表,全部使用 GoogleSQL dialect:

  • mastra_workflow_snapshot:Workflow 狀態與執行資料
  • mastra_threads:對話 thread
  • mastra_messages:個別訊息
  • mastra_resources:資源 working memory
  • mastra_scorers:評估分數
  • mastra_background_tasks:背景 Tool 執行狀態
  • mastra_agents:精簡 Agent 記錄(id、狀態、作用中版本)
  • mastra_agent_versions:版本化 Agent 設定 snapshot
  • 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 snapshot(指示、參考資料、script、asset、內容樹)
  • mastra_skill_blobs:以 SHA-256 hash 作為 key 的 content-addressable blob store,用於 Skill 版本內容
  • mastra_prompt_blocks / mastra_prompt_block_versions:prompt block 記錄與版本化內容 snapshot(範本內容、規則、request-context schema)
  • mastra_scorer_definitions / mastra_scorer_definition_versions:scorer 定義記錄與版本化設定 snapshot(judge 指示、model、分數範圍、預設設定、預設 sampling)
  • mastra_schedules / mastra_schedule_triggers:由 cron 驅動的 Workflow 排程與觸發歷程,供 Mastra 內建 WorkflowScheduler 使用
  • mastra_workspaces / mastra_workspace_versions:Workspace 記錄與版本化設定 snapshot(檔案系統、Sandbox、mount、搜尋、Skill、Tool)
  • mastra_datasets / mastra_dataset_items / mastra_dataset_versions:評估 dataset、SCD-2 版本化項目與版本 snapshot
  • mastra_experiments / mastra_experiment_results:實驗執行及其各項目結果
  • mastra_favorites:各使用者的 Agent 與 Skill 收藏項目,並在 parent 記錄上維護 denormalized favoriteCount
  • mastra_channel_installations / mastra_channel_config:多平台 channel installation 與各平台設定
  • mastra_ai_spans:用於可觀測性的 AI tracing span(各 Trace 與各 span 記錄,用來支援 Studio Trace UI)

建立資料表時,文字與 JSON payload 使用 STRING(MAX),另使用 INT64、FLOAT64、BOOL 與 TIMESTAMP。

下列資料表具有 Spanner 專用的 STORED generated column,adapter 會從 JSON payload 填入內容,讓常見 filter 可使用一般 secondary index,不必進行 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 會在 runtime fallback 至 JSON_VALUE filter。

Adapter 不會建立或使用 schema。請使用專用資料庫進行隔離。

初始化
「初始化」的直接連結

將 storage 傳入 Mastra 類別時,系統會在任何 storage 操作之前自動呼叫 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() 會依序執行各 domain 的設定。

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,因此需要寫入後狀態的呼叫端必須重新讀取。
  • 沒有 TRUNCATE。dangerouslyClearAll() 會發出 DELETE WHERE TRUE。
  • 識別碼使用反引號括住。
  • DDL 透過 database.updateSchema(...) 套用,這是非同步操作(長時間執行的操作)。
  • 不支援 NULLS FIRST/LAST。使用 IS NULL 排序 key 模擬包含 NULL 處理的排序。
  • 不原生支援 JSON containment。listTraces 的 metadata 與 scope filter 會編譯為各 key 的 JSON_VALUE(...) = @v 相等檢查,而 tags filter 則編譯為對 JSON_QUERY_ARRAY(...) 使用 EXISTS。這與 Postgres 的 @> containment operator 不同(後者可在單次 index scan 中比對巢狀結構):大多數單次查詢仍可運作,但無法表達深層巢狀結構比對。

直接存取資料庫
「直接存取資料庫」的直接連結

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 等進階情境。直接重複使用資料庫時,會略過 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 處理程序中設定相同環境變數並連線。@google-cloud/spanner client 會自動偵測 emulator。