SQLite celld Durable Objects LTX S3 一致性 Ryan Dahl

celld 把 S3 直接當一致性協定:Ryan Dahl 用 LTX ownership epoch 取代 Raft,一顆 SQLite 對一個 Durable Object 的自架成本重畫

Deno 團隊 2026 年 8 月釋出 celld v0.1.0,把 Cloudflare Durable Objects 的模型搬到自架 VM。它沒有 Raft、沒有專屬控制平面,S3 兼容 bucket 同時擔任儲存與協調層,靠 LTX 交易格式加 ownership epoch 做 fencing。本文拆解這個架構為什麼跑得動、它跟 Rivet Actors 的取捨在哪,以及在臺灣 VPS 上部署時該怎麼設計 bucket 與網路。

Ryan Dahl 十七年前寫下 Node.js,五年前把它承認為自己「最後悔的四件事」之一,接著做 Deno;2026 年 8 月他掛名的 Deno 團隊釋出 celld v0.1.0,把 Cloudflare Durable Objects 的整套模型搬回自架 VM。這件事本身的訊號比程式碼更值得注意——一位曾經公開表示「不會再碰 JavaScript runtime」的人,親自跳下來蓋一個能吃 wrangler.jsonc 設定檔的 V8 執行環境,代表他認為 Durable Objects 這個抽象化真的有一般性,而不是 Cloudflare 專屬的東西。

celld 的架構在 self-hosted 這個賽道上是一次很乾淨的答案:V8 執行 JavaScript、每一個 cell 各自一顆 SQLite、LTX 把交易複製到 S3 兼容 bucket、bucket 同時擔任協調層。沒有 Raft、沒有專屬控制平面、沒有失效偵測服務。這篇文章要看的就是這個「S3 直接當一致性協定」的設計為什麼跑得動,以及在臺灣 VPS 上實際部署要處理哪些事。

沒有 Raft 這件事的份量

分散式有狀態系統的預設答案幾乎都是 Raft 或它的變種。etcd、Consul、CockroachDB、TiKV、FoundationDB 全走這條路,理由也很清楚:多節點寫入需要達成共識,共識就得有選舉、有 log replication、有 leader lease。這條路走過三十年,工程上很成熟,但代價是複雜度會滲透到系統每一層——節點數要是奇數、網路分割要靠 quorum 撐、log compaction 要小心處理、成員變更是獨立協定。

celld 選擇的路線是把這一整套踢掉,讓 S3 兼容 bucket 直接承擔協調責任。這條路能成立的技術前提是 S3 API 本身已經演化到能提供 conditional write 語意——PutObject 帶 If-None-Match: *If-Match: <etag> 標頭就是一次跨節點的 compare-and-swap。celld 用這個 CAS 原子性做兩件事:cell 的 ownership claim、以及每一次寫入的 fencing 檢查。

實務意義是節點之間不需要互相看得到。傳統 Raft cluster 要三個以上節點彼此建立長連接、跑心跳、選 leader;celld 節點只跟 S3 bucket 溝通,彼此之間只在 request routing 時才產生流量。這對於跨區部署或想在多個 VPS 供應商之間拉冗餘的場景幫助很大——不用預先建立 mesh network、不用擔心 quorum 撐不撐得住區域斷網。

代價當然存在。S3 的每次寫入延遲通常在 20 到 100 毫秒之間,比 Raft cluster 內部一次 log entry 慢一個數量級。celld 官方 benchmark 寫的 durable write latency 大約 90 毫秒就是這個限制決定的下界——不是實作沒調好,是物理上 bucket 就要那麼久才能確認寫入。想拉低這個數字只能靠自架 Garage 或 MinIO 這種 S3 兼容儲存架在區網內。

LTX 加 ownership epoch 才是這個模型的核心

單靠 S3 CAS 建立 ownership 不夠。真正棘手的情境是 split-brain:節點 A 認為自己還擁有某個 cell、開始寫入 SQLite;但因為網路抖動,node A 的 ownership 已經被搶走給 node B。傳統 Raft 靠 term number 加 quorum 保證 leadership,celld 靠的是 ownership epoch 這個概念。

