Documentation文档
celldcelld
celld is a stateful distributed system. It runs server-side JavaScript on your machines, and it stores the long-term state in a bucket that you own: S3-compatible, Google Cloud Storage, or Azure Blob Storage. The JavaScript API and the configuration follow Cloudflare Workers: Workers, Durable Objects, KV, Queues, D1, R2, Workflows, Cron Triggers, and static assets.celld 是一个有状态的分布式系统。它在您的机器上运行服务端 JavaScript,并将长期状态存储在您拥有的存储桶中:支持 S3 协议的存储、Google Cloud Storage 或 Azure Blob Storage。其 JavaScript API 和配置遵循 Cloudflare Workers 的规范:Workers、Durable Objects、KV、Queues、D1、R2、Workflows、Cron Triggers 和静态资源。
Cells and nodes单元(Cells)与节点(Nodes)
In Cloudflare terms, a cell is a Durable Object: a small server with a name and a private SQLite database. You make one cell for each user, document, chat room, or AI agent. A cell serves HTTP, holds WebSocket connections, sets alarms, and makes outbound connections. Cells share no database, so the application divides into cells from the start. Each cell runs on one thread: a second request interleaves only while the first awaits, and storage operations are synchronous and never interleave, so the data in a cell stays consistent.在 Cloudflare 的术语中,一个单元(cell)即一个 Durable Object:一个带有名称和私有 SQLite 数据库的小型服务器。您可以为每个用户、文档、聊天室或 AI 代理创建一个单元。单元负责处理 HTTP 请求、维持 WebSocket 连接、设置闹钟并建立出站连接。各单元之间不共享数据库,因此应用程序从一开始就被划分为多个单元。每个单元运行在一个线程上:第二个请求仅在第一个请求等待时交错执行,且存储操作是同步的,绝不会交错,从而确保了单元内数据的一致性。
You run celld as one process on each machine. That process is a node, and the nodes that share one bucket are a fleet. Any node can serve any cell, so you add capacity by starting another node against the same bucket.您在每台机器上将 celld 作为单个进程运行。该进程即为一个节点,而共享同一个存储桶的节点集合构成一个集群(fleet)。任何节点都可以服务于任何单元,因此您可以通过启动更多连接到同一存储桶的节点来增加容量。
Cell lifecycle单元生命周期
A cell has the same states as a Durable Object. A resident cell is in
memory: it is active while it does work, and idle when it waits.
celld removes an idle cell from memory after CELLD_IDLE_EVICT_S seconds
without work, and earlier under memory pressure or at the residency cap.
Without CELLD_IDLE_EVICT_S, an idle cell stays in memory until pressure
or the cap removes it. A cell that then keeps its
hibernatable WebSocket clients, and stays on its node, is hibernated.
A cell that no node holds is inactive. An inactive cell is only an
object in the bucket, so it costs almost zero, and every cell starts in
this state. A paged activation adds its epoch prefix to the bucket
immediately, when it uploads its marker. An activation that is not paged
adds the prefix at its first per-cell upload. With fleet durability, a
write goes first to the log bundles of the node. The first per-cell
upload is therefore the first L1 compaction of the activation, a handoff
snapshot, or a direct upload while the log ensemble of the node is
degraded. When epoch GC is on and the cell has an older prefix, the owner
does not wait for CELLD_LTX_COMPACTION_MIN_TXIDS. It starts that
compaction at its next epoch GC pass after the first write, and a pass
can come up to 5 minutes later. With CELLD_LTX_COMPACTION=0 the owner
does not compact, so the prefix waits for one of the other uploads. A
restore reads only
the newest epochs of a cell. celld deletes the older prefixes only when
CELLD_LTX_RETENTION_SECS enables epoch GC, so without it the bytes of
a cell increase with each activation that adds a prefix.单元具有与 Durable Object 相同的状态。驻留单元(resident cell)存在于内存中:它在执行工作时处于活跃状态,在等待时处于空闲状态。celld 会在单元空闲且无工作 CELLD_IDLE_EVICT_S 秒后将其从内存中移除,在内存压力较大或达到驻留上限时会更早移除。若未设置 CELLD_IDLE_EVICT_S,空闲单元将一直保留在内存中,直到压力或上限触发移除。此后,保留其可休眠 WebSocket 客户端并留在原节点的单元将进入休眠状态。没有任何节点持有的单元处于非活跃状态。非活跃单元仅是存储桶中的一个对象,因此几乎不产生费用,且每个单元初始时都处于此状态。分页激活(paged activation)会在上传标记时立即将其纪元前缀(epoch prefix)添加到存储桶中。非分页激活则在首次按单元上传时添加该前缀。在集群持久化机制下,写入操作首先进入节点的日志包(log bundles)。因此,首次按单元上传即为该激活的首次 L1 合并、移交快照,或在节点日志集合降级时的直接上传。当开启纪元垃圾回收(epoch GC)且单元具有较旧的前缀时,所有者不会等待 CELLD_LTX_COMPACTION_MIN_TXIDS。它会在首次写入后的下一次纪元 GC 周期启动合并,而该周期可能在 5 分钟后到来。若设置 CELLD_LTX_COMPACTION=0,所有者将不进行合并,因此前缀会等待其他上传操作。恢复操作仅读取单元的最新纪元。celld 仅在 CELLD_LTX_RETENTION_SECS 启用纪元 GC 时才会删除较旧的前缀,因此若未启用,单元所占字节数会随着每次添加前缀的激活而增加。
A cell keeps no memory across these transitions, so the constructor runs again on the next event. A hibernated cell wakes the same way a cold start does, except that its WebSocket clients stay connected and it stays on its node.单元在这些转换过程中不保留任何内存,因此构造函数会在下一次事件时重新运行。休眠单元的唤醒方式与冷启动相同,区别在于其 WebSocket 客户端保持连接且单元仍留在原节点上。
One 8 GB node holds 1,000 resident cells, so one resident cell costs approximately $0.05 each month.一个 8 GB 的节点可容纳 1,000 个驻留单元,因此每个驻留单元每月的成本约为 0.05 美元。
Ownership and durability所有权与持久性
Exactly one node serves a cell at a time. The nodes do not elect a leader or keep a membership list: a node claims a cell by writing a small record to the bucket. The store accepts the write only if no other node changed the record first. Object storage therefore decides who wins, and two nodes cannot both claim the same cell. The claim expires unless the node renews it, so a failed machine releases its cells without a separate failure detector.同一时间只有一个节点服务于一个单元。节点之间不选举领导者,也不维护成员列表:节点通过向存储桶写入一条小记录来声明对单元的所有权。仅当没有其他节点先更改该记录时,存储才会接受写入。因此,对象存储决定了谁获胜,两个节点不可能同时声明同一个单元。除非节点续期,否则声明会过期,因此故障机器无需单独的故障检测器即可释放其单元。
celld does not answer a write until the data survives a failure, so no write you were told succeeded is ever lost. That guarantee is called RPO=0, for a recovery point of zero.celld 在数据成功存入以应对故障之前不会响应写入操作,因此任何被告知成功的写入都不会丢失。该保证被称为 RPO=0,即恢复点目标为零。
The durability mechanism depends on how many nodes you run. One node
writes the data to the bucket first, which costs one storage round trip. Two or more
nodes are faster: the node serving the cell sends each write to another
node as well, and answers as soon as that node has the data on its own
disk. celld uploads the data to the bucket afterwards, so the bucket
still holds the long-term state. Run two or more nodes if write latency
matters to you; a single node has nobody to send to, so every write waits
for the bucket. CELLD_DURABILITY selects this behavior and defaults to
fleet.持久化机制取决于您运行的节点数量。单节点首先将数据写入存储桶,这需要一次存储往返。两个或更多节点则更快:服务于单元的节点会将每次写入同时发送给另一个节点,并一旦该节点将其存入磁盘即作出响应。celld 随后将数据上传到存储桶,因此存储桶始终保存长期状态。如果写入延迟对您很重要,请运行两个或更多节点;单节点没有发送对象,因此每次写入都必须等待存储桶。CELLD_DURABILITY 参数选择此行为,默认值为 fleet(集群模式)。
When a node stops, another node takes the cell over. It first collects whatever the stopped node had not uploaded yet, so the takeover starts from a complete history. The guarantees page gives the full mechanism and the exact properties that the bucket must provide.当一个节点停止时,另一个节点会接管该单元。它首先收集停止的节点尚未上传的所有内容,因此接管过程从完整的历史记录开始。保证页面(guarantees page)提供了完整的机制以及存储桶必须提供的确切属性。
Alarms闹钟(Alarms)
When an event sets an alarm before its response boundary, celld does not send a
successful response until a durable wake entry covers the alarm. celld does
not let a later waitUntil alarm delay another event's response.
Each committed installation requires its own publication PUT, including an
alarm that moves later within the same minute. Each entry has a separate
identity, so delayed processing of an older alarm state cannot remove the
entry that covers a newer alarm.当事件在响应边界之前设置闹钟时,celld 在持久化唤醒条目覆盖该闹钟之前不会发送成功响应。celld 不会让后续的 waitUntil 闹钟延迟其他事件的响应。每个已提交的安装都需要其自己的发布 PUT 操作,包括在同一分钟内稍后移动的闹钟。每个条目都有独立的标识,因此对旧闹钟状态的延迟处理不会移除覆盖较新闹钟的条目。
A hibernated cell fires its alarm on the node that owns it. One node in the fleet holds the waker role at a time. The waker reads the due wake entries of the whole fleet, and it wakes only the cells whose owner node has stopped. Every other node wakes only its own hibernated cells, so the alarm load of the fleet does not queue on one node.休眠单元在其所属节点上触发闹钟。集群中一次只有一个节点担任唤醒者(waker)角色。唤醒者读取整个集群的到期唤醒条目,并仅唤醒那些所有者节点已停止的单元。其他所有节点仅唤醒各自的休眠单元,因此集群的闹钟负载不会堆积在单个节点上。
What do you build with cells使用单元构建什么
A cell fits a workload that divides into named, stateful units:单元适用于可划分为命名、有状态单元的工作负载:
- Real-time applications. A multiplayer game, a chat room, or a collaborative document is one cell. The cell holds the WebSocket connections and the state of one room, so the room needs no lock and no external message bus.实时应用程序。多人游戏、聊天室或协作文档即为一个单元。该单元持有 WebSocket 连接和单个房间的状态,因此房间无需锁,也无需外部消息总线。
- Agents. Each AI agent is one cell. The cell holds the memory, the schedule, and the inbox of one agent in its own SQLite database. An inactive agent has no resident process, so a large agent fleet costs almost nothing between events.代理(Agents)。每个 AI 代理即为一个单元。该单元在其自己的 SQLite 数据库中持有代理的内存、调度和收件箱。非活跃代理没有驻留进程,因此大型代理集群在事件间几乎不产生费用。
- Sharded web applications. One cell for each user, each tenant, or each device shards the application from the start. The contention of one shared database does not appear, because no shared database exists.分片 Web 应用程序。为每个用户、租户或设备分配一个单元,从一开始就对应用程序进行分片。由于不存在共享数据库,因此不会出现共享数据库的争用问题。
Contents目录
- Cells and nodes
- Cell lifecycle
- Ownership and durability
- Alarms
- What do you build with cells
- Install安装
- Configure object storage配置对象存储
- Deploy an application部署应用程序
- Operate D1, KV, and R2操作 D1、KV 和 R2
- Start a node启动节点
- Add nodes添加节点
- Shut down and roll out a node关闭并更新节点
- Diagnose a fleet诊断集群
- List Durable Objects列出 Durable Objects
- Environment variables环境变量
- Services服务
- Cloudflare compatibilityCloudflare 兼容性
- What celld guaranteescelld 的保证
- Limitations限制
- Security安全
- Telemetry遥测
- Testing测试
- WebAssemblyWebAssembly
- Python WorkersPython Workers
Services
Each service page contains a runnable example and the known differences from the Cloudflare service.每个服务页面都包含一个可运行的示例以及与 Cloudflare 服务的已知差异。
- WorkersWorkers
- Durable Objects / CellsDurable Objects / Cells
- Durable Object FacetsDurable Object Facets
- KV
- QueuesQueues
- D1
- R2
- WorkflowsWorkflows
- Cron TriggersCron Triggers
- Static assets静态资源
- Dynamic Workers动态 Workers
- Containers容器
Install
The installer downloads the celld binary. Replication runs inside the
celld process, so a node needs no external replicator.安装程序会下载 celld 二进制文件。复制过程在 celld 进程内部运行,因此节点不需要外部复制器。
curl -fsSL https://celld.dev/install.sh | sh
If the installer tells you, add ~/.local/bin to PATH. To install one
exact release, set CELLD_VERSION to its tag, for example v0.0.1; to
go back, run the installer again with the previous tag. The releases are
on GitHub, and each release
has a GitHub Actions build attestation: verify a downloaded file with
gh attestation verify <asset> --repo denoland/celld.如果安装程序提示,请将 ~/.local/bin 添加到 PATH。要安装特定版本,请将 CELLD_VERSION 设置为对应的标签,例如 v0.0.1;若要回退,请使用之前的标签再次运行安装程序。发布版本位于 GitHub 上,每个版本都有 GitHub Actions 构建证明:使用 gh attestation verify <asset> --repo denoland/celld 验证下载的文件。
Configure object storage
The s3://, gs://, and az:// scheme names are case-insensitive.
celld ignores leading and trailing whitespace in CELLD_BUCKET.s3://、gs:// 和 az:// 方案名称不区分大小写。celld 会忽略 CELLD_BUCKET 中的前导和尾随空格。
For an S3-compatible bucket, celld uses the standard AWS credential chain. Paged restores also use this chain, so an EC2 instance role does not require static access keys for a large cell. On Amazon EKS, celld reads the Pod Identity credentials from the injected environment variables and the authorization-token file. For Cloudflare R2: create a bucket, create an S3 API token with access to that bucket, and set these variables:对于 S3 兼容的存储桶,celld 使用标准的 AWS 凭证链。分页恢复也使用此链,因此 EC2 实例角色不需要大型单元的静态访问密钥。在 Amazon EKS 上,celld 从注入的环境变量和 authorization-token 文件中读取 Pod Identity 凭证。对于 Cloudflare R2:创建一个存储桶,创建一个有权访问该存储桶的 S3 API 令牌,并设置以下变量:
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_REGION=auto
export S3_ENDPOINT=https://ACCOUNT_ID.r2.cloudflarestorage.com
export CELLD_BUCKET=s3://YOUR-BUCKET
For Google Cloud Storage, celld uses Application Default Credentials.
Create a bucket. Then authenticate with gcloud auth application-default login, or point GOOGLE_APPLICATION_CREDENTIALS at a service-account
key that has access to the bucket. Then set the bucket:对于 Google Cloud Storage,celld 使用应用程序默认凭证。创建一个存储桶。然后使用 gcloud auth application-default login 进行身份验证,或者将 GOOGLE_APPLICATION_CREDENTIALS 指向具有存储桶访问权限的服务账户密钥。然后设置存储桶:
export CELLD_BUCKET=gs://YOUR-BUCKET
A gs:// bucket takes no S3_ENDPOINT and no AWS_* credentials, and
celld ignores the storage region. On a Compute Engine instance, celld can
use the attached service account, but the access scopes of the instance
cap this credential, and the default scope permits only storage reads.
Create the instance with the cloud-platform scope, so the IAM role of
the service account controls the access.gs:// 存储桶不需要 S3_ENDPOINT 和 AWS_* 凭证,celld 也会忽略存储区域。在 Compute Engine 实例上,celld 可以使用附加的服务账户,但实例的访问范围限制了此凭证,默认范围仅允许读取存储。请使用 cloud-platform 范围创建实例,以便服务账户的 IAM 角色控制访问权限。
For Azure Blob Storage, the bucket NAME is the container, and the storage account comes from the environment. Create a container. Then set the account and one credential:对于 Azure Blob Storage,存储桶 NAME 即为容器,存储账户来自环境变量。创建一个容器。然后设置账户和凭证:
export AZURE_STORAGE_ACCOUNT_NAME=YOUR-ACCOUNT
export AZURE_STORAGE_ACCOUNT_KEY=...
export CELLD_BUCKET=az://YOUR-CONTAINER
You must configure exactly one Azure credential family: a storage account
key, a managed identity, or a workload identity. A system-assigned managed
identity needs only the account name. A user-assigned managed identity needs exactly one selector among
AZURE_CLIENT_ID, AZURE_OBJECT_ID, and AZURE_MSI_RESOURCE_ID,
because two selectors can name two different identities. An identity
must hold the Azure Blob data-plane permission to read, write, list, and
delete blobs; the built-in
Storage Blob Data Contributor
role supplies it.您必须配置且仅配置一种 Azure 凭证系列:存储账户密钥、托管标识或工作负载标识。系统分配的托管标识仅需要账户名称。用户分配的托管标识需要在 AZURE_CLIENT_ID、AZURE_OBJECT_ID 和 AZURE_MSI_RESOURCE_ID 中指定且仅指定一个选择器,因为两个选择器可能指向两个不同的标识。标识必须持有 Azure Blob 数据平面权限以读取、写入、列出和删除 Blob;内置的 Storage Blob Data Contributor 角色提供了此权限。
celld reads a managed identity from the Azure instance metadata service,
which an Azure VM and an AKS node supply. Azure App Service and Azure
Container Apps supply a different endpoint, so a managed identity does
not work there; use a workload identity or an account key instead. The
standard AKS workload identity environment (AZURE_AUTHORITY_HOST,
AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN_FILE)
works with the public authority host, https://login.microsoftonline.com,
with or without a trailing slash; celld rejects a sovereign or custom
host. celld also rejects each recognized Azure configuration variable
outside these families, including another credential source, an endpoint
override, or the OneLake endpoint. It ignores an AZURE_* name that
object_store 0.12 does not recognize, because that name cannot change
the client.celld 从 Azure 实例元数据服务读取托管标识,Azure VM 和 AKS 节点均提供此服务。Azure App Service 和 Azure Container Apps 提供不同的端点,因此托管标识在此处不起作用;请改用工作负载标识或账户密钥。标准的 AKS 工作负载标识环境 (AZURE_AUTHORITY_HOST, AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN_FILE) 可与公共授权主机 https://login.microsoftonline.com 配合使用(无论是否有尾随斜杠);celld 会拒绝主权或自定义主机。celld 还会拒绝这些系列之外的任何已识别的 Azure 配置变量,包括其他凭证源、端点覆盖或 OneLake 端点。它会忽略 object_store 0.12 无法识别的 AZURE_* 名称,因为该名称无法更改客户端。
An az:// bucket takes no S3_ENDPOINT and no AWS_* credentials, and
celld ignores the storage region. celld qualifies an az:// bucket for
a production fleet. The startup test verifies the conditional writes and
ranged reads. A live Azure test from 2026-08-18 covers the multipart upload
path. See guarantees. For local development against Azurite,
set AZURE_STORAGE_USE_EMULATOR=true; Azurite is a development store, so
celld does not qualify it for a fleet either.az:// 存储桶不需要 S3_ENDPOINT 和 AWS_* 凭证,celld 也会忽略存储区域。celld 将 az:// 存储桶视为生产集群的合格存储。启动测试会验证条件写入和范围读取。2026-08-18 的实时 Azure 测试涵盖了分段上传路径。请参阅保证页面。对于针对 Azurite 的本地开发,请设置 AZURE_STORAGE_USE_EMULATOR=true;Azurite 是一个开发存储,因此 celld 不将其视为集群的合格存储。
The bucket credentials give full control of the fleet, so keep them safe. The bucket contains the deployments, the SQLite replicas, the ownership records, the node leases, and the peer-authentication secret.存储桶凭证赋予了对集群的完全控制权,因此请妥善保管。存储桶包含部署、SQLite 副本、所有权记录、节点租约和对等身份验证密钥。
A bucket value can add a key prefix: s3://YOUR-BUCKET/PREFIX. Every
object of the fleet then goes below PREFIX/, so two fleets can share one
bucket. A bucket value without a prefix keeps the objects at the root of
the bucket, therefore an existing fleet does not move its data.存储桶值可以添加键前缀:s3://YOUR-BUCKET/PREFIX。集群的每个对象都将位于 PREFIX/ 下,因此两个集群可以共享一个存储桶。没有前缀的存储桶值会将对象保留在存储桶根目录下,因此现有集群不会移动其数据。
The store must provide conditional writes. A ranged read must return the exact requested bytes. The store must also provide read-after-write consistency. Cell ownership and stored data depend on these properties. Amazon S3, Cloudflare R2, Google Cloud Storage, Tigris, and Azure Blob Storage qualify; Backblaze B2, Hetzner, and DigitalOcean Spaces do not. MinIO (the community edition) passes the storage test, but celld has not qualified it for production; do not use RELEASE.2025-09-06T17-38-46Z, which rejects the conditional create that the first deploy sends (denoland/celld#162). See what celld guarantees for the exact requirements.存储必须提供条件写入。范围读取必须返回确切的请求字节。存储还必须提供读后一致性。单元所有权和存储数据依赖于这些属性。Amazon S3、Cloudflare R2、Google Cloud Storage、Tigris 和 Azure Blob Storage 符合要求;Backblaze B2、Hetzner 和 DigitalOcean Spaces 不符合。MinIO(社区版)可以通过存储测试,但 celld 尚未将其认证用于生产环境;请勿使用 RELEASE.2025-09-06T17-38-46Z,因为它会拒绝首次部署发送的条件创建请求 (denoland/celld#162)。请参阅 celld 的保证页面了解确切要求。
Deploy an application
If the project contains Worker code, install esbuild on PATH; an
asset-only project does not need it. Then run celld deploy from an
applicable Wrangler project:如果项目包含 Worker 代码,请在 PATH 中安装 esbuild;纯资源项目不需要它。然后从适用的 Wrangler 项目运行 celld deploy:
git clone https://github.com/denoland/celld
cd celld/examples/counter
celld deploy . \
--bucket "$CELLD_BUCKET" \
--endpoint "$S3_ENDPOINT" \
--region "$AWS_REGION"
celld deploy accepts module Workers, Durable Object bindings, service
bindings, variables, cron triggers, D1 databases, KV namespaces, Queues,
R2 buckets, Workflows, WebAssembly modules, and static assets. An asset project can include a
Worker or be asset-only, and the asset functions include the assets
binding, HTML handling, not-found handling, worker-first routes,
_headers, and _redirects. If the Wrangler configuration contains an
unknown key, the deploy stops with an error. See the
Cloudflare compatibility page for the complete
deployment boundary.celld deploy 接受模块化 Workers、Durable Object 绑定、服务绑定、变量、Cron Triggers、D1 数据库、KV 命名空间、Queues、R2 存储桶、Workflows、WebAssembly 模块和静态资源。资源项目可以包含 Worker 或仅包含资源,资源功能包括资源绑定、HTML 处理、404 处理、优先路由、_headers 和 _redirects。如果 Wrangler 配置包含未知键,部署将停止并报错。请参阅 Cloudflare 兼容性页面了解完整的部署边界。
A new deployment manifest records the full SHA-256 digest of each JavaScript or WebAssembly module. A node verifies each module before it builds the deployment, so changed module bytes cannot become active. A node also accepts a legacy 16-character digest and verifies that prefix, so an upgrade can load an existing deployment.新的部署清单记录了每个 JavaScript 或 WebAssembly 模块的完整 SHA-256 摘要。节点在构建部署之前会验证每个模块,因此更改后的模块字节无法变为活跃状态。节点也接受旧版的 16 字符摘要并验证该前缀,因此升级可以加载现有部署。
A running node does not restart for a new deployment. Each node reads
deploy/current.json every 30 seconds (CELLD_DEPLOY_POLL_S) and adopts
a new deployment in place, and POST /reload on the internal listener
makes a node adopt the pointer now. A node builds the new deployment
beside the one it serves, and then it switches new requests to the new
deployment in one step. A request that started on the previous deployment
finishes on it. A deployment that does not build leaves the current
deployment serving, and the node reports the failure in its log and in
the /reload response. POST /reload also rebuilds an unchanged
deployment.运行中的节点不会因新部署而重启。每个节点每 30 秒 (CELLD_DEPLOY_POLL_S) 读取一次 deploy/current.json 并原地采用新部署,内部监听器上的 POST /reload 可使节点立即采用该指针。节点在服务当前部署的同时构建新部署,然后一步切换新请求到新部署。在先前部署上启动的请求会在该部署上完成。未能构建的部署将保持当前部署继续服务,节点会在日志和 /reload 响应中报告失败。POST /reload 也会重新构建未更改的部署。
A Durable Object that is not resident runs the new deployment at its
next activation. A resident Durable Object moves to the new deployment
at a safe point: no request runs in it, no alarm handler runs in it, no
output waits for durability, and no regular WebSocket is open. A request
that arrives while the object moves waits for the new code. The move
keeps the object's storage, its epoch, and its hibernatable WebSockets,
and it does not read or write the bucket. An object that reaches no safe
point in CELLD_DEPLOY_MAX_AGE_S seconds (default 60) is forced: celld
cancels its running work and closes its regular WebSockets with code
1012, which is what a Cloudflare deployment does to every object. A
value of 0 forces every resident object at the adoption. In this window,
a request on one deployment can call a Durable Object on the other, so
two adjacent versions must accept each other's calls. The /state
response reports the deployment a node serves, the deployments it still
drains, the objects that are moving, and the deployment each resident
object runs.非驻留的 Durable Object 会在下一次激活时运行新部署。驻留的 Durable Object 会在安全点移动到新部署:即没有运行中的请求、没有运行中的闹钟处理程序、没有等待持久化的输出,且没有打开的常规 WebSocket。在对象移动期间到达的请求会等待新代码。移动过程保留对象的存储、纪元及其可休眠的 WebSocket,且不会读取或写入存储桶。在 CELLD_DEPLOY_MAX_AGE_S 秒(默认 60)内未达到安全点的对象将被强制移动:celld 取消其正在运行的工作并以 1012 代码关闭其常规 WebSocket,这与 Cloudflare 部署对每个对象的操作一致。值为 0 会在采用时强制所有驻留对象移动。在此窗口期,一个部署上的请求可以调用另一个部署上的 Durable Object,因此两个相邻版本必须接受彼此的调用。/state 响应报告节点服务的部署、仍在排空的部署、正在移动的对象以及每个驻留对象运行的部署。
Develop an application locally本地开发应用程序
Run celld dev in a Wrangler project:在 Wrangler 项目中运行 celld dev:
celld dev
The command opens a local object store, deploys the application, and starts one celld node. It does not require Docker or a cloud bucket. The supervisor stops the node when the command finishes or returns an error. On Linux, the kernel also stops the node if the supervisor exits abruptly.该命令打开一个本地对象存储,部署应用程序,并启动一个 celld 节点。它不需要 Docker 或云存储桶。当命令完成或返回错误时,监控程序会停止该节点。在 Linux 上,如果监控程序突然退出,内核也会停止该节点。
The Worker listener uses http://127.0.0.1:9876 by default. Use --port to
select a different port:Worker 监听器默认使用 http://127.0.0.1:9876。使用 --port 选择不同的端口:
celld dev --port 3000
Use --host to select the Worker listener interface:使用 --host 选择 Worker 监听器接口:
celld dev --host 0.0.0.0
A non-loopback IP exposes the Worker listener to the network. The internal operator listener stays on loopback, so another machine cannot use its operator API.非回环 IP 会将 Worker 监听器暴露给网络。内部操作员监听器保持在回环地址上,因此其他机器无法使用其操作员 API。
The default display uses color to identify each status and the application
URL. It hides the node warning and information logs, so the errors and the
listener remain easy to find. Use --logs to show these logs:默认显示使用颜色来标识每个状态和应用程序 URL。它隐藏了节点警告和信息日志,以便于查找错误和监听器。使用 --logs 显示这些日志:
celld dev --logs
Set NO_COLOR to disable color. Set FORCE_COLOR to enable color when
the output is not a terminal. NO_COLOR always takes priority when both
variables are set.设置 NO_COLOR 可禁用颜色。设置 FORCE_COLOR 可在输出非终端时启用颜色。当同时设置这两个变量时,NO_COLOR 总是优先。
You can also give the command a project directory or a Wrangler configuration file:您还可以为命令提供项目目录或 Wrangler 配置文件:
celld dev ./examples/counter
The command reads a .dev.vars file beside the Wrangler configuration, as
wrangler dev does. Each line of the file has the form NAME=value. A
value can be wrapped in double or single quotes, and the command removes
the quotes. The command does not read the other dotenv features, such as a
comment after a value or a value on more than one line. An entry becomes a
Worker variable, and it overrides an entry of the same name in the vars
of the configuration. Only celld dev reads the file, so a local
credential does not reach a fleet through celld deploy. Add .dev.vars
to the application's .gitignore file. A change to the file rebuilds the
application, so a new value takes effect without a restart. The command
writes each value into the local deployment record under .celld/dev, and
--clean deletes that record.该命令会像 wrangler dev 一样读取 Wrangler 配置旁边的 .dev.vars 文件。文件的每一行格式为 NAME=value。值可以用双引号或单引号包裹,命令会移除引号。该命令不会读取其他 dotenv 特性,例如值后的注释或跨行的值。条目将成为 Worker 变量,并覆盖配置 vars 中同名的条目。只有 celld dev 会读取该文件,因此本地凭证不会通过 celld deploy 到达集群。请将 .dev.vars 添加到应用程序的 .gitignore 文件中。对文件的更改会重新构建应用程序,因此新值无需重启即可生效。该命令将每个值写入 .celld/dev 下的本地部署记录中,--clean 会删除该记录。
The command stores the local objects and the celld work files in .celld/dev
below the project directory. Add .celld/ to the application's .gitignore
file. A normal shutdown keeps this directory, so the next invocation uses the
same durable application state.该命令将本地对象和 celld 工作文件存储在项目目录下的 .celld/dev 中。请将 .celld/ 添加到应用程序的 .gitignore 文件中。正常关闭会保留此目录,因此下一次调用会使用相同的持久化应用程序状态。
This persistence holds across a configuration change, and celld does not
migrate the stored state to match the new configuration. An object therefore
keeps a value that the previous configuration wrote, and the application can
reject that value and fail. The error names the stored value and not the
configuration, so the failure can look unrelated to the change you made. Use
--clean to start the command from an empty local state:这种持久性在配置更改后依然存在,celld 不会将存储的状态迁移以匹配新配置。因此,对象会保留先前配置写入的值,而应用程序可能会拒绝该值并失败。错误名称指向存储的值而非配置,因此失败看起来可能与您所做的更改无关。使用 --clean 从空的本地状态启动命令:
celld dev --clean
The flag deletes .celld/dev before the server starts, and it deletes nothing
else below .celld. The command reports the outcome on the console. An absent
.celld/dev directory is the wanted result, so the flag succeeds on a project
that never ran celld dev.该标志在服务器启动前删除 .celld/dev,且不会删除 .celld 下的其他内容。命令会在控制台上报告结果。不存在 .celld/dev 目录是预期的结果,因此该标志在从未运行过 celld dev 的项目上也会成功。
The command does not expose the local object store through a fleet flag. A regular node or an operator subcommand must use a supported cloud bucket.该命令不会通过集群标志暴露本地对象存储。常规节点或操作员子命令必须使用受支持的云存储桶。
The command watches the project directory. A source or configuration change builds a new deployment and restarts the local node. The current application continues to run during the build, and a failed build does not replace it. The restart retains the durable application state.该命令会监视项目目录。源代码或配置的更改会构建新的部署并重启本地节点。当前应用程序在构建期间继续运行,失败的构建不会替换它。重启会保留持久化的应用程序状态。
A read of a project file is not a change, so a tool that only reads the project does not start a build.对项目文件的读取不是更改,因此仅读取项目的工具不会启动构建。
The watcher ignores .celld, .git, .wrangler, node_modules, and target
directories at each depth. Use --watch-ignore PATTERN to ignore an additional
project-relative glob, and repeat the option to add more globs. The
--watch-ignore option cannot be used with --no-watch. Use --no-watch to
disable all automatic builds and restarts.监视器在每个深度忽略 .celld、.git、.wrangler、node_modules 和 target 目录。使用 --watch-ignore PATTERN 忽略额外的项目相对 glob,并重复该选项以添加更多 glob。--watch-ignore 选项不能与 --no-watch 一起使用。使用 --no-watch 禁用所有自动构建和重启。
The watcher does not watch a source file outside the project directory. Worker
projects need esbuild on PATH, and asset-only projects do not need it.监视器不会监视项目目录之外的源文件。Worker 项目需要在 PATH 中安装 esbuild,纯资源项目则不需要。
Operate D1, KV, and R2
celld d1 runs SQL and migrations against a deployed D1 database. The
command uses the fleet bucket to find a node, and the node routes the
operation to the database cell.celld d1 针对已部署的 D1 数据库运行 SQL 和迁移。该命令使用集群存储桶查找节点,节点将操作路由到数据库单元。
celld d1 migrations apply ledger --bucket "$CELLD_BUCKET"
The migrations_dir value must be a relative path inside the project. An
absolute path or a path with a .. component is invalid.migrations_dir 值必须是项目内的相对路径。绝对路径或包含 .. 组件的路径无效。
celld kv reads and writes a deployed KV namespace. The bulk commands use
the Wrangler file format, so wrangler kv bulk get can export data for
celld kv bulk put.celld kv 读取和写入已部署的 KV 命名空间。批量命令使用 Wrangler 文件格式,因此 wrangler kv bulk get 可以导出数据供 celld kv bulk put 使用。
celld kv bulk put sessions wrangler-export.json --bucket "$CELLD_BUCKET"
celld kv bulk get writes each row before it reads the next value, so the
command does not retain the complete namespace. A named output file changes
only after the export completes. If a stdout export fails, the output remains
an incomplete JSON array and a consumer cannot import it as a complete export.celld kv bulk get 在读取下一个值之前写入每一行,因此该命令不会保留整个命名空间。命名输出文件仅在导出完成后才会更改。如果 stdout 导出失败,输出将保持为一个不完整的 JSON 数组,消费者无法将其作为完整导出导入。
celld kv list prints at most 1000 keys, because a namespace can hold
many more keys than an operator wants to read. The command reports on
stderr that more keys exist, and it gives the --after KEY that continues
the listing. Pass --all to read every key, or --json for one JSON
object per key.celld kv list 最多打印 1000 个键,因为命名空间中可能包含比操作员想要读取的更多的键。命令会在 stderr 上报告存在更多键,并提供继续列出的 --after KEY。传递 --all 读取所有键,或 --json 以每个键一个 JSON 对象的形式输出。
celld r2 reads and writes the objects behind an r2_buckets binding. The
binding serves the fleet bucket under the reserved r2/<bucket_name>/
prefix, and this command reads the same prefix directly. It therefore needs
no running node, so a release pipeline can publish an artifact before it
deploys the Worker.celld r2 读取和写入 r2_buckets 绑定背后的对象。绑定在保留的 r2/<bucket_name>/ 前缀下服务于集群存储桶,该命令直接读取相同前缀。因此它不需要运行节点,发布流水线可以在部署 Worker 之前发布工件。
celld r2 put assets app.zip --path dist/app.zip \
--content-type application/zip \
--metadata '{"release":"1.2.3"}' \
--bucket "$CELLD_BUCKET"
The first argument is the bucket_name from the project's r2_buckets
entry, not the binding name. An object this command writes carries the same
record as an object env.BUCKET.put() writes, so --metadata becomes
customMetadata and the content flags become httpMetadata. Another tool
can write an object into the prefix, and the binding still reads it: the
object's user metadata becomes its customMetadata, and its headers become
its httpMetadata. Such an object carries no cacheExpiry and no
checksums, because a store has no place for either.第一个参数是项目 r2_buckets 条目中的 bucket_name,而不是绑定名称。此命令写入的对象带有与 env.BUCKET.put() 写入的对象相同的记录,因此 --metadata 变为 customMetadata,内容标志变为 httpMetadata。其他工具可以将对象写入该前缀,绑定仍然可以读取它:对象的用户元数据变为其 customMetadata,其标头变为其 httpMetadata。此类对象不携带 cacheExpiry 和校验和,因为存储中没有地方存放它们。
celld r2 get writes the object to stdout as bytes, and it reads the body
as a stream, so an artifact larger than the available memory still
transfers. celld r2 list prints at most 1000 keys and reports on stderr
how to continue, the same as celld kv list. The celld r2 head command
prints the object's stored record. With --json, the http and custom
fields contain the binding's httpMetadata and customMetadata values.celld r2 get 将对象作为字节写入 stdout,并将主体作为流读取,因此大于可用内存的工件仍然可以传输。celld r2 list 最多打印 1000 个键并在 stderr 上报告如何继续,与 celld kv list 相同。celld r2 head 命令打印对象的存储记录。使用 --json 时,http 和 custom 字段包含绑定的 httpMetadata 和 customMetadata 值。
Every celld command writes its data to stdout and its messages to stderr, so a redirect or a pipe carries only data:每个 celld 命令将其数据写入 stdout,消息写入 stderr,因此重定向或管道仅携带数据:
celld kv list sessions --all --json --bucket "$CELLD_BUCKET" > keys.ndjson
celld --help, celld --version, and the listener announcements use the same
stdout sink. The sink treats a closed pipe as a successful stop, and it reports
all other write and flush errors.celld --help、celld --version 和监听器公告使用相同的 stdout 接收器。接收器将关闭的管道视为成功的停止,并报告所有其他写入和刷新错误。
Start a node
For local development, the default listener is sufficient:对于本地开发,默认监听器已足够:
celld \
--bucket "$CELLD_BUCKET" \
--endpoint "$S3_ENDPOINT" \
--region "$AWS_REGION"
For a fleet node, bind the public and internal listeners separately. The ingress can reach the public listener, and the other nodes can reach the internal listener:对于集群节点,请分别绑定公共和内部监听器。入口可以到达公共监听器,其他节点可以到达内部监听器:
celld \
--bucket "$CELLD_BUCKET" \
--endpoint "$S3_ENDPOINT" \
--region "$AWS_REGION" \
--listen 0.0.0.0:8080 \
--internal-listen 10.0.0.12:8081 \
--advertise node-a.internal:8081
An explicit advertised address requires an explicit internal-listener address: set both command-line options, or their equivalent environment variables. celld also rejects an explicit non-loopback public listener without an explicit internal listener, because that shape identifies an obsolete one-listener configuration.显式的通告地址需要显式的内部监听器地址:设置两个命令行选项或其等效的环境变量。celld 还会拒绝没有显式内部监听器的显式非回环公共监听器,因为这种配置标识的是过时的单监听器模式。
celld cannot verify that an advertised hostname or a translated port reaches the internal listener. You must route the advertised address to the internal listener, and you must not route it to the public Worker listener.celld 无法验证通告的主机名或转换后的端口是否能到达内部监听器。您必须将通告地址路由到内部监听器,且绝不能将其路由到公共 Worker 监听器。
Add nodes
Start each node with the same bucket settings. Give each internal listener
a different address that the other nodes can reach, and set --advertise
to that address. The nodes find each other through the leases in the
bucket; there is no join command and no fixed membership list.使用相同的存储桶设置启动每个节点。为每个内部监听器提供其他节点可以到达的不同地址,并将 --advertise 设置为该地址。节点通过存储桶中的租约相互发现;没有 join 命令,也没有固定的成员列表。
The bucket supplies discovery and authority, not network reachability. Cell fetch and RPC traffic has a protocol version but has no content signature, so the private network and its code are the security boundary. Peer-control and reserved-cell operator requests use the fleet HMAC, but celld does not terminate TLS. Put the advertised addresses on a private network that you trust, or use an encrypted overlay such as WireGuard or Tailscale. The internal listener also has an unauthenticated operator API, so it must not reach the public internet. See the security page for the complete boundary.存储桶提供发现和授权,而非网络可达性。单元获取和 RPC 流量具有协议版本,但没有内容签名,因此私有网络及其代码是安全边界。对等控制和保留单元操作员请求使用集群 HMAC,但 celld 不终止 TLS。将通告地址放在您信任的私有网络上,或使用加密覆盖网络(如 WireGuard 或 Tailscale)。内部监听器还有一个未经身份验证的操作员 API,因此绝不能将其暴露给公共互联网。请参阅安全页面了解完整的边界。
The listener that receives a request decides where a new cell activates,
so a load balancer must include a new node in its rotation. A node below
its residency cap keeps every cell that activates on it. A node at its
cap, and a node that sheds under memory pressure, hands the activation to
the peer with the lowest load, and an empty node wins that comparison. A
shed eviction releases the ownership record
(CELLD_PRESSURE_OWNERSHIP=release, the default), so the next request
can move the cell. An idle eviction keeps the ownership record, so an
idle cell wakes on its own node. The node proves the cell durable before
it stops the runtime, and the cell answers requests through that proof. A
request that arrives before the stop therefore cancels the eviction, and
the cell keeps its runtime and its epoch. A bucket proof confirms the
cell's objects before the stop, so it makes this first window longer than
a fleet proof does. A request that arrives during the stop can also keep
the epoch. The node stops the runtime, and then it writes a handoff
snapshot to the bucket. Before that write the node has published nothing,
so it gives the cell back and starts it again on the same database at the
same epoch. After that write the snapshot is the restore source for the
next owner, so a request waits for the cell to start again at a new
epoch. The two durability postures behave the same way during the stop,
because both write the same snapshot at the same point. An idle eviction
must stop the runtime within CELLD_OPERATION_DEADLINE_MS. If the
runtime does not stop in time, the node keeps the cell resident at its
current epoch. The node waits one more idle period before the next
attempt, and it waits the same period after it gives a cell back.接收请求的监听器决定新单元在何处激活,因此负载均衡器必须将新节点纳入其轮询。低于驻留上限的节点会保留在其上激活的每个单元。达到上限的节点,以及在内存压力下进行驱逐的节点,会将激活移交给负载最低的对等节点,空节点在比较中胜出。压力驱逐会释放所有权记录(CELLD_PRESSURE_OWNERSHIP=release,默认值),因此下一个请求可以移动该单元。空闲驱逐保留所有权记录,因此空闲单元会在其原节点上唤醒。节点在停止运行时之前会证明单元是持久的,单元通过该证明响应请求。因此,在停止前到达的请求会取消驱逐,单元保留其运行时和纪元。存储桶证明在停止前确认单元的对象,因此它使此第一个窗口比集群证明更长。在停止期间到达的请求也可以保留纪元。节点停止运行时,然后向存储桶写入移交快照。在该写入之前,节点尚未发布任何内容,因此它归还单元并在同一纪元下的同一数据库上再次启动它。在该写入之后,快照成为下一个所有者的恢复源,因此请求会等待单元在新的纪元再次启动。两种持久化姿态在停止期间表现相同,因为两者都在同一点写入相同的快照。空闲驱逐必须在 CELLD_OPERATION_DEADLINE_MS 内停止运行时。如果运行时未及时停止,节点会将单元保留在当前纪元。节点在下一次尝试前等待一个空闲周期,在归还单元后也等待相同的周期。
A new node also receives existing idle cells. Every node reads a shared
fleet sample every five seconds (CELLD_REBALANCE_INTERVAL_MS; 0
disables balancing). One node claims each refresh through a conditional
write and reads the node leases for the fleet. The other nodes read
the result from fleet/capacity-v1.json. A node judges the leases in a
sample at the moment the sample was taken, because the copied leases age
while the nodes renew the originals. A sample expires after one
configured interval. A node that reads during a refresh keeps the
previous sample while that sample is less than three intervals old. A
refresher that fails puts the previous sample back, so the next reader
refreshes at once. A refresher that stops holds its claim for three
intervals, or for 30 seconds if that period is shorter. This claim
lifetime does not exceed the maximum accepted age of a previous sample.
A stopped refresher can leave the other nodes without a sample until the
claim expires. An unavailable sample stops balancing and closes the
bucket-format gate.
With balancing disabled, the format check still reads the shared sample
every five seconds. A node that hands an activation to a peer reads the
same shared sample, so a burst of activations at a full node costs one
read each. Readiness, a drain, follower recruitment, and lease authority
read the node leases directly.新节点也会接收现有的空闲单元。每个节点每五秒读取一次共享集群样本 (CELLD_REBALANCE_INTERVAL_MS;0 禁用平衡)。一个节点通过条件写入声明每次刷新,并读取集群的节点租约。其他节点从 fleet/capacity-v1.json 读取结果。节点在样本采集时刻评估样本中的租约,因为复制的租约在节点续期原始租约时会老化。样本在配置的一个间隔后过期。在刷新期间读取的节点会在样本年龄小于三个间隔时保留之前的样本。失败的刷新器会将之前的样本放回,因此下一个读取者会立即刷新。停止的刷新器会保持其声明三个间隔,如果该周期较短则为 30 秒。此声明生命周期不超过之前样本允许的最大年龄。停止的刷新器可能会让其他节点在声明过期前没有样本。不可用的样本会停止平衡并关闭存储桶格式门。在禁用平衡的情况下,格式检查仍每五秒读取一次共享样本。将激活移交给对等节点的节点会读取相同的共享样本,因此满载节点上的突发激活每次读取一次。就绪性、排空、追随者招募和租约授权直接读取节点租约。
Each node has an ownership target: the fleet's owned cells, divided in
proportion to the node weights
(CELLD_PLACEMENT_WEIGHT, default: the CPU count). The node with the
most owned cells per unit of weight hands at most 32 idle cells
to the peer that is furthest below its
target, and it hands over no more cells than the receivers have room
for. A receiver fills to 2% below its target, so a stale sample cannot
make two nodes trade the same cells. Only a hibernated cell moves. Its
move is one ownership record write and one signed acquire, it stays
hibernated on the new owner, and its parked hibernatable WebSockets close
with code 1012 so the clients reconnect to the new owner. A resident cell
does not move: it hibernates through idle eviction first
(CELLD_IDLE_EVICT_S) and moves after. Therefore a fleet without idle
eviction balances only the cells that hibernate on their own. A request
that arrives for a moving cell waits for the new owner and then follows
the normal route to it. A node that drains receives no cell, and a node
with cold activations queued behind its activation ceiling (restoring in
its lease) receives no cell until that backlog clears. The fleet moves nothing while a
node reports a lease without a weight, so a rolling upgrade to this
version completes before the first move. An operator can pause the fleet with
POST /rebalance/pause on the internal listener of any node.每个节点都有一个所有权目标:集群拥有的单元,按节点权重 (CELLD_PLACEMENT_WEIGHT,默认:CPU 核心数) 分配。每单位权重拥有单元数最多的节点最多向最远低于其目标的对等节点移交 32 个空闲单元,且移交的单元数不超过接收者的容量。接收者填充至其目标的 2% 以下,因此陈旧的样本不会导致两个节点交易相同的单元。仅休眠单元会移动。其移动是一次所有权记录写入和一次签名获取,它在新的所有者上保持休眠,且其驻留的可休眠 WebSocket 以 1012 代码关闭,以便客户端重新连接到新的所有者。驻留单元不会移动:它首先通过空闲驱逐 (CELLD_IDLE_EVICT_S) 进入休眠,然后再移动。因此,没有空闲驱逐的集群仅平衡那些自行休眠的单元。为正在移动的单元到达的请求会等待新的所有者,然后沿正常路径到达它。正在排空的节点不接收单元,在激活上限后排队冷激活(在其租约中恢复)的节点在积压清除前不接收单元。当节点报告没有权重的租约时,集群不移动任何内容,因此该版本的滚动升级在第一次移动前完成。操作员可以通过任何节点的内部监听器上的 POST /rebalance/pause 暂停集群。
Feed an autoscaler为自动缩放器提供数据
celld does not scale itself. An external system starts and stops the nodes, and celld hands the cells off. Two surfaces report the numbers that a scaling decision needs.celld 不会自行缩放。外部系统启动和停止节点,celld 进行单元移交。两个表面报告缩放决策所需的数字。
Each node writes its lease to nodes/<node>.json in the bucket, and
each renewal refreshes it (CELLD_TTL_MS, default 10000, renewed at
one third of the lifetime). The lease carries a load block:
owned_cells, placement_weight, resident_cells, host_websockets,
rss_bytes, in_use_bytes, cpu_percent_x100, open_fds, pressured,
memory_headroom, shed_cells, and restoring. A script can read these
objects with the
bucket credentials alone, and sampled_ms dates each sample. The
owned_cells value includes the dormant cells and the cells that are waking
from dormancy. A started node omits the value until it has read the
ownership record of every cell on its own disk, so a restarted node does
not report an empty node while it still owns its hibernated cells. The
resident_cells value includes only the cells that consume the local
runtime capacity.每个节点将其租约写入存储桶中的 nodes/<node>.json,每次续期都会刷新它 (CELLD_TTL_MS,默认 10000,在生命周期的三分之一处续期)。租约携带一个负载块:owned_cells、placement_weight、resident_cells、host_websockets、rss_bytes、in_use_bytes、cpu_percent_x100、open_fds、pressured、memory_headroom、shed_cells 和 restoring。脚本可以仅凭存储桶凭证读取这些对象,sampled_ms 标记每个样本的日期。owned_cells 值包括休眠单元和正在从休眠中唤醒的单元。已启动的节点在读取其磁盘上每个单元的所有权记录之前会省略该值,因此重启的节点在仍拥有其休眠单元时不会报告为空节点。resident_cells 值仅包括消耗本地运行时容量的单元。
The shared runtime samples the process memory, open files, and CPU every
second, and once before the first lease acquisition. The lease and /state
report these values from the latest sample, so they are normally one sample
period old. The allocator and libc_malloc objects in /state are read
when the request arrives.共享运行时每秒对进程内存、打开文件和 CPU 进行采样,并在第一次租约获取前采样一次。租约和 /state 从最新样本报告这些值,因此它们通常有一个采样周期的延迟。/state 中的 allocator 和 libc_malloc 对象在请求到达时读取。
The internal listener answers GET /state with the live counters:
owned_cells, occupied, capacity_waiting, activation_waiting, restoring,
shedding, and the memory values. The allocator object reports the
Rust allocator's own counters in bytes: allocated_bytes is the memory
that Rust code holds, resident_bytes adds the freed pages that the
allocator keeps for reuse, and mapped_bytes and retained_bytes are its
address space. V8 maps its heaps itself, so rss_bytes includes them and
allocated_bytes does not. On Linux the libc_malloc object reports the
C allocator that SQLite and V8 use: in_use_bytes is what they hold, and
free_bytes is what glibc keeps for them after a free. The handed_off counter is the number of
cells this node gave to a peer, rebalanced is the part of that number
that balancing moved, and rebalance_failed is the number of balancing
moves that no peer acquired. A positive capacity_waiting means
that the node queues activations behind its residency cap, and that is
the direct signal to add a node. A pressured lease is the memory form
of the same signal. Scale down only when every remaining node reports
memory_headroom and a small restoring backlog. A drain into a fleet
without spare capacity parks the cells dormant on the survivors, and
each later activation must then shed a resident cell to run.内部监听器通过 GET /state 回答实时计数器:owned_cells、occupied、capacity_waiting、activation_waiting、restoring、shedding 以及内存值。allocator 对象以字节为单位报告 Rust 分配器自己的计数器:allocated_bytes 是 Rust 代码持有的内存,resident_bytes 加上分配器保留以供重用的空闲页面,mapped_bytes 和 retained_bytes 是其地址空间。V8 自行映射堆,因此 rss_bytes 包含它们而 allocated_bytes 不包含。在 Linux 上,libc_malloc 对象报告 SQLite 和 V8 使用的 C 分配器:in_use_bytes 是它们持有的内存,free_bytes 是 glibc 在释放后为它们保留的内存。handed_off 计数器是该节点移交给对等节点的单元数,rebalanced 是该数字中平衡移动的部分,rebalance_failed 是没有对等节点获取的平衡移动数。正数的 capacity_waiting 意味着节点在驻留上限后排队激活,这是添加节点的直接信号。pressured 租约是相同信号的内存形式。仅当每个剩余节点都报告 memory_headroom 和较小的 restoring 积压时才进行缩减。排空到没有剩余容量的集群中会将单元停在幸存者节点上,此后的每次激活都必须驱逐一个驻留单元才能运行。
The remote_route_refreshes counter counts cached owner or capacity routes
that expire and start a new lookup. It increases once for each retired
route, so it also increases during normal lease renewal. An adoption
without a lease and an explicit invalidation do not increase it.remote_route_refreshes 计数器统计过期并启动新查找的缓存所有者或容量路由。它每退役一个路由增加一次,因此在正常租约续期期间也会增加。没有租约的采用和显式失效不会增加它。
The deployment.isolates object reports the V8 isolates of the current
deployment, and each entry of deployment.draining reports the isolates
of one superseded deployment. The cells map has one entry for each
Worker script, and the stateless and services entries cover the
request handlers. Each entry counts the isolates that accept work
(live), the live isolates that house no cell (live_empty), the
retiring isolates that still hold a heap (retiring), and the freed
slots (freed), with the cells, requests, and turns that they hold.
heap_bytes is the physical memory that V8 has committed to those heaps,
and external_bytes is the memory that the isolates track outside them. A
dormant cell holds no isolate, and celld retires an empty cell isolate on
its next maintenance pass, so a live_empty count that persists for more
than 30 seconds means that the pass does not run. A retiring count that
persists means that a turn or a request still holds the heap.deployment.isolates 对象报告了当前部署的 V8 隔离区(isolates),而 deployment.draining 的每个条目则报告了被取代部署的隔离区情况。cells 映射表中的每个条目对应一个 Worker 脚本,stateless 和 services 条目则涵盖了请求处理程序。每个条目都会统计接受工作的隔离区(live)、不包含单元(cell)的活动隔离区(live_empty)、仍然保留堆内存的退役隔离区(retiring),以及已释放的插槽(freed),并列出它们所持有的单元、请求和轮次(turns)。heap_bytes 是 V8 已分配给这些堆的物理内存,external_bytes 是隔离区在自身之外追踪的内存。休眠单元不持有隔离区,celld 会在下一次维护周期中退役空的单元隔离区,因此如果 live_empty 计数持续超过 30 秒,意味着该维护周期未运行。如果 retiring 计数持续存在,则意味着某个轮次或请求仍在使用该堆内存。
The public /.well-known/celld/health path stays a boolean. It reports
503 during a drain and before a joining node settles, so an
orchestrator can gate a rolling update on it, but it carries no
utilization number.公共路径 /.well-known/celld/health 保持为布尔值。它在排空(drain)期间以及加入的节点稳定之前返回 503,因此编排器可以据此对滚动更新进行门控,但它不包含利用率数值。
Shut down and roll out a node
celld shuts a node down gracefully on SIGTERM or SIGINT, the signals that
systemctl stop, docker stop, and a Kubernetes pod delete send. The
/.well-known/celld/health path reports the node as unhealthy, so a load balancer
stops public routing to it. New public requests receive a 503 response,
and celld closes each connection so the client can retry on a healthy
node. The node finishes the public HTTP requests that it accepted before
shutdown, and it continues to accept versioned peer traffic for cells that
it has not handed off.celld 会在收到 SIGTERM 或 SIGINT 信号时优雅地关闭节点,这些信号由 systemctl stop、docker stop 和 Kubernetes pod 删除命令发送。/.well-known/celld/health 路径会将节点报告为不健康,从而使负载均衡器停止向其进行公共路由。新的公共请求将收到 503 响应,并且 celld 会关闭每个连接,以便客户端可以在健康的节点上重试。节点会完成在关闭前已接受的公共 HTTP 请求,并继续接受尚未移交的单元的带版本对等流量。
You must set a longer orchestrator stop grace, such as systemd
TimeoutStopSec or Kubernetes terminationGracePeriodSeconds. The grace
must cover the drain-token wait, the expected complete handoff, and one
no-progress interval (the settings below). Otherwise, the orchestrator
can send SIGKILL before celld completes the handoff.你必须设置更长的编排器停止宽限期,例如 systemd 的 TimeoutStopSec 或 Kubernetes 的 terminationGracePeriodSeconds。该宽限期必须涵盖排空令牌等待时间(drain-token wait)、预期的完整移交过程以及一个无进展间隔(见下文设置)。否则,编排器可能会在 celld 完成移交前发送 SIGKILL 信号。
The handoff works in batches. The node reserves a batch of cells and stops new local routes to them: an accepted request stays local until it finishes, and a new request waits for the successor route. The node also cancels each firing alarm in the batch. The alarm runtime records a retry and arms its wake entry, so the successor can run the alarm at least once. The node cancels an active internal fetch or RPC handler before the durability proof, so a Queue broker cannot retain the batch until the process stops. The node proves the batch durable in the live durability ensemble, and it then closes each database. The node publishes one full L9 snapshot of the closed database and verifies that the bucket contains a restore object. The successor therefore does not replay the complete transaction history. If the snapshot fails, the node materializes the additive L0 chain within the same durability deadline. A database larger than the deadline can upload, 80 MiB at the default deadline of 10 seconds, skips the snapshot and hands off through its L0 chain at once, and a snapshot that fails once is not retried for that cell. The node does not release the cell unless one complete restore path is visible. The node then releases each ownership record and asks a compatible peer to acquire it. The peer acknowledges after the ownership update and keeps the cell dormant, so the handoff does not restore an unused runtime; a later request starts the cell under the peer activation limit. The donor starts the next batch after each acknowledgement.移交工作分批进行。节点会预留一批单元并停止向其发送新的本地路由:已接受的请求在完成前保持本地处理,新请求则等待后续路由。节点还会取消该批次中每个正在触发的警报。警报运行时会记录一次重试并武装其唤醒条目,以便后续节点至少能运行一次该警报。节点会在持久性证明之前取消活跃的内部获取或 RPC 处理程序,因此队列代理(Queue broker)无法在进程停止前保留该批次。节点在实时持久性集合中证明该批次的持久性,然后关闭每个数据库。节点发布已关闭数据库的一个完整 L9 快照,并验证存储桶(bucket)中是否包含还原对象。因此,后续节点无需重放完整的事务历史。如果快照失败,节点会在相同的持久性期限内实现增量 L0 链。如果数据库大小超过了期限(默认 10 秒期限下为 80 MiB),则会跳过快照并立即通过其 L0 链进行移交,且一旦快照失败,该单元将不再进行重试。除非可见一条完整的还原路径,否则节点不会释放该单元。随后,节点释放每条所有权记录,并请求兼容的对等节点获取它。对等节点在更新所有权后进行确认,并使该单元保持休眠状态,因此移交不会还原未使用的运行时;后续的请求会在对等节点的激活限制下启动该单元。捐赠节点在每次确认后启动下一批次。
celld orders the resident cells by their local request count. The newest local request ID resolves an equal count, and the cell ID resolves an unused cell. Therefore, a total stop bound moves shared resources before one-time resources.celld 根据本地请求计数对驻留单元进行排序。最新的本地请求 ID 解析出相等的计数,单元 ID 则解析出未使用的单元。因此,总停止边界会先移动共享资源,再移动一次性资源。
CELLD_RELEASES limits the complete handoffs in progress (default 128).
Each handoff includes activity cancellation, a durability proof, a final
snapshot, an ownership release, and a successor update. CELLD_ACTIVATIONS
limits the demand-driven restores and the startup work.CELLD_RELEASES 限制了正在进行的完整移交数量(默认 128)。每次移交包括活动取消、持久性证明、最终快照、所有权释放和后续节点更新。CELLD_ACTIVATIONS 限制了需求驱动的还原和启动工作。
CELLD_SHUTDOWN_TOTAL_MS sets the complete process stop bound (default
40000 ms). The orchestrator stop grace must exceed this bound, so celld can
finish its local durability shutdown. celld derives its internal waits from
this one value. The token wait is three quarters of the bound (default
30000 ms). The handoff no-progress interval is five eighths of the bound
(default 25000 ms), with a minimum of one millisecond. Fractional milliseconds
are rounded down before the minimum applies.CELLD_SHUTDOWN_TOTAL_MS 设置了完整的进程停止边界(默认 40000 毫秒)。编排器的停止宽限期必须超过此边界,以便 celld 能够完成其本地持久性关闭。celld 从该值推导出其内部等待时间。令牌等待时间为边界的四分之三(默认 30000 毫秒)。移交无进展间隔为边界的八分之五(默认 25000 毫秒),最小值为 1 毫秒。在应用最小值之前,小数毫秒会被向下取整。
Each successor acknowledgement restarts the no-progress interval. The total stop bound still limits the complete shutdown, so progress cannot extend the process lifetime beyond the configured grace. A handoff reserves the smaller of one second and ten percent of the total bound for the drain-token release. A same-node preserve operation uses the no-progress interval as its semantic limit because it has no successor acknowledgements. The token-release reserve does not apply to a same-node preserve.每次后续节点的确认都会重置无进展间隔。总停止边界仍然限制了完整的关闭过程,因此进展无法将进程生命周期延长至超过配置的宽限期。移交为排空令牌释放预留了 1 秒和总边界 10% 中的较小值。同节点保留操作使用无进展间隔作为其语义限制,因为它没有后续节点的确认。令牌释放预留不适用于同节点保留操作。
celld rejects the removed CELLD_SHUTDOWN_DRAIN_MS and
CELLD_DRAIN_TOKEN_WAIT_MS variables at startup. Remove these variables and
set only CELLD_SHUTDOWN_TOTAL_MS when the default total bound is unsuitable.
The CELLD_PACED_HANDOFF switch is also removed. A bucket-backed node
attempts ownership handoff within this budget. If no successor can accept
the cells, the existing no-progress and total stop bounds still apply.celld 在启动时会拒绝已移除的 CELLD_SHUTDOWN_DRAIN_MS 和 CELLD_DRAIN_TOKEN_WAIT_MS 变量。请移除这些变量,并在默认总边界不合适时仅设置 CELLD_SHUTDOWN_TOTAL_MS。CELLD_PACED_HANDOFF 开关也已被移除。基于存储桶的节点会在此预算内尝试所有权移交。如果没有后续节点可以接受这些单元,现有的无进展间隔和总停止边界依然适用。
Simultaneous stop signals do not flood the surviving nodes. A draining node claims a fleet drain token before it releases cells, so concurrent donors hand off one node at a time. A donor that cannot claim the token within its derived wait proceeds without it because the orchestrator grace is finite. The token is advisory: a dead holder's claim expires, and a handoff without the token is still safe.同时发生的停止信号不会淹没存活节点。排空节点在释放单元前会声明一个集群排空令牌,因此并发的捐赠节点每次只移交一个节点。无法在其推导出的等待时间内声明令牌的捐赠节点将不带令牌继续执行,因为编排器的宽限期是有限的。令牌仅供参考:死掉的持有者的声明会过期,且不带令牌的移交依然是安全的。
A fresh process also holds its first healthy response until the fleet is
settled. The process requires its live node lease, no active donor, and memory
below every pressure low watermark on each live node. A donor records each
node's restore backlog before it releases ownership. The replacement permits
one CELLD_ACTIVATIONS budget above each recorded value. The incumbent
ownership counts must remain within one equal successor share above the fleet
mean. A joining process can satisfy this condition when it advertises paced
handoff support and owns fewer cells than the busiest incumbent. The process
publishes this successor capacity before readiness, so an idle rollout can
repair the ownership distribution during the next donor handoff.新进程在集群稳定之前也会保持其第一个健康响应。该进程需要其活跃节点租约、无活跃捐赠节点,且每个活跃节点上的内存低于所有压力低水位线。捐赠节点在释放所有权前会记录每个节点的还原积压情况。替换节点允许在每个记录值之上增加一个 CELLD_ACTIVATIONS 预算。现有的所有权计数必须保持在集群平均值之上一个相等的后续节点份额内。当加入的进程声明支持分步移交且拥有的单元少于最繁忙的现有节点时,它可以满足此条件。进程在就绪前发布此后续节点容量,因此空闲的上线过程可以在下一次捐赠移交期间修复所有权分布。
An initial spare has no drain token, so it has no rollout restore baseline to check. It still requires memory headroom and acceptable ownership distribution.初始备用节点没有排空令牌,因此没有需要检查的上线还原基准。它仍然需要内存余量和可接受的所有权分布。
An older peer does not publish the low-watermark result, so the gate uses that
peer's pressure latch during a mixed-version update. An unreadable fleet or an
unsettled condition keeps the process unhealthy until the condition clears.
CELLD_READY_FLEET_GATE_MS sets the observation deadline in milliseconds
(default 120000; 0 disables the gate). celld emits ready_gate_expired once
at that deadline, but readiness stays closed. Set an orchestrator rollout
deadline so a persistent capacity problem fails the rollout. After the first
healthy response, fleet state does not remove readiness again.较旧的对等节点不会发布低水位线结果,因此门控在混合版本更新期间使用该对等节点的压力锁存器。不可读的集群或不稳定的状态会使进程保持不健康,直到条件清除。CELLD_READY_FLEET_GATE_MS 设置观测期限(毫秒,默认 120000;0 禁用该门控)。celld 在该期限到达时触发一次 ready_gate_expired,但就绪状态保持关闭。请设置编排器上线期限,以便持续的容量问题导致上线失败。在第一次健康响应之后,集群状态不会再次移除就绪状态。
A deadline-cut handoff can leave a node-log recovery for the replacement. One process reads and uploads that dead session, and the other processes wait for its result. A waiting process can replace an unresponsive recovery after 30 seconds, so a failed recovery cannot block the fleet permanently. A recovery store error keeps the session unsealed, so a later recovery must read every retained bundle before the replacement can restore the acknowledged state.截止期限前的移交可能会为替换进程留下节点日志恢复任务。一个进程读取并上传该死掉的会话,其他进程等待其结果。等待进程可以在 30 秒后替换无响应的恢复任务,因此失败的恢复任务不会永久阻塞集群。恢复存储错误会使会话保持未密封状态,因此后续的恢复任务必须在替换进程恢复已确认状态前读取每个保留的包。
Recovery records a checkpoint after each group of 32 completed cell epochs. A later attempt uses these checkpoints to skip repeated coverage checks and uploads for the recorded transactions. Each checkpoint follows the data uploads, so an interrupted upload cannot create a checkpoint for incomplete data. An unavailable or invalid checkpoint causes recovery to check the cell coverage again. A checkpoint write failure does not stop recovery. The checkpoints do not replace the follower evidence or the session seal, and different sessions continue to recover independently.恢复任务在每组 32 个完成的单元纪元(epochs)后记录一个检查点。后续尝试使用这些检查点跳过已记录事务的重复覆盖检查和上传。每个检查点跟在数据上传之后,因此中断的上传无法为不完整的数据创建检查点。不可用或无效的检查点会导致恢复任务重新检查单元覆盖范围。检查点写入失败不会停止恢复。检查点不能替代跟随者证据或会话密封,不同的会话继续独立恢复。
The internal listener continues to accept /state requests during the
drain, and the response reports the handoff and restore counters. The
public health response identifies the active drain with a 503 status.内部侦听器在排空期间继续接受 /state 请求,响应报告移交和还原计数器。公共健康响应通过 503 状态标识活跃的排空过程。
A remote cell call can resolve an owner generation that a replacement process
rejects. celld then waits for a different (node, epoch) and refreshes the
route. The wait uses CELLD_OPERATION_DEADLINE_MS as its total bound (default
15000). celld retries only a peer attempt that proves that the handler did not
start. It does not retry an ambiguous attempt because the handler can have
completed without returning its response.远程单元调用可能会解析出替换进程拒绝的所有者代际。celld 随后等待不同的(节点,纪元)并刷新路由。等待时间使用 CELLD_OPERATION_DEADLINE_MS 作为其总边界(默认 15000)。celld 仅重试证明处理程序未启动的对等尝试。它不会重试模棱两可的尝试,因为处理程序可能在未返回响应的情况下已经完成。
A marked stale-owner response to the outer peer tunnel proves that the application request did not start. celld can therefore refresh that route for an HTTP, an RPC, or a WebSocket request.外部对等隧道收到的已标记 stale-owner(陈旧所有者)响应证明了应用程序请求未启动。因此,celld 可以为 HTTP、RPC 或 WebSocket 请求刷新该路由。
An application must keep one stable operation ID when it retries an ambiguous fetch, RPC, D1, or service operation. A WebSocket transport cannot move between owners, so a client must reconnect and keep the same application operation ID.应用程序在重试模棱两可的获取、RPC、D1 或服务操作时必须保持一个稳定的操作 ID。WebSocket 传输无法在所有者之间移动,因此客户端必须重新连接并保持相同的应用程序操作 ID。
To roll out a new version, use the rolling update of your orchestrator: stop each node with SIGTERM, wait for its replacement to report healthy, then move to the next node. celld paces the cell handoffs inside each node shutdown, and the first-readiness gate paces the update against fleet recovery, so a deployer does not need a separate fleet-level handoff gate.要上线新版本,请使用编排器的滚动更新:用 SIGTERM 停止每个节点,等待其替换节点报告健康,然后移动到下一个节点。celld 在每个节点关闭时对单元移交进行步调控制,且首次就绪门控根据集群恢复情况控制更新进度,因此部署者无需单独的集群级移交门控。
Some upgrades are exceptions:部分升级是例外情况:
- The upgrade from v0.1.0 to v0.2.0 must not be a rolling update. Stop every v0.1.0 node, then start the v0.2.0 nodes. Two changes require this: v0.2.0 nodes advertise the internal listener, so ownership records that v0.1.0 nodes wrote name an address that v0.1.0 peers can not follow to a v0.2.0 node; and v0.2.0 compacts replicated data into block objects that a v0.1.0 reader can not restore. A fleet must not mix the two versions.从 v0.1.0 升级到 v0.2.0 不得使用滚动更新。请停止所有 v0.1.0 节点,然后启动 v0.2.0 节点。两项变更要求这样做:v0.2.0 节点宣告内部侦听器,因此 v0.1.0 节点写入的所有权记录指向的地址,v0.1.0 对等节点无法跟踪到 v0.2.0 节点;此外,v0.2.0 将复制数据压缩为 v0.1.0 读取器无法还原的块对象。集群不得混合使用这两个版本。
- The upgrade from v0.2.1 to v0.3.0 can use a rolling update. v0.3.0
changes the default durability from
buckettofleet: a single node keeps the v0.2.1 behavior, and a fleet of two or more nodes activates fleet replication automatically. Stage the v0.3.0 binary on every node, and restart one node at a time. A mixed fleet stays safe, but a v0.3.0 node cannot replicate to a v0.2.x peer, so it acknowledges writes through the bucket and retries until the peer runs v0.3.0. Do not start a v0.2.x binary after that node runs v0.3.0 unless the shutdown log containsnode-log close: sealed epoch. A graceful stop attempts this seal, but a stop under load can leave the record open, and a v0.2.x binary cannot read writes that wait in the replicated log or bundle objects. Therefore, this downgrade can lose acknowledged writes.从 v0.2.1 升级到 v0.3.0 可以使用滚动更新。v0.3.0 将默认持久性从存储桶更改为集群:单个节点保持 v0.2.1 的行为,而两个或更多节点的集群会自动激活集群复制。在每个节点上部署 v0.3.0 二进制文件,并逐个重启节点。混合集群保持安全,但 v0.3.0 节点无法复制到 v0.2.x 对等节点,因此它通过存储桶确认写入并重试,直到对等节点运行 v0.3.0。除非关闭日志包含 node-log close: sealed epoch,否则不要在该节点运行 v0.3.0 后启动 v0.2.x 二进制文件。优雅停止会尝试此密封,但在负载下停止可能会留下打开的记录,而 v0.2.x 二进制文件无法读取等待在复制日志或包对象中的写入。因此,此降级可能会丢失已确认的写入。 - The upgrade from v0.3.0 to v0.4.0 must not use a rolling update. Stop every v0.3.0 node, and then start the v0.4.0 nodes. v0.4.0 moves every proxied fetch, RPC, and WebSocket call onto one tunneled connection that carries plain HTTP. The peer protocol refuses a different version, so the two versions cannot proxy calls to each other. v0.4.0 also stores each new large KV value under its ownership epoch and writes an epoch-qualified row reference. A v0.3.0 node cannot read that reference, so a mixed fleet can make a committed KV value unavailable. The tunnel establishment carries the version, so a later protocol change can negotiate instead of refuse.从 v0.3.0 升级到 v0.4.0 不得使用滚动更新。请停止所有 v0.3.0 节点,然后启动 v0.4.0 节点。v0.4.0 将每个代理获取、RPC 和 WebSocket 调用移动到携带普通 HTTP 的隧道连接上。对等协议拒绝不同版本,因此两个版本无法相互代理调用。v0.4.0 还将其每个新的大型 KV 值存储在其所有权纪元下,并写入一个纪元限定的行引用。v0.3.0 节点无法读取该引用,因此混合集群可能导致已提交的 KV 值不可用。隧道建立过程携带版本信息,因此后续的协议变更可以协商而不是拒绝。
- The upgrade from v0.4.0 to v0.4.1 can use a rolling update. v0.4.1 restores a large cell by paging, and a paged epoch continues the chain of the epoch before it instead of opening with a whole-database snapshot. A v0.4.0 node cannot restore such an epoch. Each v0.4.1 node therefore publishes the bucket format it reads in its lease, and it pages a takeover only while every live lease reads that format. A v0.4.1 node in a mixed fleet clones a large cell the way v0.4.0 does, and paging starts on its own one lease lifetime after the last v0.4.0 node stops. Do not start a v0.4.0 binary after that point: a cell that paged in has no epoch a v0.4.0 node can restore, and it stays unavailable on that node until a v0.4.1 node takes it over.从 v0.4.0 升级到 v0.4.1 可以使用滚动更新。v0.4.1 通过分页还原大型单元,分页纪元继续前一个纪元的链,而不是以全数据库快照打开。v0.4.0 节点无法还原此类纪元。因此,每个 v0.4.1 节点在其租约中发布其读取的存储桶格式,并且仅在每个活跃租约都读取该格式时才对接管进行分页。混合集群中的 v0.4.1 节点以 v0.4.0 的方式克隆大型单元,分页在最后一个 v0.4.0 节点停止一个租约生命周期后自行开始。在此之后不要启动 v0.4.0 二进制文件:已分页的单元没有 v0.4.0 节点可以还原的纪元,并且在 v0.4.1 节点接管之前,它在该节点上保持不可用。
- The upgrade from v0.5.1 to v0.6.0 must not use a rolling update when
the fleet uses
fleetdurability. Stop every v0.5.1 node, and then start the v0.6.0 nodes. A v0.6.0 node recovers its previous log session only when a follower returns the ranged tail format. A v0.5.1 follower returns the entries-only format, so the v0.6.0 node refuses to start. A fleet that usesbucketdurability has no followers, so it can use a rolling update.当集群使用集群持久性时,从 v0.5.1 升级到 v0.6.0 不得使用滚动更新。请停止所有 v0.5.1 节点,然后启动 v0.6.0 节点。v0.6.0 节点仅在跟随者返回范围尾部格式时才恢复其先前的日志会话。v0.5.1 跟随者返回仅条目格式,因此 v0.6.0 节点拒绝启动。使用存储桶持久性的集群没有跟随者,因此可以使用滚动更新。 - The upgrade from v0.6.0 to v0.6.1 can use a rolling update. Do not set
CELLD_LTX_RETENTION_SECSon a node until every node runs v0.6.1. A v0.6.0 node does not readcells/<cell>/ltx/retired.json, so it treats the rows of a deleted epoch as unstored. That node keeps those bundle rows, it cannot seal its log at shutdown, and its next start uploads the rows again. No data is lost, but storage use and recovery time increase. A v0.6.0 node refuses a Python Workers deployment, so deploy a Python application only after every node runs v0.6.1. A v0.6.0 node also refuses an asset index with a file larger than 25 MiB. Therefore, raiseCELLD_MAX_ASSET_FILE_BYTESabove that size only after every node runs v0.6.1. A rollback to v0.6.0 can also use a rolling update. UnsetCELLD_LTX_RETENTION_SECSon every node first, so that no v0.6.1 node deletes an epoch while a v0.6.0 node runs.从 v0.6.0 升级到 v0.6.1 可以使用滚动更新。在每个节点都运行 v0.6.1 之前,不要设置 CELLD_LTX_RETENTION_SECS。v0.6.0 节点不读取 cells/<cell>/ltx/retired.json,因此它将已删除纪元的行视为未存储。该节点保留这些包行,无法在关闭时密封其日志,且其下一次启动会再次上传这些行。不会丢失数据,但存储使用量和恢复时间会增加。v0.6.0 节点拒绝 Python Workers 部署,因此仅在每个节点运行 v0.6.1 后部署 Python 应用程序。v0.6.0 节点还拒绝大于 25 MiB 文件的资产索引。因此,仅在每个节点运行 v0.6.1 后,才将 CELLD_MAX_ASSET_FILE_BYTES 提高到该大小以上。回滚到 v0.6.0 也可以使用滚动更新。请先取消设置每个节点上的 CELLD_LTX_RETENTION_SECS,以便在 v0.6.0 节点运行时没有 v0.6.1 节点删除纪元。
The internal listener also provides an alpha operator API. /state
reports the node state, and its node_load object is the same load sample
that the node lease publishes. POST /reload adopts the deployment pointer now,
and POST /shutdown starts the same graceful
handoff. POST /rebalance/pause stops balancing. The node publishes the
pause in its lease, and one paused lease stops every move in the fleet, so
a pause on one node pauses the fleet. POST /rebalance/resume on the same
node resumes it. The POST /shutdown?handoff=preserve request prepares a clean
same-node reload and keeps the ownership records. The preserve operation cancels
a cold activation, including a wait for a recovery retry, because the activation
has no runtime to retain. celld writes the reload marker
only after it uploads each stopped database position to the bucket. If an upload
fails, the next process uses the normal recovery path. A release can change this
API, so keep the operator tooling and the celld release together.内部侦听器还提供 alpha 操作员 API。/state 报告节点状态,其 node_load 对象与节点租约发布的负载样本相同。POST /reload 立即采用部署指针,POST /shutdown 启动相同的优雅移交。POST /rebalance/pause 停止负载均衡。节点在租约中发布暂停状态,一个暂停的租约会停止集群中的所有移动,因此在一个节点上暂停即会暂停整个集群。在同一节点上执行 POST /rebalance/resume 即可恢复。POST /shutdown?handoff=preserve 请求准备一次干净的同节点重载并保留所有权记录。保留操作会取消冷激活(包括等待恢复重试),因为激活没有可保留的运行时。celld 仅在将每个已停止数据库位置上传到存储桶后才写入重载标记。如果上传失败,下一个进程使用正常的恢复路径。版本发布可能会更改此 API,因此请保持操作员工具与 celld 版本同步。
Diagnose a fleet
celld diagnose reads the node leases in the bucket and sends a probe to
each live peer. It does not take a lease, and it does not change
ownership.celld diagnose 读取存储桶中的节点租约并向每个活跃的对等节点发送探测。它不获取租约,也不更改所有权。
celld diagnose \
--bucket "$CELLD_BUCKET" \
--endpoint "$S3_ENDPOINT" \
--region "$AWS_REGION"
To probe only some nodes, use --peer NODE_ID one or more times. The
report identifies expired records, unsafe or incorrect advertised
addresses, peers that it cannot reach, authentication failures, and
protocol versions that do not agree.要仅探测部分节点,请多次使用 --peer NODE_ID。报告会识别过期的记录、不安全或不正确的宣告地址、无法到达的对等节点、身份验证失败以及不一致的协议版本。
Each node line also shows restoring: the count of cold routes that hold
an activation permit or wait for one (a capacity waiter already holds a
permit, so each cold route counts once). During a rolling update, wait
for every node to report restoring=0 before you restart the next node,
so one restart's cold work finishes before the next restart removes more
warm capacity. A node that restarts recovers its previous session's log
before it takes a lease. If a peer is already recovering that log, the
node waits behind the peer while the peer's heartbeat is fresh, and it
takes the recovery over only when the heartbeat stops.每个节点行还显示 restoring:持有激活许可或正在等待许可的冷路由计数(容量等待者已持有许可,因此每个冷路由仅计数一次)。在滚动更新期间,请等待每个节点报告 restoring=0 后再重启下一个节点,以便一次重启的冷工作在下一次重启移除更多热容量前完成。重启的节点在获取租约前会恢复其先前会话的日志。如果对等节点已经在恢复该日志,则节点在对等节点心跳新鲜时等待其后,仅在心跳停止时接管恢复。
List Durable Objects
List the Durable Object instances in the bucket:列出存储桶中的 Durable Object 实例:
celld cell list \
--bucket "$CELLD_BUCKET" \
--endpoint "$S3_ENDPOINT" \
--region "$AWS_REGION"
Each line is a Class:ID cell scope. The ID is the Durable Object ID: a
64-character hash unless the application supplies its own name. Give a
class name to list only that class, and pass --json for one JSON object
per line. The command puts the class in the storage prefix, so --limit
applies only to that class. An instance appears after the first event reaches
it, because its owner then writes an ownership record to the bucket. An ID
that an application only derives does not appear.每一行都是一个 Class:ID 单元范围。ID 为 Durable Object ID:除非应用程序提供自己的名称,否则为 64 字符的哈希值。提供类名可仅列出该类,传递 --json 可每行输出一个 JSON 对象。该命令将类放入存储前缀,因此 --limit 仅适用于该类。实例在第一个事件到达后出现,因为其所有者随后会将所有权记录写入存储桶。应用程序仅派生的 ID 不会出现。
The listing also shows celld's own cells. A D1 database, a KV namespace,
and a Workflow are each a cell in a reserved class, and their names start
with __. They hold real data in the bucket, therefore the command shows
them. The --json output marks each row with "reserved": true or
false, so a script can select one kind:列表还显示 celld 自身的单元。D1 数据库、KV 命名空间和 Workflow 都是保留类中的单元,其名称以 __ 开头。它们在存储桶中持有真实数据,因此该命令会显示它们。--json 输出将每行标记为 "reserved": true 或 false,以便脚本可以选择一种类型:
celld cell list --all --json --bucket "$CELLD_BUCKET" |
jq -r 'select(.reserved | not) | .scope'
A storage request returns at most 1000 instances, so the command stops at 1000 instances and writes this line to stderr:存储请求最多返回 1000 个实例,因此该命令在 1000 个实例处停止并向 stderr 写入此行:
1000 cells shown; more exist. Continue with --after Room:d99d9174b25e46310694dd931b47fbde70a7460bb7b210b546060651ea2ff6e0
Pass that --after SCOPE to read the next 1000 instances, or use
--limit N for a different bound:传递 --after SCOPE 以读取接下来的 1000 个实例,或使用 --limit N 设置不同的边界:
celld cell list --bucket "$CELLD_BUCKET" \
--after Room:d99d9174b25e46310694dd931b47fbde70a7460bb7b210b546060651ea2ff6e0
Pass --all to read the whole listing in one command. This makes one
request for each 1000 instances, so it can take minutes on a large fleet;
the command reports its progress and its request count on stderr.传递 --all 以在一条命令中读取整个列表。这会为每 1000 个实例发送一个请求,因此在大型集群上可能需要几分钟;该命令会在 stderr 上报告其进度和请求计数。
The order is the storage order, and --after continues from the last
instance printed, so a sequence of --after commands lists each instance
one time. An instance that an application creates during the sequence can
appear or not appear, because the listing is not a snapshot.顺序为存储顺序,--after 从打印的最后一个实例继续,因此一系列 --after 命令会列出每个实例一次。应用程序在序列期间创建的实例可能会或可能不会出现,因为列表不是快照。
The command writes the instances to stdout and every message to stderr, so a script reads only instance data:该命令将实例写入 stdout,并将每条消息写入 stderr,因此脚本仅读取实例数据:
celld cell list --all --json --bucket "$CELLD_BUCKET" > cells.ndjson
To see the superseded epoch prefixes that epoch GC can delete, run
celld cell gc --dry-run. The command takes the same listing options and
writes nothing. It prints one row for each cell that has candidates: the
candidate epochs, their bytes, and the base of the cell's restore chain.
The owners can delete later than the report shows, or not at all:要查看纪元 GC 可以删除的被取代的纪元前缀,请运行 celld cell gc --dry-run。该命令采用相同的列表选项且不执行任何写入。它为每个有候选者的单元打印一行:候选纪元、其字节数以及单元还原链的基础。所有者删除的时间可能晚于报告显示的时间,或者根本不删除:
- Only a fleet node with a positive
CELLD_LTX_RETENTION_SECSdeletes, and it uses that grace instead of--grace-secs.只有具有正值 CELLD_LTX_RETENTION_SECS 的集群节点才会执行删除,并且它使用该宽限期而不是 --grace-secs。 - A paged cell waits until its local file is complete.分页单元等待直到其本地文件完成。
- An inactive cell waits until it is active again.非活跃单元等待直到再次活跃。
- A cell that is not paged waits until its activation has a write, so a
cell that is only read deletes nothing. After the write, the owner
compacts at its next pass and deletes at the pass after it. With
CELLD_LTX_COMPACTION=0, the cell waits for a handoff snapshot.未分页的单元等待直到其激活产生写入,因此仅读取的单元不会删除任何内容。写入后,所有者在下一次维护周期进行压缩,并在之后的周期进行删除。使用 CELLD_LTX_COMPACTION=0 时,单元等待移交快照。 - One pass deletes at most 64 epochs of a cell.一次维护周期最多删除单元的 64 个纪元。
If the command cannot read a cell, it reports the cell, continues, and exits with an error at the end.如果命令无法读取单元,它会报告该单元,继续执行,并在最后以错误退出。
celld cell gc --dry-run --bucket "$CELLD_BUCKET" --grace-secs 3600
Hot-cell overload热单元过载
celld admits a maximum of 64 concurrent fetch events for one Durable Object.
Set CELLD_MAX_CELL_REQUESTS to use a different positive limit.celld 为一个 Durable Object 最多准入 64 个并发获取事件。设置 CELLD_MAX_CELL_REQUESTS 以使用不同的正数限制。
A Queue broker admits a maximum of 256 concurrent producer calls. It commits at most 64 calls in one transaction, and four transactions can overlap their durability proofs. These fixed bounds limit the copied message bodies while a storage proof is slow.队列代理最多准入 256 个并发生产者调用。它在一次事务中最多提交 64 个调用,且四个事务可以重叠其持久性证明。这些固定边界限制了存储证明缓慢时的复制消息体大小。
celld returns HTTP status 503 when the target reaches this limit, and it
does not start the excess event. The response contains Retry-After: 1 and
X-Celld-Overload: cell, so the application can retry or reject the work.
A local or remote Queue owner uses the same status and headers when its
producer reservations are full. The Queue response body is
{"error":"cell admission refused"}. A fixed-rate client must count the
response as rejected work, so an immediate retry does not increase the
configured offered rate.当目标达到此限制时,celld 返回 HTTP 状态 503,且不会启动多余的事件。响应包含 Retry-After: 1 和 X-Celld-Overload: cell,因此应用程序可以重试或拒绝工作。本地或远程队列所有者在其生产者预留已满时使用相同的状态和标头。队列响应体为 {"error":"cell admission refused"}。固定速率客户端必须将该响应计为拒绝的工作,因此立即重试不会增加配置的提供速率。
A caught Queue producer error contains cell overload: admission refused, so
the Worker can return the same admission response to the client.捕获的队列生产者错误包含 cell overload: admission refused,因此 Worker 可以将相同的准入响应返回给客户端。
The runtime writes a cell_overload_refused log event when a target becomes
saturated. The event contains the cell scope, the node, the region, the
in-flight count, and the limit. Count these overload responses separately
from the transport errors and the application failures.当目标变得饱和时,运行时会写入 cell_overload_refused 日志事件。该事件包含单元范围、节点、区域、飞行中计数和限制。请将这些过载响应与传输错误和应用程序故障分开统计。
Environment variables
For the full list, run celld -h. This table shows the primary settings:如需完整列表,请运行 celld -h。此表显示了主要设置:
| variable | purpose |
|---|---|
CELLD_BUCKET |
The fleet bucket, and an optional key prefix. The same as --bucket |
S3_ENDPOINT |
The S3-compatible endpoint. The same as --endpoint |
AWS_REGION, AWS_DEFAULT_REGION |
The storage region |
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN |
Explicit AWS credentials. The standard AWS credential chain is also available |
GOOGLE_APPLICATION_CREDENTIALS, GOOGLE_SERVICE_ACCOUNT_KEY |
Google credentials for a gs:// bucket. Application Default Credentials are also available |
AZURE_STORAGE_ACCOUNT_NAME |
The storage account for an az:// bucket. The bucket NAME is the container |
AZURE_STORAGE_ACCOUNT_KEY |
The storage account key. Do not combine it with an identity selector |
AZURE_AUTHORITY_HOST, AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN_FILE |
The standard AKS workload identity environment for an az:// bucket. The authority must be the public Azure host |
AZURE_STORAGE_USE_EMULATOR |
Set to true to develop against Azurite. celld does not qualify Azurite for a production fleet |
CELLD_ADDR |
The public Worker listener. The same as --listen |
CELLD_INTERNAL_ADDR |
The peer and operator listener. The same as --internal-listen |
CELLD_ADVERTISE |
The internal address that peers can reach. The same as --advertise |
CELLD_UNSAFE_PUBLIC_ADVERTISE |
Set to 1 to permit a literal public IP in CELLD_ADVERTISE. This setting does not resolve a DNS name or restrict the internal listener |
CELLD_NODE |
An explicit node-session ID. Use 1 to 128 ASCII letters, numbers, dots, dashes, or underscores. The value cannot be . or .. |
CELLD_WATCH |
The local work directory for SQLite and replication |
CELLD_ESBUILD |
The path of the esbuild executable |
CELLD_ACTIVATIONS |
The limit for concurrent cold-cell activations (default: 8 for each available CPU, at least 16 and at most 128). A cold activation waits on the object store for most of its time, so the default is above the CPU count |
CELLD_DEPLOY_POLL_S |
The interval in seconds at which a node reads the deployment pointer and adopts a new deployment in place (default: 30) |
CELLD_DEPLOY_MAX_AGE_S |
How long a resident Durable Object can keep the previous deployment's code after an adoption before celld forces the move (default: 60; 0 forces at once) |
CELLD_OPERATION_DEADLINE_MS |
The deadline for a non-restore operation (default: 15000) |
CELLD_MAX_CELL_REQUESTS |
The concurrent fetch limit for one Durable Object or Queue broker (default: 64) |
CELLD_MAX_REQUEST_BODY_BYTES |
The body limit for a public Worker request or a direct Durable Object request (default: 1 GiB) |
CELLD_MAX_RESIDENT_CELLS |
The hard limit for resident cells, enforced at admission |
CELLD_IDLE_EVICT_S |
The age in seconds after which an idle resident cell leaves memory and hibernates (unset: only pressure or the residency cap removes an idle cell) |
CELLD_PLACEMENT_WEIGHT |
The ownership share of this node, relative to the other nodes' weights (default: the CPU count) |
CELLD_REBALANCE_INTERVAL_MS |
The interval between shared fleet sample reads and the maximum sample age, in milliseconds (default: 5000; 0 disables balancing) |
CELLD_MAX_RSS_MB |
The memory threshold for pressure shedding, applied to the greater of the allocator-adjusted RSS and the allocator-adjusted active cgroup working set (default: 80% of the available memory; 0 disables the threshold and the absolute cap) |
CELLD_DURABILITY |
How celld proves a write durable before it answers. The default is fleet: the node serving the cell sends the write to one or two other nodes and answers once they hold it on disk, or once the bucket upload finishes, whichever comes first. This needs two or more nodes; a single node has nobody to send to, so every write waits for the bucket. Set bucket to always wait for the bucket |
CELLD_LOG_PIPELINE |
The limit for fleet log rounds that can be in flight (default: 4) |
CELLD_LOG_HEDGE_MS |
The wait before a leader sends a second copy of a slow log append to a follower. The default is adaptive: celld derives the wait from the slowest recent append in the ensemble (4 times that append, at least 250 ms, and always below the eviction backstop), so a loaded fleet does not send copies for honest slow appends. Set a value to use a fixed wait in milliseconds, and set 0 to disable the second copy. An append is idempotent per sequence, so the copy is safe, and the leader uses the answer that arrives first and confirms |
CELLD_LTX_TRUNCATE_PAGES |
The WAL size, in pages, at which celld truncates an ordinary cell's WAL file at the next checkpoint (default: 128, a 512 KiB cap). A passive checkpoint does not shrink the WAL file, so each capture reads the stale region after a restart. The truncate keeps the read small. A truncate ends in a full image of the database, so a database larger than 4 MiB also waits until its WAL is larger than the database. Queue cells use passive checkpoints only. Set 0 to disable the truncate for all cells |
CELLD_LTX_RETENTION_SECS |
Unset or 0 by default, and then celld deletes no epoch prefix. A positive value enables epoch GC on a fleet node (CELLD_DURABILITY=fleet, the default); a node with CELLD_DURABILITY=bucket deletes nothing. After the owner of a cell activates it, the owner deletes each epoch prefix below the base of the cell's restore chain. The base is the epoch where a restore stops, because that epoch opens with a whole-database snapshot. A paged cell waits until its local file is complete, because until then it reads pages from the older epochs. A cell that is not paged waits until its activation has a write, so a cell that is only read deletes nothing. After the write, the owner compacts the new rows into the first L1 object of its epoch, even below CELLD_LTX_COMPACTION_MIN_TXIDS. That object lets the owner prove that a successor restores from its epoch. The owner compacts at its next pass after the write and deletes at the pass after the compaction, and each pass can come up to 5 minutes after the previous one. With CELLD_LTX_COMPACTION=0 the owner does not compact, so the cell waits for a handoff snapshot. With CELLD_LTX_HYDRATE_MBPS=0 a paged cell never completes its file, so epoch GC never runs for it. The owner keeps its own epoch and the epoch before it. It also keeps each epoch whose newest object is younger than the value, in seconds. The owner first writes cells/<cell>/ltx/retired.json, so that bundle GC and recovery treat the rows of those epochs as stored. Epoch GC skips facet streams. It needs a store with list-after-write consistency; see the storage requirements. Run celld cell gc --dry-run to see the candidates |
CELLD_LTX_COMPACTION |
The default is 1: celld creates additive L1 objects, and a takeover reads tens of objects instead of thousands. Set 0 on every node of a mixed fleet until all nodes can read v0.5.2 block objects, because an old reader cannot take over a cell after its first L1 publication |
CELLD_LTX_COMPACTION_MIN_TXIDS |
The durable TXID distance that queues a background L1 attempt (default: 256) |
CELLD_LTX_COMPACTION_MIN_MB |
The L0 bytes since the last fold that queue a background L1 attempt, in MiB (default: 32, at most 64). The byte threshold lets a cell with large rows compact before it reaches the transaction threshold |
CELLD_LTX_PAGED |
The default is 1: celld restores a taken-over cell by paging when its chain is at least CELLD_LTX_PAGED_MIN_MB. It opens the database through a fault-in VFS that reads each page from the bucket on first use, instead of downloading the whole restore chain first, and a smaller chain is cloned. Set 0 to clone every chain. A paged cell's local file is a cache, so celld does not preserve it as an eviction snapshot and does not publish a handoff snapshot from it. A paged epoch continues the chain it paged in. celld uploads a small marker object at the next transaction before the cell serves, and its later objects follow it, so a restore composes the epochs. Set 0 on every node of a mixed fleet until all nodes run v0.4.1 or later, because a paged epoch holds no whole-database snapshot and a node before v0.4.1 restores one epoch only. Such a node refuses the activation of that cell, and the refusal is permanent for the cell. A full activation opens its epoch with a whole-database snapshot. A node pages only while every live lease in the fleet publishes a bucket format that reads a paged epoch, so a rolling update from a release without paged restore clones until that release has left the fleet; the paged_gate log event reports each change. A clean reload does not keep a paged cell resident; the next process pages it in again. An inspect of a paged cell must read the bucket, because celld does not copy the cell's sparse file in place. After the activation, celld fills the rest of the file in the background at CELLD_LTX_HYDRATE_MBPS, so a cell that stays resident soon reads only its local file. Until that fill completes, a read of a page that is not in the local file is a page fault. The fault reads the bucket synchronously inside the JavaScript turn, so it blocks the cell and the other cells on its isolate until its bucket reads complete. A query that reads many such pages can therefore take minutes. Set CELLD_LTX_PAGED=0 to remove the fault path for every cell. A higher CELLD_LTX_PAGED_MIN_MB removes the fault path only for a chain that is smaller than the new value. In both cases, the cost is a slower activation, because celld downloads the whole chain before the cell serves |
CELLD_LTX_PAGED_MIN_MB |
The chain size, in MiB, from which a restore pages instead of cloning (default: 256). A clone of a smaller chain takes seconds and fits the node's memory, so it needs no fault path and no hydration. Set 0 to page every chain |
CELLD_LTX_HYDRATE_MBPS |
The rate, in MiB per second, at which a paged cell's file fills in the background (default: 16). One cell fills at a time per node. Set 0 to keep a paged cell sparse, so every cold page is read from the bucket on first use |
CELLD_LTX_COMPACTIONS |
The node-wide limit for concurrent background L1 attempts (default: 2). CELLD_RELEASES bounds final handoff snapshots |
CELLD_LTX_DURABILITY_TIMEOUT_SECS |
The budget for one durability proof, in seconds (default: 10). The budget runs from the moment the node starts to upload the write. A write that waits behind the uploads of other cells waits while the node proves other writes, for at most six budgets. The same budget bounds the final snapshot or L0 fallback of a handoff. A slow object store can need a longer budget for a large write |
CELLD_TOKIO_THREADS |
The worker thread count of the host Tokio runtime (default: the CPU count) |
RUST_LOG |
The runtime log filter |
When a node starts, celld emits one host_runtime log event at the info
level. The worker_count field reports the worker count of the host Tokio
runtime, so an operator can verify the applied CELLD_TOKIO_THREADS setting.节点启动时,celld 在 info 级别发出一个 host_runtime 日志事件。worker_count 字段报告主机 Tokio 运行时的 worker 计数,因此操作员可以验证已应用的 CELLD_TOKIO_THREADS 设置。
Each L1 compaction attempt merges at most 256 source objects. The buffer budget for the source data is 64 MiB. If one source object exceeds this budget, celld compacts that object through temporary files in the cell's local LTX directory. The node must have free disk space for the source copy and the output file. The codec keeps the page index in memory, so its memory use increases with the page count. Compaction keeps the source objects and publishes a continuous L1 range, including when a source overlaps an existing L1 object.每次 L1 压缩尝试最多合并 256 个源对象。源数据的缓冲区预算为 64 MiB。如果一个源对象超过此预算,celld 会通过单元本地 LTX 目录中的临时文件压缩该对象。节点必须有足够的磁盘空间用于源副本和输出文件。编解码器将页面索引保留在内存中,因此其内存使用量随页面计数增加。压缩保留源对象并发布连续的 L1 范围,包括源与现有 L1 对象重叠的情况。
CELLD_OUTPUT_GATE is removed. Remove the variable from the environment,
including a value of 1 or an empty value. celld always waits for the
configured durability proof before it acknowledges a write.CELLD_OUTPUT_GATE 已被移除。请从环境中移除该变量,包括值为 1 或空值的情况。celld 在确认写入前始终等待配置的持久性证明。
These settings are also removed. Remove them from the environment, including an empty value or a value that matches the former default.这些设置也已被移除。请从环境中移除它们,包括空值或匹配先前默认值的情况。
| Removed setting | Current behavior |
|---|---|
CELLD_OTEL_SINK |
Set CELLD_OTEL=1 for the fleet bucket or set CELLD_OTEL to the collector base URL for OTLP. |
CELLD_AI_BINDING, CELLD_AI_URL |
The experimental AI adapter is removed. Call the provider from application code and remove the AI binding declaration. |
CELLD_CLOUD_RESTART_ON_DEPLOY |
A managed deployment adopts the new code in place. Credential rotation can still restart the process. |
CELLD_STORAGE_PROBE |
A node checks the storage contract before it serves a bucket-backed deployment. |
CELLD_EVICTIONS |
A node runs at most four concurrent evictions. |
CELLD_LOG_CAPTURE_WORKERS |
A node uses at most eight workers for log capture. |
CELLD_REBALANCE_BATCH_CELLS |
A balancing batch moves at most 32 idle cells. The release limit and the receiver capacity can reduce this count. |
CELLD_PRESENCE_SHADOW |
A managed node sends its serving status and its owned-cell count. It does not send a separate bucket comparison. |
CELLD_LOG_BUNDLE |
Fleet durability uses bundled tiering. A node still uses per-cell uploads when the bucket must prove a write. |
CELLD_QUEUE_PRODUCER_GROUP_MS |
A Queue owner uses a 4 ms timer to group concurrent producer calls into a SQLite transaction. |
CELLD_LOG_GROUP_COMMIT_MS |
A node waits 1 ms before a fleet log capture when Queue writes are pending. |
CELLD_PACED_HANDOFF |
A bucket-backed node attempts ownership handoff within the shutdown budget. Set CELLD_SHUTDOWN_TOTAL_MS to match the supervisor grace. |
The Queue batching timers do not set a response deadline. A Queue still waits for the wake-entry proof and the configured write durability proof.队列批处理计时器不设置响应期限。队列仍然等待唤醒条目证明和配置的写入持久性证明。
Fleet uploads combine changes from multiple cells into shared bucket objects.
CELLD_DURABILITY=bucket still requires a bucket proof for every acknowledged
write, even when peers are available. Recovery can read the existing bundles
and the per-cell objects.集群上传将来自多个单元的更改合并到共享存储桶对象中。CELLD_DURABILITY=bucket 即使在有对等节点可用时,仍然要求对每次确认的写入进行存储桶证明。恢复任务可以读取现有的包和每个单元的对象。
The storage check still distinguishes a contract violation from an ambiguous transport error. A violation prevents startup. After the existing retries, an ambiguous error produces a warning and permits startup.存储检查仍然区分合同违规和模棱两可的传输错误。违规会阻止启动。在现有重试之后,模棱两可的错误会产生警告并允许启动。
Use celld diagnose --read-only to check the bucket leases and the peers.
A managed presence report does not prove that a node holds a bucket lease.使用 celld diagnose --read-only 检查存储桶租约和对等节点。托管存在报告不能证明节点持有存储桶租约。
The help output also shows the advanced tuning switches and their defaults.帮助输出还显示了高级调优开关及其默认值。
An unset variable selects its documented default. A Boolean variable
accepts only 0 or 1. celld exits during startup when a supplied value
is invalid.未设置的变量会选择其文档记录的默认值。布尔变量仅接受 0 或 1。当提供的值无效时,celld 在启动期间退出。