Functional Difference Between Resident and Hibernated Cells in celld: A Complete Guide

Resident cells remain loaded in memory with active SQLite connections for instant request handling, while hibernated cells are persisted to disk as snapshots to free RAM, requiring re-activation overhead when accessed.

The denoland/celld runtime manages Durable Object (DO) state through an abstraction called cells, each existing in specific lifecycle phases that determine storage location and access speed. Understanding the functional difference between resident cells and hibernated cells in celld is essential for optimizing performance and resource allocation in production deployments. These two states represent the fundamental trade-off between memory consumption and request latency within the system's state machine.

Lifecycle Phases and State Machine

The celld architecture defines cell states through the Phase enum, distinguishing between active memory-resident instances and disk-persisted hibernated snapshots.

The Resident Phase

A cell enters the resident phase when it occupies active memory with an open database connection. In crates/logic/lib.rs, the Phase::Resident { epoch } variant (lines 814‑816) marks cells that are kept in-memory with an open SQLite connection, allowing the executor to run top-level Worker requests directly on the loaded isolate without disk I/O. This state provides the fastest possible request handling because the isolate, its SQLite WAL, and file handles are already initialized and ready.

The Hibernated Phase

When a cell is hibernated, it is evicted from memory and its state is serialized to disk. The SQLite database connection is closed, and a snapshot is written to a .hibernated file, leaving the cell dormant until explicitly reactivated. According to the connection management logic in crates/celld/storage.rs (lines 9‑12), the system follows an "open on activate, close on hibernate" policy, ensuring that hibernated cells consume no RAM while maintaining durability through disk snapshots.

Performance and Resource Characteristics

The choice between keeping a cell resident or allowing it to hibernate directly impacts node performance and capacity planning.

Latency and Throughput

Resident cells deliver very low latency because requests execute immediately against the loaded isolate. In contrast, accessing a hibernated cell incurs significant overhead: the runtime must first restore the .hibernated snapshot, reopen the SQLite database, and reconstruct the isolate environment before processing the request.

Memory Constraints and Limits

Resident cells consume RAM for the isolate heap, SQLite WAL, and file descriptors, bounded by the CELLD_MAX_RESIDENT_CELLS hard cap. Hibernated cells free these resources, allowing a node to host substantially more cells than physical memory permits, limited instead by the CELLD_HIBERNATIONS configuration. This architecture enables celld to support large fleets of rarely-accessed Durable Objects without exhausting memory.

State Transitions and Activation Mechanics

Cells transition between resident and hibernated states based on request patterns and resource pressure.

Becoming Resident

Cells become resident when a top-level Worker fetch targets their ID and a resident slot is available under the CELLD_MAX_RESIDENT_CELLS limit. The scheduler pins the isolate for the request duration, ensuring subsequent operations on that cell remain fast.

The Hibernation Process

When eviction logic determines that memory pressure requires freeing resources, the runtime invokes the async hibernate method defined in crates/celld/runtime.rs (lines 216‑225). This method delegates to ltx_repl::hibernate to serialize the cell state to a .hibernated snapshot while storage.rs closes the underlying database connection. The logic/lib.rs module tracks which cells are eligible for hibernation through the hibernation_permits field (lines 82‑84), recording timestamps when hibernation requests are refused due to active locks.

Reactivation from Hibernation

When a request arrives for a hibernated cell, the runtime executes an activation sequence: it locates the existing .hibernated snapshot, restores the database state, reopens the SQLite connection via the storage layer, and transitions the cell back to Phase::Resident. This process is significantly slower than resident access but ensures that infrequently used cells do not consume active resources.

Implementation Details in Source Code

The distinction between resident and hibernated cells is enforced across several core modules:

  • crates/logic/lib.rs: Defines the Phase enum including Phase::Resident { epoch } and manages hibernation permits that control eviction eligibility.
  • crates/celld/storage.rs: Maintains the map of open connections and implements the lifecycle policy documented at lines 9‑12, explicitly closing databases on hibernate.
  • crates/celld/runtime.rs: Implements the hibernate and activate async methods that orchestrate state transitions and snapshot management.
  • crates/celld/ltx_repl.rs: Handles the creation, compression, and reuse of .hibernated snapshot files on disk.

Practical Code Examples

The following Rust patterns demonstrate interacting with both cell states:

// Fast-path execution against a resident cell
let cell_id = "my-do";
let epoch = state.cells[&cell_id].phase; // → Phase::Resident { epoch }
runtime.dispatch_do_call(cell_id, request).await?; // No DB reload required
// Explicitly hibernating a cell to free memory
await runtime.hibernate(cell_id, epoch, preserve_local = true).await?;
state.cells.get_mut(cell_id).unwrap().phase = Phase::Remote { /* … */ };
// Reactivating a hibernated cell (higher latency)
let result = runtime.activate(cell_id, epoch).await?;
match result {
    Phase::Resident { .. } => println!("Cell is now resident again!"),
    _ => panic!("Unexpected phase"),
}

Summary

  • Resident cells maintain active SQLite connections and isolates in RAM, providing immediate request execution but consuming memory limited by CELLD_MAX_RESIDENT_CELLS.
  • Hibernated cells are persisted as .hibernated snapshots on disk with closed database connections, freeing RAM but requiring snapshot restoration and DB reopening on access.
  • State transitions are managed through runtime.hibernate() and runtime.activate() in crates/celld/runtime.rs, with connection lifecycle handled by storage.rs.
  • The system uses hibernation_permits in crates/logic/lib.rs to coordinate safe eviction under memory pressure.

Frequently Asked Questions

When does celld decide to hibernate a cell?

The eviction logic triggers hibernation when the node experiences memory pressure and the number of resident cells approaches the CELLD_MAX_RESIDENT_CELLS hard cap. Cells that have been inactive and hold valid hibernation_permits (tracked in crates/logic/lib.rs) are selected for hibernation to free resources for active workloads.

How does hibernation affect request latency?

Accessing a resident cell incurs minimal latency because the isolate and database are already loaded. A hibernated cell requires restoring the .hibernated snapshot from disk and reopening the SQLite connection, introducing significant latency overhead compared to the resident fast path.

What limits the number of hibernated cells?

While hibernated cells consume minimal RAM, their quantity is bounded by the CELLD_HIBERNATIONS configuration parameter and available disk storage for .hibernated snapshot files. Unlike resident cells, hibernated instances do not hold open file descriptors or database connections.

Can data be lost when a cell hibernates?

No. The hibernation process in crates/celld/runtime.rs ensures durability by writing a complete snapshot to the .hibernated file before closing the database connection. The storage.rs module guarantees that all WAL entries are flushed during the "close on hibernate" operation, making hibernation a safe, crash-resistant state.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →