跳至主要內容

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),另使用 INT64FLOAT64BOOLTIMESTAMP

下列資料表具有 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,因此需要寫入後狀態的呼叫端必須重新讀取。
  • 沒有 TRUNCATEdangerouslyClearAll() 會發出 DELETE WHERE TRUE
  • 識別碼使用反引號括住。
  • DDL 透過 database.updateSchema(...) 套用,這是非同步操作(長時間執行的操作)。
  • 不支援 NULLS FIRST/LAST。使用 IS NULL 排序 key 模擬包含 NULL 處理的排序。
  • 不原生支援 JSON containment。listTracesmetadatascope 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。