遷移、表鎖與診斷
加欄、改型別、刪欄、產生大量測試資料或診斷大表,可能需要在背景掃描資料列。遷移 API 把「這張表現在是否能接受 schema 寫入」與「某一個背景工作的進度」分成兩個狀態,前端要分別追蹤。
什麼時候會出現 migration
Schema endpoint 的成功回應若含 background_migration: true 與 migration_id,表示 schema 變更已受理,但資料回填仍在背景進行。純量欄位變更通常在小表同步處理,超過門檻才背景化;計算欄多半是 metadata-only。不要把固定筆數寫死成 UI 判斷,應以回應為準。
遷移期間伺服器會鎖表,避免另一個 schema/大量操作與它交錯。衝突請求回 423 Locked。此時不要盲目重送原 mutation;先查 table lock 與已知 migration。
欄位更新與轉型
PATCH .../columns/{column_id} 並不代表每個欄位都可修改。link、rollup、lookup 或 formula 欄只能修改 name 與 description;關係、彙總、lookup、pick 與 expression 都是建立時設定。欄位也不能轉成或轉離這四種計算型別。若要改設定,必須刪除並重建。
Stored 欄位轉型在遷移開始前有以下 preflight gates:
- 轉成
select或multi_select時,同一個請求必須提供完整options清單。 - 必填欄必須提供新的、與新型別相容的
default_value。選填欄若沒有提供新預設值,schema 預設值會被清除,無法轉換的 cell 會退回null。 - 把
required從 false 改為 true 會掃描 live rows;任何 cell 缺少或為 JSONnull都會讓更新失敗。Preflight 不會把""當成空值,雖然後續 record write 會拒絕明確寫入空字串到必填欄;client 應先清理既有空字串。 - 被 link/rollup/lookup/formula 相依引用的欄位一律不能轉型,唯一例外是
integer⇄float。這也會擋看似相近的string⇄text;有損數值轉換仍會針對適用的unique規則預先檢查碰撞。 - 舊型別專用設定會被移除,例如離開 select 會移除
options,離開 attachment 會移除附件限制。
既有 cell 轉換矩陣
轉型不會無條件把每個值替換成新預設值。伺服器會逐一盡力轉換既有且已存在的 cell;只有無法轉換的值才退回新的 default_value,未提供時則為 null。既有 null 與 "" 會保持空值,不會被預設值填滿。同步與背景路徑使用同一套規則。
| 舊型別 | 新型別 | 既有 cell 結果 |
|---|---|---|
integer ⇄ float | 另一個數值型別 | 進行數值轉換;integer→float 可能失去精度,float→integer 會先四捨五入,且必須落在 signed 64-bit 範圍。 |
string / text | integer / float | 解析 trim 後的數值文字;無效、非有限或超出範圍時退回。 |
integer / float / boolean | string / text | 使用伺服器的字串表示;布林會變成 "True" 或 "False"。 |
string ⇄ text | 另一個文字型別 | 保留字串值。 |
date | datetime | 附加 00:00:00;這個僅遷移會產生的含秒形狀與一般 datetime 驗證衝突,見下方。 |
datetime | date | 保留空白或 T 之前的日期部分。 |
string / text | json | 把 trim 後的字串解析成 JSON,並套用 JSON byte、depth、node 與有限數字限制;無效時退回。 |
integer / float / boolean | json | 保留純量,作為合法 JSON 值。 |
json | string / text | 以 object key 排序後序列化;若超過新的明確 max_length 則退回。 |
| 任意型別 | 轉成或轉離 attachment | 一律退回;blob ID 不會跨欄位型別轉換。 |
| 任意型別 | 轉成或轉離 principal / user / social_client | 一律退回;schema 遷移不會重新驗證不透明的租戶身分。這包含 user ⇄ social_client 與 user ⇄ principal——沒有任何轉型會把裸 id 加上前綴變成帶標籤的 cell。 |
select、multi_select 或上表未列出的任何組合 | 另一個 stored 型別 | 退回新預設值,未提供時為 null。 |
已知後端不一致 一般紀錄寫入與預設值只接受不含秒的
YYYY-MM-DD HH:mm。但 date→datetime 遷移會寫入YYYY-MM-DD 00:00:00,所以前端可能讀到一個無法原樣寫回的含秒值。請把它視為遷移輸出,而不是新支援的輸入格式;後續寫入前先正規化到分鐘。
Select 選項改名是另一種遷移。option_renames 會改寫 live 與 soft-deleted records 的相符值、同步更新相符的 select 預設值,並重建受影響的 unique-rule entries。同一個請求不能同時使用 option_renames 與完整 options replacement。
Table migration status:回答「表是否被鎖」
這條路徑仍是一般 scoped table path:
GET /private/module/custom_tables/{scope}/.../tables/{table_id}/migration/status聊天室與部門版本的 {scope} 仍包含自己的 scope ID。回應重點是:
{
"table_id": "22222222-2222-4222-8222-222222222222",
"is_locked": true,
"lock_info": {
"operation": "add_column",
"acquired_at": "2026-07-19T02:20:00Z",
"expires_at": "2026-07-19T02:30:00Z"
},
"record_count": 15000
}它適合在表設定頁決定是否 disable schema controls,也能在只收到 423、手上沒有 migration ID 時診斷目前 lock。is_locked: false 才表示可以重新整理 schema 並考慮重試使用者動作。
Migration status:回答「工作做到哪裡」
背景工作用 migration_id 輪詢:
GET /private/module/custom_tables/{scopeName}/migrations/{migration_id}/status這是 id-less path:scopeName 只填 chatroom、department 或 company,後面沒有 chatroom_id、department_id 或 table_id。例如:
GET /private/module/custom_tables/department/migrations/88888888-8888-4888-8888-888888888888/statusMigration IDs 在全系統全域唯一。伺服器會由 migration ID 找到所屬的表,並依該表真實的 scope 做授權;只有 company 前綴的路由會在表不屬於 company 時額外回 404 — chatroom/department 前綴的路由則不比對 URL 前綴,只看你對該表的存取權。全域唯一不代表只要登入就能讀別人的進度。
回應提供 operation、status、progress、total_records、migrated_records 與失敗時的 error_message。常見狀態是 pending、running、completed、failed、cancelled。只有 completed 才刷新 schema/records;failed/cancelled 應停止 polling 並呈現伺服器訊息。
Diagnosis:找 schema、資料與效能問題
診斷從一般 scoped table route 啟動:
POST .../tables/{table_id}/diagnosis它檢查有效/無效資料列、validation errors、schema issues、data-integrity issues 與 performance recommendations。聊天室與部門需要 table moderator;公司範圍只要求 table access,這是不能從相同 path tail 推論 auth 的例子。
小表可能同步回 diagnosis_results。超過背景門檻時回 background_processing: true 與 migration ID;先輪詢一般 migration status,完成後再取:
GET /private/module/custom_tables/{scopeName}/migrations/{migration_id}/diagnosis-resultsDiagnosis results 路徑與 status 一樣沒有 scope ID/table ID,而且只在背景診斷完成且結果仍可取得時使用。若啟動時表已被其他遷移鎖住,會回 423,不會排入另一個互相衝突的診斷。
建議的前端狀態機
- Schema/diagnosis 回同步結果:立即刷新並結束。
- 回
migration_id:保存 ID、scope name、table ID 與 operation,畫進度並輪詢 job status。 - 只收到
423:查 table migration status,顯示lock_info.operation。 - Job
completed:若是 diagnosis,再取 diagnosis-results;否則重新抓 table/schema。 - Job
failed/cancelled:停止重試,顯示error_message,保留診斷資訊供支援使用。
不要只把 polling state 放在單一 React component;重新整理後應能從保存的 migration ID 恢復,或至少用 table lock endpoint 誠實顯示維護中。
所有狀態 response 與各 scope auth 見生命週期參考。欄位變更如何產生 migration 見欄位型別;可在 API Playground檢查 id-less URL 展開結果。