Skip to Content
操作指南原子資料集發布

原子資料集發布

Publish session 是給「整份資料必須一次對讀者可見」的批次通道。Moderator 先開啟 session,把一個或多個經驗證的 chunk 暫存到所有 record read 都看不到的區域,再派送 commit。Commit 會在同一個資料庫 transaction 裡軟刪除選填的 replace 集合,並新增所有 staged rows:不是完整新資料集一次落地,就是讀者繼續看到完整舊資料集。

這不是 bulk-insert、bulk-update、bulk-delete 的部分成功語意。三種保證的並排比較見批次操作。

Scope 與權限

Publish session 只存在於 department 與 company table:

ScopeBase path
Department/private/module/custom_tables/department/{department_id}/tables/{table_id}/publish-sessions
Company/private/module/custom_tables/company/tables/{table_id}/publish-sessions

沒有 chatroom 路由。每個操作都要求目前仍具 table moderator 權限。開啟 session 的 moderator 擁有變更權:只有他能 stage chunk、commit 或 abort;任何目前的 moderator 都能讀取 status,讓共同管理者看出是誰占用 active publish slot,以及 commit 為何失敗。

資料表帶有 require_approval 或 channel 規則時不能開啟 session;write_policy 為 commands_only 時,則以 403 governed_context_required(operation: "publish"、surface: "publish_session")拒絕。Publish commit 無法先 hold 等待日後覆核,而 background worker 也沒有 acting chatroom 可通過 channel floor。

1. 開啟唯一 session

對 department 或 company base path 送出相同 payload:

{ "idempotency_key": "catalog-2026-08-20", "replace_filter": [ { "column": "col_55555555_5555_4555_8555_555555555555", "op": "eq", "value": "current" } ] }

兩個欄位都可省略。replace_filter 省略或為空時是 append-only publish;提供時最多包含 20 個已儲存欄位條件,並使用內部欄位鍵。Server 會在開啟時立即編譯,因此未知或 computed column 會在任何資料暫存前就失敗。Chunk 暫存時會把每一列對照存活資料列,而不是對照 replace 的結果:與「這次 replace 會刪除的資料列」衝突的資料列——相同的 unique 值,或同一分割內重疊的 date_interval_exclusion 期間——會在暫存時以 400 invalid_rows 被拒絕。請把這樣的資料列改放到空閒的值或區間,或先更動存活資料列。

idempotency_key 最長 128 字,空字串視同省略。對同一張表與同一位 actor,只要原 session 尚未過期,且狀態為 open、committing 或 committed,以同一 key 重送就會回傳原 session 並標記 replayed: true,不會再開一個。已 aborted 的 session 即使原 expiry 尚未到,也不會被重播。

其他任何 top-level key、超長的 key,或超過 20 個 predicate,都會在儲存任何資料前以 422 拒絕。開啟時若表格不在路由的 scope 內,會回 404 Table not found in this department(或 company);schema 操作正持有 table lock 時則回 423 Locked(見遷移)。

{ "session_id": "33333333-3333-4333-8333-333333333333", "expires_at": "2026-08-21T00:00:00Z", "replayed": false }

硬性到期時間是建立後 24 小時。到期採惰性處理:到了 expires_at 本身不會發生任何事。下一個碰到這個 session 的請求——讀取 status、chunk、commit 或 abort,或同一張表的另一次開啟——才會把它標為 expired、清掉已 staged 的資料列並釋放 slot。從 expires_at 起,同一個 idempotency key 不會再重播它。每張表同一時間只能有一個 open 或 committing session;在 owner abort、session 過期,或 commit 完成釋放 slot 之前,第二次開啟會收到 409 session_conflict。

2. 暫存可安全重試的 chunks

每個 chunk 送到:

POST {base}/publish-sessions/{session_id}/chunks
{ "seq": 0, "records": [ { "訂單編號": "A-1001", "狀態": "current" }, { "訂單編號": "A-1002", "狀態": "current" } ] }