每次 cell 被 claim 走都遞增 epoch number 並寫進 S3 的一個特定 key。node A 在把 SQLite 交易寫到 bucket 之前會做一次 conditional write:只有當 bucket 上的 ownership key 仍然指向 node A 加上正確的 epoch,寫入才算數。這個檢查跟寫入是一起做的(S3 的原子 CAS 語意),所以就算 node A 網路慢半拍、以為自己還握著 cell,寫入這一步就會失敗——node A 這一輪的交易被送進一個帶著舊 epoch 的 path,永遠不會被認為是這個 cell 的有效狀態。

被使用的交易格式是 LTX(Litestream 的 replica format),這是 Ben Johnson 為了 Litestream 定義的一套把 SQLite 頁面變更打包成 segment 的格式。celld 直接把 SQLite 的每一次 commit 攔截下來、封裝成 LTX segment、丟到 bucket 對應 cell 加 epoch 的路徑。cell 醒來時再從 bucket 依 epoch 順序把 LTX segment 回放到本地 SQLite。這比手工設計一套 WAL 傳輸格式好處明顯——LTX 已經被 Litestream 用了幾年,格式穩定、有現成工具能看、Rust 實作也有底子。

這個設計還帶來一個副作用:LTX segment 天生就是 immutable 的,寫進 bucket 之後不會被覆寫。舊 epoch 的 segment 留在 bucket 裡不會傷害資料完整性,只是佔空間而已,可以靠 lifecycle policy 定期清掉。傳統資料庫 replication 常見的 log truncation 跟 GC 這一整層工程可以直接省掉。

每個 cell 一顆 SQLite 的成本剖面

celld 官方公佈的密度數字是:resident cell 的 RAM overhead 大約 0.47 MB,一個 8 GB 節點可以塞 2500 顆 cell;hibernated cell 只留 metadata 在 bucket,記憶體佔用歸零。cell wake time 大約 4 毫秒——這是從 S3 拉最新 LTX segment 回放到本地 SQLite 的時間。durable write latency 90 毫秒、failover 20 秒完成、SPOF 恢復不掉資料。

這個成本剖面適合的工作負載是:租戶數量多、單一租戶負載中等、大部分時間閒置、活躍時要求低延遲讀寫。典型例子是即時協作文件(每份文件一個 cell)、多租戶 SaaS(每個 tenant 一個 cell)、AI Agent 的長對話 session(每段對話一個 cell)。這些場景的共同特徵是「幾百萬顆潛在單位,但同一時間只有一小部分活著」。

不適合的工作負載也很明確:單一 cell 要吃掉整臺機器記憶體的重型工作(拿去跑 vector index 的節點)、需要跨 cell 交易的商業邏輯(celld 沒有 cross-cell 交易)、寫入延遲不能超過 10 毫秒的高頻交易系統(S3 的物理下界擋住)。這些場景還是回頭找 CockroachDB、TiDB 這類 Raft-based 分散式資料庫比較合理。

跟 Rivet Actors、OpenWorkers 的取捨在哪

自架 Durable Objects 這個題目在 2026 年至少浮出三個認真的方案:Rivet Actors、OpenWorkers、celld。三者的差別不在「都是 self-hosted」這個共同點,而在架構選擇的側重。

Rivet Actors 走的是更廣義的 actor 模型,用 Rust 從頭寫、FoundationDB 或 Postgres 當熱儲存、S3 當冷儲存。抽象化不特別綁 Cloudflare API,設計目標是能接住 AI Agent 這類長時間 stateful 工作負載。密度數字更誇張(單一 actor 記憶體 overhead 只有 0.6 KB),但需要拉 FoundationDB cluster 或搭 Postgres,運維成本比 celld 高一階。

OpenWorkers 專注在 Workers runtime 本身的相容性,把 rusty_v8 綁起來提供 KV、R2、Service Binding 這些介面,但不處理 Durable Objects 的分散式狀態。適合已經在 Cloudflare Workers 上跑無狀態或近乎無狀態程式碼的團隊,想把成本從 Cloudflare 帳單搬回自家機房。

celld 的定位剛好卡在中間:它跟 Cloudflare API 的相容性夠深,wrangler.jsonc 直接吃、Durable Objects 完整搬過來;同時它的部署複雜度低得離譜——58 MB 靜態執行檔加一個 S3 兼容 bucket 就跑得起來。用一句話總結三者的定位差別:Rivet 是「AI Agent 導向的高效能 actor runtime」、OpenWorkers 是「無狀態 Workers 的自架版」、celld 是「有狀態 Workers 的最小可行自架版」。

