Skip to Content
參考資料資料庫模型

資料庫模型

下列五張表都已註冊到 ORM metadata。其中四張(edge_workers、edge_worker_versions、edge_worker_outbox、edge_worker_audit_logs)自後端 5.10.11 起,欄位、索引、CHECK 與外鍵都沒有變動;edge_worker_maintenance 則是新增的。有變動的是寫入既有欄位的值集合:見下方 自 5.10.11 起新增的儲存值。

結構佈署:沒有 alembic migrations

alembic/versions/ 刻意不含這些表的任何 revision(任何其他東西的也沒有 — 在後端 5.22.0,該目錄仍只有一個 dummy 檔,alembic/ 底下也沒有任何東西提到 edge-worker 表)。結構經由 ORM metadata 出貨:Base.metadata.create_all 只建立缺少的表、永不 ALTER,edge_worker_maintenance 也是這樣建立的。模型檔自己的 docstring 就寫明政策:只編輯這個 ORM 檔;永遠不要手寫 alembic/versions revision。

實務後果:

  • 除主鍵外,每個 NOT NULL 的非 TEXT 欄位都帶 server_default(字面值,時間戳記則是 now()),滾動的 hotfix 才能對線上表加欄位。
  • TEXT/BLOB 欄位(sbom、build_receipt、error_message)沒有字面 server default — MySQL 無法給 TEXT 預設值。它們可為 null 純粹是為了混版滾動佈署安全;V2 對每個 ready 修訂版都要求兩份證據文件。

不要跑 alembic upgrade head 期待出現 edge-worker 表 — 這套結構不是那樣佈署的。

edge_workers — 部門單例

一個部門擁有的單一 Worker 的穩定身分(每部門每環境一列)。

欄位型別Null預設備註
idString(36)PK—有索引、唯一
company_idString(36)否""FK companies.id ON DELETE CASCADE,索引
department_idString(36)否""FK departments.id ON DELETE CASCADE,索引
script_nameString(63)否""推導(見下),絕非請求輸入
statusString(16)否"draft"CHECK:draft / active / paused
latest_revisionInteger否0每次插入修訂版前遞增,所以編號從 1 開始
deployment_sequenceBigInteger否0單調的准入配發器
current_version_idString(36)可—沒有 FK
created_by_user_idString(36)否""
created_at / updated_atDateTime否now()updated_at 另有 onupdate=now()

約束:

  • uq_edge_worker_department — UNIQUE(department_id)。部門單例不變量;資料庫是併發首次提交的最終權威。
  • uq_edge_worker_script_name — UNIQUE(script_name)。全域唯一的 Cloudflare script 名稱。
  • ix_edge_worker_company_department — INDEX(company_id, department_id),雙錨定租戶讀取。
  • ck_edge_worker_status — CHECK status IN ('draft','active','paused')。

script_name 是 "dw-" + sha256("edge-worker-v2\0{company_id}\0{department_id}")[:48] — 63 字元欄位裡的 51 字元,不透明(兩個 id 都不洩漏)、改名不變。

edge_worker_versions — 不可變修訂版

不可變的 V2 專案/建置修訂版,加上可變的部署證據。

欄位型別Null預設備註
idString(36)PK—
company_id / department_idString(36)否""索引,無 FK
worker_idString(36)否""FK edge_workers.id CASCADE,索引
revisionInteger否1
languageString(16)否"javascript"CHECK:javascript / typescript
module_name / module_versionString(64)否""manifest 身分
entrypointString(256)否""
compatibility_dateString(10)否""
manifestJSON可—僅正規化的已驗證 manifest;壓縮檔/bundle 在 R2
manifest_digest / contract_digestString(64)否""
project_object_keyString(1024)否""R2 key
project_digestString(64)否""
project_size_bytesInteger否0
lock_digest / dependency_digestString(64)否""
build_policy_versionString(64)否""此修訂版建置時所用的 builder policy(新提交:department-worker-project-v2.10);部署規則以數值方式比較它,見版本與部署
bundle_object_keyString(1024)否""R2 key
bundle_digest / sbom_digest / build_receipt_digestString(64)否""
sbomTEXT(MySQL 上 MEDIUMTEXT)可—builder 允許到 512 KiB;64 KiB 的 TEXT 不夠
build_receiptTEXT(MySQL 上 MEDIUMTEXT)可—
stateString(24)否"uploading"見下方 CHECK
error_codeString(64)否""
error_messageText可—
created_by_user_idString(36)否""
deployed_atDateTime可—
created_at / updated_atDateTime否now()