Chunk contract 有明確上限:

  • seq 是 0 到 63 的整數,因此一個 session 最多有 64 個 chunk slot。超出範圍的 seq、空的 records 清單或未知的 top-level key 都是 422。
  • 每個 chunk 可放 1–5,000 列;整個 session 的所有 sequence slots 合計最多 5,000 staged rows。
  • 每列使用一般 create body shape:顯示名稱或內部鍵,以及單筆建立路徑接受的 link 與 attachment shape。不要送 id。Validator 內部可能暫用一個 ID,但真實 record ID 只由 commit 的 create path 產生。
  • 重送既有 seq 會原子取代該整個 chunk。遇到回應不明時請重用同一 sequence number,不要以新號碼附加一份重複資料。
  • Schema validation 與 advisory rule precheck 會檢查完整 chunk;任一列失敗,整個 chunk 都拒絕,回傳 zero-based row_index errors——最多列出前 100 筆失敗資料列,error_count 仍然精確——原本該 slot 的內容保持不變。
  • 每一列也必須低於單筆 record 大小上限(Record data is <n> bytes; the limit is <limit> bytes),且不得內嵌二進位內容;兩者都以 row error 回報。
  • 驗證一次只看一個 chunk。Rule precheck 會拿一列與 live records 比較,並且——僅限 unique 與 unique_tuple 的 key——與同一個 chunk 的其他列比較,絕不與其他 seq 已 staged 的列比較,因此拆在兩個 chunk 裡的重複 unique key 會通過暫存,要到 commit 才以 rule_violation 失敗,並標出該列的 seq 與 row_index。其他每一種 rule(no_overlap、count_limit、exists/not_exists、date_interval_exclusion)都只讀 live 資料列,所以彼此衝突的暫存列都會通過暫存,並以同樣方式在 commit 時失敗。
  • 對其他表格的 session 做 chunk、commit、abort 或讀取 status,會回 404 Publish session not found on this table。
  • Chunk、commit 或 abort 碰到已超過 24 小時期限的 session 時,會當場讓它過期,並回 409 {"error": "session_expired", "message": "Publish session expired; open a new session", "session_id": …};已 staged 的資料列會被清掉。

接受的列只存在 staging table。一般 record list、search、saved view、public read、callback 與 agent tool 都看不到。成功回應的 accepted count 與 staged_total 只代表已暫存,不代表已發布。

3. 派送原子 commit

確認 staged_count 後呼叫:

POST {base}/publish-sessions/{session_id}/commit

Session 必須是 open,且至少已有一列;table lock 會回 423 Locked,session 仍保持 open。選填的 worker query parameter 用來選擇 lane——legacy、batch、sqs 或 pubsub——預設為部署的 DEFAULT_BACKGROUND_WORKER_TYPE(該變數未設定或無效時為 legacy)。成功回應是非同步派送:

{ "ticket_id": "ct-publish-44444444-4444-4444-8444-444444444444", "replayed": false }

這不代表 commit 已完成。Session 已是 committing 或 committed 時重試,只會回既有 ticket 並標記 replayed: true,絕不重複派送同一次 commit。

提交給 batch、sqs 或 pubsub worker 時若拋出錯誤,呼叫會回 500,detail 為 Failed to submit task to AWS Batch: … 或 Failed to submit task to SQS-ECS: …,ticket 也會寫成 failed。這次提交仍可能已送進佇列,所以 session 會刻意停在 committing:重試 commit 會重播那張 failed ticket,abort 則以 session_not_open 拒絕,直到 24 小時期限過後,下一個請求才會讓 session 過期。提交開始前的失敗則會重新開啟 session。決定下一步之前,請先讀 session status。

Worker 會在一個 transaction 內完成整次 flip:

  1. 重新載入並鎖住 session 與 table,重新檢查 opener 現在的權限。
  2. 若有 replace_filter,將它與 opener 目前的 row ACL 及 scope floor 以 AND 合併;不在授權集合內的列永遠不會成為刪除目標。
  3. Live replacement set 最多 5,000 列。更廣的結果會以 replace_target_cap_exceeded 失敗;請縮小 filter 或拆分發布。
  4. 經由真正的 delete core 軟刪除所有獲授權 replacement rows。
  5. 經由真正的 create core 新增全部 staged rows,包括 schema(因此 decimal cell 會以正規形式儲存)、rules(含 same_dimension_reference 完整性檢查與 date_interval_exclusion 期間)、links、attachments 與 scope enforcement。
  6. 只 commit 一次,接著清掉 staging 並把 session 標為 committed。