如果團隊已經在用 Cloudflare Workers 跟 Durable Objects、程式碼想不改就搬回自家、可以接受 90 毫秒的寫入延遲,celld 就是目前最直接的答案。想要更高效能或更複雜的 actor 抽象化,再往 Rivet 走。

在臺灣 VPS 上部署 celld 的實際考量

celld 的部署最小組合是:一臺 VPS 加一個 S3 兼容 bucket。實務上要做的決定有三個。

S3 兼容儲存的選擇:直接用 AWS S3 是最省事但延遲最痛。臺灣訪問 S3 Tokyo region 的 RTT 通常 40 到 60 毫秒,加上 S3 內部處理,durable write 很容易衝到 150 毫秒以上。比較合理的路徑是在同一機房架 Garage 或 MinIO——RTT 壓到 1 毫秒以內,durable write 能拉回 20 到 30 毫秒。Garage 對這個場景特別合適,它的設計目標就是分散式 S3 儲存、寫入延遲低、多節點複寫。用 3 節點 Garage cluster 撐 celld 的 bucket,冗餘度跟延遲同時到位。

多節點 celld 的網路拓樸:celld 官方文件明確要求 peer 之間的流量走 private network,因為 request routing 需要節點互相轉發請求給擁有該 cell 的 owner node。WireGuard 或 Tailscale 都是合理選擇——WireGuard 效能較好但要手工管 config,Tailscale 部署快但走 Tailscale coordination server。跨區部署時建議直接自架 headscale 撐 control plane,避開對外部服務的依賴。

cell owner 的區域親和性:celld 沒有內建 cell 的區域偏好機制,一顆 cell 醒在哪個節點是由 request 打到哪決定的。這代表如果多節點分散在不同資料中心、client 從東京連到臺北節點、cell 又被東京節點擁有,每次請求會多一次跨區域 hop。目前的 workaround 是靠應用層 routing 或 sticky load balancing,把同一個 cell 的請求盡量打到同一區。這個機制未來預期會補進 celld,但 v0.1.0 還沒有。

這個模型還缺什麼

celld v0.1.0 是 alpha 版本,官方明確列出目前不覆蓋的 Cloudflare 功能:KV bindings、R2、Cache API、Workers AI、Cron handlers、managed ingress。這些對 Cloudflare Workers 的完整體驗都很重要,但至少 KV 跟 Cron 相對容易在自架環境用 Redis 或 systemd timer 補上。R2 直接用 S3 兼容 bucket 頂就好,本來就是同一件事。

比較困難的是 hostile multi-tenant——celld 明說目前不支援不受信任的 tenant 共用同一組節點。這個限制對 SaaS 場景是個問題,因為使用者提交的 JavaScript 程式碼理論上有機會影響同節點其他 cell。Cloudflare Workers 在這個議題上花了很多年打磨 isolate 邊界,包括 Spectre 緩解、精確計時器阻絕、記憶體限制強制執行。celld 目前的建議是只在信任的內部團隊間共用節點,或者為每個租戶開獨立節點跑。

即便如此,把「有狀態 JavaScript 執行環境」壓縮到單一 58 MB binary 加一個 bucket 就跑得動這件事本身就值得記住。Durable Objects 之前的實作路徑要嘛綁在 Cloudflare 上、要嘛就是拉 Kubernetes cluster 加共識服務加專屬控制平面,門檻高到大部分中小團隊直接放棄。celld 把這個門檻壓到「一臺 VPS 加一個 bucket」的層級,等於把 Durable Objects 這個抽象化從邊緣運算專屬變成任何人都能自架的基礎設施。

要在自家 VPS 上跑 celld 撐真正的 production 工作負載,儲存跟網路那一層要做得夠紮實才有意義——低延遲的 S3 兼容 bucket、穩定的 private network、以及 IP transit 品質決定了 durable write 這條路的實際體驗。NCSE Network 提供的臺灣是方電訊機房 VPS,搭配 Intel Gold CPU 跟 NVMe SSD,適合直接跑 Garage cluster 撐 celld 的儲存後端,也能透過 IP Transit 服務把多節點跨區流量帶起來。想拉一套自架 Durable Objects 環境的團隊,可以到 ncse.tw 了解 VPS 與網路服務選項。

需要技術開發支援?

NCSE Network 提供 Discord Bot、LINE Bot、AI Agent、爬蟲、監控系統等客製化開發服務,從規劃到上線一站式完成。

洽談專案 →