約束:

  • uq_edge_worker_version_revision — UNIQUE(worker_id, revision):不可變修訂版階梯。
  • ix_edge_worker_version_tenant — INDEX(company_id, department_id, worker_id)。
  • ix_edge_worker_version_state — INDEX(company_id, department_id, state)。
  • ck_edge_worker_version_language — CHECK language IN ('javascript','typescript')。
  • ck_edge_worker_version_state — CHECK state IN ('uploading','ready','deploy_pending','deploying','deployed','superseded','failed','uncertain')。

uploading 預設是 fail-closed 哨兵值,不是公開提交階段:V2 路由一律以 ready 插入修訂版,任何忘記設 state 的插入路徑都會產生不可部署的列。狀態轉換見版本與部署。

edge_worker_outbox — 耐久意圖與公開票證

僅存純量的耐久部署、控制面稽核與清理意圖。ticket_id 就是 GET /worker/deployments/{ticket_id} 所輪詢的對象。

欄位型別Null預設備註
idString(36)PK—
ticket_idString(36)否""索引、唯一
company_id / department_id / worker_id / version_idString(36)否""索引,無 FK
dispatch_namespaceString(64)否""索引;該列的 provider 部署範圍:雲端是 Worker 已記錄之服務位置的 namespace(Worker 的首次部署與每一筆產物清理意圖,預設取該環境的 EDGE_WORKERS_CLOUDFLARE_DISPATCH_NAMESPACE),地端是 onprem:<12 hex>。雲端的回收掃描認領共用位置加上自己環境的舊有位置,不再認領舊有的 "" 列(沒有可用的範圍設定時,什麼都不認領);地端的掃描只認領自己的 onprem:<12 hex> 範圍,絕不認領 "";屬於另一個環境之舊有位置的列,留給該環境處理。見背景任務
operationString(16)否"deploy"CHECK:deploy / delete / artifact_cleanup / control_audit
dedupe_keyString(256)否""部署列:deploy:{worker_id}:{version_id}:{base_revision},部署請求帶有 operation_settings 時會再附加 :{operation_settings_digest}
admission_sequenceBigInteger否0准入時從 worker.deployment_sequence 複製
active_deployment_worker_idString(36),生成且持久化可—CASE WHEN operation='deploy' AND status IN ('pending','leased','uncertain') THEN worker_id ELSE NULL END
statusString(16)否"pending"CHECK:pending / leased / completed / failed / cancelled / uncertain
attemptsInteger否0
lease_ownerString(128)否""
lease_expires_at / next_attempt_at / completed_atDateTime可—
last_error_codeString(64)否""
last_errorString(1024)否""
payloadJSON可—僅純量的任務參數(部署 payload 帶有 operation_settings_digest,沒有設定時為 null,在雲端還帶有一筆伺服端擁有、記錄服務該 Worker 之位置的紀錄,沒有任何路由會回傳它);絕不含機密值、API 金鑰或服務帳號密碼
created_at / updated_atDateTime否now()

約束:

  • uq_edge_worker_outbox_dedupe — UNIQUE(operation, dedupe_key):冪等的意圖准入。
  • uq_edge_worker_outbox_ticket — UNIQUE(ticket_id):公開票證身分。
  • uq_edge_worker_outbox_active_deployment_worker — UNIQUE(active_deployment_worker_id):每個 Worker 至多一個存活部署。因 MySQL 唯一索引允許多個 NULL,作用等同部分唯一索引;已結算的列生成欄位為 NULL。
  • ix_edge_worker_outbox_pending — INDEX(status, next_attempt_at):worker 迴圈掃描。
  • ix_edge_worker_outbox_tenant — INDEX(company_id, department_id, status)。
  • ix_edge_worker_outbox_worker_operation_sequence — INDEX(worker_id, operation, admission_sequence):最新部署查詢。
  • ck_edge_worker_outbox_operation — CHECK operation IN ('deploy','delete','artifact_cleanup','control_audit')。
  • ck_edge_worker_outbox_status — CHECK status IN ('pending','leased','completed','failed','cancelled','uncertain')。

edge_worker_maintenance — resync epoch 與備份圍欄(地端)

每個部署範圍各有一份耐久的 resync epoch 與備份啟用圍欄。主鍵就是範圍,即 onprem:<12 hex>。地端 provider 會讀寫它(維護與還原閘門、備份圍欄、resync;見地端)。在雲端,這張表同其他表一樣由 create_all 建立,但從不被讀取:雲端閘門恆為開啟。

欄位型別Null預設備註
scopeString(64)PK—地端部署範圍
stateString(16)否"idle"CHECK:idle / active
epochString(64)否""
started_byString(64)否""
started_atDateTime可—
activation_fence_untilDateTime可—
edge_data_holdBoolean否false還原保護
updated_atDateTime否now()另有 onupdate=now()

