📌 项目地址:denoland/celld | ⭐ 2,079 颗星 | 🔧 Rust | 📜 未标注
Cloudflare Durable Objects 是种有趣的抽象:它把状态和计算绑在一个对象里,每个对象有独立存储、独立故障域,像一个微型服务。问题是它只能在 Cloudflare 的平台上跑。denoland/celld 把这套抽象拆了出来,让它在你自己掌握的机器上运行。
核心设计:对象即数据库
celld 里每个 Durable Object 是一个独立的 SQLite 数据库。对象按名称寻址,状态持续异步复制到 S3 兼容桶。节点之间没有任何直接通信——没有控制平面,不跑 Raft,不做成员探测。所有协调都通过对象存储完成。
一个节点的职责是:从桶里拉取某个 cell 的数据库,加载到本地 SQLite,执行 V8 里的 Worker 代码,处理请求,然后持续把变更写回桶。节点本身是无状态的——它的全部持久身份都在桶里。
S3 的 compare-and-swap 操作是所有权仲裁的基础。每个 cell 对应一个小的所有权记录,节点通过 CAS 尝试认领它。认领成功就执行,失败就跳过。这里没有失败检测器:节点失联后,它的 lease 会过期,其他节点通过 CAS 重新认领。桶是真相来源,节点只是缓存。
这个架构的直接结果是:应用天然分片。每个 cell 是独立数据库,不存在一个共享库被所有请求争用的问题。爆炸半径被限制在单个对象内,而不是整个数据集。空闲的 cell 可以被驱逐出内存,在存储里休眠,醒来时从桶恢复。
部署模型:对象存储即协调层
celld 需要两个东西:一批能跑 V8 的节点(普通 Linux 机器即可),一个 S3 兼容桶。桶里放三类数据:部署包、cell 状态、所有权记录。
celld deploy . --bucket s3://my-cells-bucket 把 Wrangler 构建的产物上传到桶里。节点启动时连同一个桶,拉取所有权记录,开始认领工作。多节点只需各自指向同一桶,不需要互相知道对方存在。负载均衡器背后的每个节点需要设置不同的 --advertise 地址,让其他节点能访问到它。
Docker 运行时也走同样的路径:
docker volume create celld-state
docker run --rm --network host
-e AWS_ACCESS_KEY_ID
-e AWS_SECRET_ACCESS_KEY
-e AWS_SESSION_TOKEN
-e CELLD_WATCH=/var/lib/celld/state
-v celld-state:/var/lib/celld
ghcr.io/denoland/celld
--bucket s3://my-cells-bucket
--endpoint https://ACCOUNT.r2.cloudflarestorage.com
--region auto
--listen 0.0.0.0:8080
--advertise node-a.internal:8080
这里有个细节:容器用 --network host,--advertise node-a.internal:8080 是给其他节点访问用的地址。--endpoint 和 --region 是针对 Cloudflare R2 的,如果是真实 AWS S3,这两个参数不需要。
实现层面的一些事实
每个节点内嵌 V8 引擎,执行 Wrangler 构建的 bundle。这意味着部署的是标准 Cloudflare Worker 产物,不是某种自定义格式。安装工具会验证二进制来源,gh attestation verify 可查证。
安装路径和其他 Deno 工具类似:
curl -fsSL https://celld.dev/install.sh | sh
~/.local/bin 要加入 PATH。如果部署的是含 Worker 代码的项目,需要 esbuild;纯静态资产项目可以跳过。
这些是 README 里明确写的。关于性能、容量和延迟的数据,README 没有给出。
这个架构的门槛在哪里
我的判断是,celld 把复杂度从运行时转移到了运维预期上。原来 Cloudflare 替你处理的一切——故障转移、跨区域复制、热点迁移——现在都压缩在两个原语里:S3 的原子操作 + 每个对象的 SQLite 数据库。这比自建 etcd 集群简单,但比“部署一个 postgres”复杂得多。你需要理解对象存储的语义,比如读写计费、最终一致性窗口、CAS 的并发上限。
还有个实际限制:S3 的原子性上限是单 key 的 compare-and-swap。跨 key 的事务在对象存储层不存在。celld 把所有权记录和数据库状态拆成不同 key,意味着它们之间的一致性靠时序保证,而不是靠底层原子性。这在故障场景下会表现为“旧 owner 还在写数据库,新 owner 已经开始读”——数据库的写入顺序可能被打乱。具体怎么解决,README 没有展开,但设计上应该依赖 SQLite WAL 的复制顺序来规避。
Cloudflare Durable Objects 有价值,是因为它提供了平台级的可观测性、配额管理和流量调度。celld 去掉了这些,保留了对象模型本身。如果你已经接受自托管的一切运维成本,又不想从零搭一套分布式状态管理,这是个值得试的项目。如果只是想要一个 SQLite 的 HTTP 封装,那它可能不是最合适的东西——先把 Workers 的编程模型搞明白再决定。
Stars 2089 对一个只有几个月的新项目来说不算少。Deno 团队在 Workers 生态里积累了不少经验,celld 更像是把这些经验外溢到了自有基础设施上。对象存储作为协调层不是新鲜想法,但拿它跑 V8 + SQLite 的组合,这个方向算得上原创。