最後 commit 前的任何失敗都會回滾完整 flip。讀者永遠看不到刪到一半或新增到一半的資料集。

同時輪詢兩個來源

以 generic task ticket 追蹤 worker 進度:

GET /public/task/{ticket_id}

並同時讀取 session:

GET {base}/publish-sessions/{session_id}

Generic ticket 告訴你 background work 是 submitting、processing、completed 或 failed。它一開始為 processing(legacy)或 submitting(其他 lane),execution_environment 為 fastapi、aws_batch 或 sqs_ecs,並帶有 progress、total_records、inserted_records、deleted_records 與 current_step,每新增 500 列改寫一次;新增的列在最後唯一一次 commit 前都看不到。completed 結束時 progress 為 100。failed 帶 progress 0、結構化的 error 與 error_message。Ticket 在最後一次寫入後保留 24 小時,請在那之前讀取結果。只有 commit 該 session 的使用者能輪詢這張 ticket;其他呼叫者得到 404 {"detail": "Task not found"},與未知 id 的回應相同(見誰可以輪詢哪種 ticket)。Session 則提供 publish lifecycle(open、committing、committed、aborted、expired)、staged count、到期時間、ticket、replace filter 與最近一次 structured commit error。共同 moderator 可以讀這份 status,但不能變更 session。

在不暴露部分資料的前提下重試或放棄

可重試的 validation、row、link、permission 或 rule 失敗會回滾 transaction、記錄 structured error、讓 session 回到 open,並保留所有 chunks。請以相同 seq 重送來修正 staged chunk,或修好 live dependency,再次 commit;新的 attempt 會得到新 ticket。不要只看 generic ticket failed 就認定 session 已中止,必須讀 session status。

Session 的 error.error_code 會告訴你屬於哪一種情況:

error_code之後的 session
table_not_found(表格已刪除)、session_scope_mismatch(表格已換公司)aborted;清掉 staging
session_expired(worker 執行前已過期)expired;清掉 staging
actor_not_authorized(開啟者已不是 moderator 或管理者)open;保留 staging,直到 owner 恢復權限或 abort
approval_conflict、channel_rule_conflict(session 開啟後才加上 require_approval 或 channel rule)open;移除該 rule 或 abort
governed_context_required(資料表在 session 開啟後才變成 commands_only)open;把 write_policy 改回 any 或 abort
replace_target_cap_exceeded(附 matched 與 limit)open;縮小 filter 或拆分發布
rule_violation、permission_denied、validation_error、commit_failedopen;修正該列或 live dependency 後再次 commit

由某一筆 staged 列造成的失敗,會在 error 中加上該列的 seq 與 row_index,message 最多 2,000 字元。Session 一旦 committed,就沒有 publish-session rollback;被軟刪除的舊列改走一般 record history 與 restore 語意。

若要放棄尚未 commit 的發布,由 owner 呼叫:

POST {base}/publish-sessions/{session_id}/abort

只有 open session 可以 abort。它會刪除所有 staged rows 並釋放 active slot;已進入 committing 的 commit 不能在這裡取消,已完成的 commit 也不能靠 abort 還原。

Client checklist

  1. 只在 department/company table 且確實需要一次可見時使用這條 lane。
  2. 將 open 回應的 session_id、expires_at 與穩定 idempotency key 一起保存。
  3. 以 deterministic 方式編號 chunks;回應不明時用相同 seq 重試。
  4. 絕不提供 record IDs,讓 commit 產生它們。
  5. Commit 前先確認 status 的 staged_count。
  6. 把 commit 200 視為派送,接著同時輪詢 task 與 session。
  7. 可重試失敗時修復或取代問題 chunk 再 commit;確定放棄時趁 session 仍 open 執行 abort。

Endpoint 卡片與精確 request/response models 見批次操作參考。

Last updated on