約束:ck_edge_worker_maintenance_state — CHECK state IN ('idle','active')。除主鍵外,沒有其他索引、沒有 FK、沒有唯一約束。地端若缺少該列,會失敗即關閉:部署與修復回 409 edge_workers_maintenance。

edge_worker_audit_logs — 只追加的事實

只追加、有界的稽核事實;永不含原始碼、bundle 或憑證(只存摘要:機密值、API 金鑰或服務帳號密碼絕不寫入,只寫入它的加鹽或未加鹽 SHA-256 摘要)。寫什麼、何時寫見稽核。

欄位型別Null預設
idString(36)PK—
company_id / department_idString(36)否""(索引)
actor_typeString(32)否""
actor_idString(36)否""
auth_methodString(32)否""
actionString(128)否""
resource_typeString(64)否""
resource_idString(36)否""
resultString(32)否"success"
reason_codeString(64)否""
digestString(64)否""
created_atDateTime否now()

索引:ix_edge_worker_audit_tenant_created(company_id, department_id, created_at)與 ix_edge_worker_audit_resource(resource_type, resource_id)。沒有唯一約束、沒有 FK、沒有 updated_at、沒有 CHECK — 冪等性(以 audit_id)只存在於應用層寫入器,它會截斷 actor_type(32)、auth_method(32)、action(128)、resource_type(64)、result(32)、reason_code(64)、digest(64),但不截斷 id 欄位。

自 5.10.11 起新增的儲存值

沒有任何欄位改變,但這些值現在會出現在既有欄位中。

欄位新值
edge_worker_audit_logs.action操作設定、Worker 機密與服務帳號的動作,退役確認 department.worker_retired / department.worker_script_neutralized / department.worker_script_deleted / department.worker_tombstone_deleted(其 reason_code 為 dispatch namespace),secrets.purged_before_legacy_deploy,service_account.*;地端另有 edge_worker.resync。完整清單:稽核
edge_worker_outbox.last_error_codedepartment_deleted、edge_workers_provider_invalid、legacy_serving_target_unavailable(部署列、被復原輪判為 failed 的控制面稽核列,以及控制面稽核的回執確認)、deployment_target_unavailable、deployment_target_release_changed、worker_deleted(Worker 列已不存在的部署列)、worker_retired(因其 Worker 已退役而結算的控制面稽核列);地端另有 edge_host_fenced、其他 edge_host_* 代碼、edge_worker_deleted、edge_host_resync_failed
edge_worker_versions.error_codedepartment_deleted、edge_workers_provider_invalid、legacy_serving_target_unavailable、deployment_target_unavailable、deployment_target_release_changed;地端另有 edge_host_fenced(error_message 為 activation deferred by backup fence)、edge_host_capacity_exhausted(以 host 的固定指引文字作為 error_message;其他所有終態部署失敗則存 deployment failed)、resync 代碼(error_message 為 deployment failed during Edge Worker resync,或 ... resync abort)
edge_worker_outbox.dispatch_namespace地端 onprem:<12 hex>
edge_worker_outbox.dedupe_key部署的 dedupe key 可能以 :<operation_settings_digest> 結尾

值得知道的怪癖

  • 非 null String 欄位的通用哨兵是空字串而非 NULL;API 層在讀取時把 "" 轉成 None。
  • FK 只存在於 edge_workers(company/department)與 edge_worker_versions.worker_id。outbox、audit 與 maintenance 列沒有 FK;edge_workers.current_version_id 也沒有。
  • 刪除部門不會刪除任何列。部門只是軟刪除,因此不會級聯:edge_workers、修訂版、outbox 與 audit 列都保留,雲端 script 則另行退役(見退役)。公司的根層級硬刪除會移除部門列,這會級聯到 edge_workers,並透過 edge_worker_versions.worker_id 再級聯到修訂版;outbox 與 audit 列則留存,是這個資料庫部署過哪些 script 的唯一證據,孤兒調解會用到它。Worker 列已不存在的部署列,會以 worker_deleted 結算為 failed。
  • outbox 的 delete operation 在 CHECK 裡但沒有任何存活程式路徑(沒有 EVENT_* 常數)— 純向前相容。
  • 公司管理的「最新部署」排序最後以原始字串 id 決勝,而 admission_sequence = 0 的舊列刻意排在有序號的之後。
  • CRUD 寫入 helper(create_submission、write_audit)flush 但永不 commit — 交易與 R2 補償編排由路由擁有。
Last updated on