FUSE Write Model and Buffered‑Write Surface in Computerd: How Cloudflare's FUSE Driver Manages File Writes
Computerd implements a FUSE write model that delegates byte ownership to the DOFS layer, utilizing a buffered-write surface with methods like openWriteBufferForCreateSync and releaseWriteBufferSync to stage writes in memory until atomic commit, while providing a fallback staging model for providers lacking direct buffer access.
The cloudflare/computer repository contains computerd, a daemon that mounts a virtual filesystem (VFS) inside containers through a FUSE driver. This article examines the FUSE write model and buffered-write surface implemented in packages/computerd/src/fuse/driver.ts, explaining how the system handles file mutations while maintaining consistency across FUSE clients, RPC calls, and synchronization logic.
Byte Ownership and the Buffered‑Write Surface Architecture
Unlike traditional FUSE implementations that manage staging buffers internally, computerd’s driver acts as a thin adapter over the DOFS (Database‑Backed Object File System) provider.
DOFS Layer Ownership
The bytes of a file belong to the DOFS layer, not to the FUSE driver itself. This architectural decision means the driver never holds a separate staging buffer for normal writes in packages/computerd/src/fuse/driver.ts. Instead, the driver delegates all byte‑level operations to the underlying storage provider, which maintains the authoritative state.
The Three‑Method Interface
When the DOFS provider implements the low‑level buffered-write surface—specifically the three methods openWriteBufferForCreateSync, openWriteBufferSync, and releaseWriteBufferSync—the driver wires FUSE operations directly to these hooks. This interface is defined in packages/computerd/src/fuse/backend.ts and implemented by the VFS wrapper in packages/computerd/src/fuse/vfs.ts.
The Write Path Lifecycle in packages/computerd/src/fuse/driver.ts
The FUSE write model follows a strict lifecycle from file creation through atomic persistence.
Creating and Opening Write Buffers
When a container process creates a new file, the FUSE create operation invokes openWriteBufferForCreateSync, which stores a pending‑create buffer keyed by the file path. For existing files, the open operation calls openWriteBufferSync, retrieving or creating an inode‑keyed buffer that persists for the duration of the file handle. Both methods establish the in‑memory surface where subsequent mutations accumulate.
In‑Flight Mutations
Once a buffer is active, write and truncate operations directly mutate this in‑memory staging area. read operations serve data from the active buffer while the file remains open, ensuring that subsequent reads see uncommitted changes. If no buffer is active for a read, the driver falls back to readRangeSync, which queries the underlying vfs_chunks table in the VFS chunk store.
Atomic Commit via Release
The critical durability point occurs during release (or flush/fsync). This triggers releaseWriteBufferSync, which atomically writes the entire buffered content into the underlying vfs_chunks table within a single transaction. After successful persistence, the driver clears the pending entry and frees the associated memory, completing the write lifecycle.
Consistency and Visibility Guarantees
While a file remains open via FUSE, any reads performed by other FUSE clients, RPC calls, or synchronization logic see the in‑flight buffered bytes. This design provides a consistent view across the entire system, eliminating race conditions between the container process and the DO synchronization loop. The buffered-write surface effectively creates a transactional scope that isolates uncommitted changes from external observers until explicit release.
Fallback to the Legacy Staging Model
If the DOFS provider does not expose the buffered-write surface—common in legacy tests or custom providers—the driver falls back to an older staged model. In this mode, packages/computerd/src/fuse/driver.ts maintains a per‑file in‑memory FileEntry buffer and spills it to storage only upon release or flush. This fallback ensures compatibility while sacrificing the performance benefits of the direct buffer interface.
// Example: Forcing fallback in unit tests
vfs.disableDirectWriteBuffers(); // Subsequent writes use legacy FileEntry staging
Monitoring Buffer Statistics
To prevent memory exhaustion and diagnose leaks, the driver exposes runtime metrics via GET /__computerd/stats. This endpoint returns current buffer statistics including entries, dirtyEntries, pendingCreates, capacityBytes, and openHandles. The system enforces a hard limit of MAX_FILE_BYTES = 256 MiB to protect the daemon from OOM attacks.
# Inspect buffered-write state
curl http://localhost:45678/__computerd/stats | jq .
{
"entries": 12,
"dirtyEntries": 3,
"pendingCreates": 1,
"capacityBytes": 268435456,
"logicalBytes": 1048576,
"dirtyLogicalBytes": 4096,
"openHandles": 2,
"openPaths": 2
}
Practical Write Example
When a container writes to the mounted workspace, it exercises the full buffered-write surface:
import { writeFileSync, readFileSync } from "node:fs";
// Create triggers openWriteBufferForCreateSync
writeFileSync("/workspace/hello.txt", "Hello");
// Append mutates the existing buffer
writeFileSync("/workspace/hello.txt", " World!", { flag: "a" });
// Read serves from in-flight buffer
const content = readFileSync("/workspace/hello.txt", "utf8");
console.log(content); // "Hello World!"
// Process exit triggers release → releaseWriteBufferSync
// Data commits atomically to vfs_chunks
Summary
- Byte ownership resides in the DOFS layer, not the FUSE driver, eliminating redundant buffer copies.
- Buffered-write surface requires three specific methods (
openWriteBufferForCreateSync,openWriteBufferSync,releaseWriteBufferSync) to enable direct memory staging. - Atomic commit occurs during
release/flush, writing the entire buffer tovfs_chunksin one transaction. - Fallback path uses legacy
FileEntrystaging when the provider lacks the buffered-write API. - Visibility guarantees ensure all readers see consistent in-flight data while files remain open.
- Safety limits enforce a 256 MiB cap per file with diagnostic endpoints for monitoring.
Frequently Asked Questions
What happens if the DOFS provider doesn't implement the buffered-write methods?
The driver detects the missing interface and automatically falls back to the legacy staging model. In this mode, packages/computerd/src/fuse/driver.ts allocates a per‑file FileEntry buffer that accumulates writes in memory until release or flush triggers persistence, sacrificing some performance for compatibility.
How does computerd prevent memory exhaustion during large file writes?
The driver enforces a hard limit of 256 MiB (MAX_FILE_BYTES) per file buffer. This cap protects the daemon from OOM attacks or runaway container processes. Additionally, the GET /__computerd/stats endpoint exposes capacityBytes and logicalBytes metrics to help operators monitor buffer utilization in real time.
Can other processes read a file while it's being written through FUSE?
Yes. While a file handle remains open, the buffered-write surface ensures that all readers—including other FUSE clients, RPC calls, and sync logic—see the in-flight buffered bytes. This provides strong consistency during the write session, with changes becoming permanently visible only after releaseWriteBufferSync commits the transaction.
Where is the buffered data stored before commit?
Before the release operation calls releaseWriteBufferSync, the buffered data resides in memory within the DOFS provider's implementation (typically in packages/computerd/src/fuse/vfs.ts). The FUSE driver itself does not maintain a separate staging area; it delegates entirely to the provider's buffer, which persists the data atomically to the vfs_chunks table upon release.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →