> Discover all available pages from the documentation index: https://mastra.zisheng.pro/zh-TW/llms.txt # Google Cloud Spanner 儲存空間 Google Cloud Spanner 儲存空間實作為 Mastra 提供可水平擴充容量、具備強一致性的儲存 backend。它以 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 的 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`): 設為 true 時,會略過首次使用時自動建立資料表。你必須在個別部署步驟中明確呼叫 storage.init()。 (Default: `false`) **skipDefaultIndexes** (`boolean`): 設為 true 時,略過初始化期間建立預設索引。 (Default: `false`) **indexes** (`CreateIndexOptions[]`): 要建立的自訂 secondary index。每個索引都必須指定所屬資料表。索引會根據資料表名稱路由至適當的 domain。 **initMode** (`'sync' | 'validate'`): 控制 schema 初始化行為。'sync' 會在 init() 期間建立缺少的資料表、欄位與索引(既有行為)。'validate' 不會發出 DDL,而是確認每個預期的資料表、欄位與預設/自訂索引都已存在;若有任何缺漏,就擲回有型別的使用者錯誤。當外部處理程序(Terraform、Liquibase、release 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 管理 儲存 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()`: ```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()` 會依序執行各 domain 的設定。 ```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`。使用 `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 物件: ```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 等進階情境。直接重複使用資料庫時,會略過 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 處理程序中設定相同環境變數並連線。`@google-cloud/spanner` client 會自動偵測 emulator。