How the FUSE Driver's Buffered-Write Surface Ensures Immediate Visibility of Buffered Bytes
The FUSE driver in computerd uses an in-memory FileEntry.buf alongside the virtual file system so that writes, reads, and file statistics all operate on buffered data before any backend persistence occurs.
When computerd detects a DOFS provider offering buffered-write surface methods—openWriteBufferForCreateSync, openWriteBufferSync, and releaseWriteBufferSync—it bypasses immediate disk or network writes. Instead, data lands in a per-file buffer that serves as the temporary source of truth. This design gives applications the illusion of synchronous durability while the driver batches and optimizes the actual persistence.
How Writes Enter the Buffer
The write() handler in packages/computerd/src/fuse/driver.ts copies incoming data directly into entry.buf and marks the entry dirty via markDirty. No SQL transactions or chunk uploads execute at this stage.
// Sequential small writes accumulate cheaply in memory
await rpc.call('write', '/tmp/buffered.txt', Buffer.from('chunk1'), 0);
await rpc.call('write', '/tmp/buffered.txt', Buffer.from('chunk2'), 6);
Lines 70-94 of driver.ts implement this path. Because the buffer lives alongside the VFS, multiple rapid writes incur only memory copies—no I/O stalls.
Reads Serve from the Same Buffer
When a process reads the file, read() checks files.get(path) first. If an entry exists, it copies directly from entry.buf (lines 89-93 of driver.ts).
const data = await rpc.call('read', '/tmp/buffered.txt', 0, 12);
// data contains 'chunk1chunk2' even though nothing hit the VFS yet
This mechanism ensures read-after-write consistency for any client accessing the file through the FUSE mount or the VFS directly. The buffered bytes are instantly visible without waiting for a flush cycle.
Stat Reflects Buffered Size Immediately
Tools like du and ls -l report accurate sizes because getattr() overrides the returned stat structure. Lines 86-89 of driver.ts set:
stat.size = entry.size;
stat.blocks = blocksForSize(entry.size);
The driver's entry.size tracks the logical buffer length, not the (possibly stale) VFS metadata. Consequently, quota checks and size-based operations behave correctly while data remains unflushed.
Durability and Cross-Process Visibility
Buffered data becomes durable and globally visible through two mechanisms:
-
flush()— The kernel invokes this when a file descriptor closes. The driver'sflush()handler callsflushEntry(path)(lines 33-37 ofdriver.ts), committing the buffer to the VFS in a single transaction. -
fsync()— Explicit synchronization also triggersflushEntry, allowing applications to force durability without closing the descriptor.
Because flush runs before release, any subsequent VFS read—whether from an RPC caller, another process, or the shim—observes the committed bytes immediately. Lines 71-75 of driver.ts implement flushEntry, which returns any error code to the kernel for proper syscall semantics.
// Close triggers flush automatically
await rpc.call('release', '/tmp/buffered.txt');
// Or force early durability
await rpc.call('fsync', '/tmp/buffered.txt');
Summary
- In-memory buffering via
FileEntry.bufeliminates per-write I/O overhead. - Read redirection through
files.get(path)guarantees visibility of unflushed data. - Stat override in
getattr()presents accurate file sizes to user-space tools. - Kernel-coordinated flush on close or
fsyncmakes data durable and globally visible without waiting forrelease.
Frequently Asked Questions
What happens if the computerd process crashes before a flush?
Unflushed data in FileEntry.buf resides only in process memory. If computerd terminates before flushEntry completes, those bytes are lost. The underlying VFS remains unchanged. Applications requiring stronger guarantees should invoke fsync explicitly.
Can multiple processes read the buffered data simultaneously?
Yes. The files map in driver.ts is shared across all FUSE operations on the same mount. Any reader accessing the file through the FUSE mount receives data from entry.buf while the buffer exists, ensuring coherent views without VFS round-trips.
How does the driver handle very large buffered writes?
The implementation relies on memory allocation for entry.buf. While the analysis does not specify explicit limits, the DOFS provider's openWriteBufferSync contract and available system memory bound practical buffer sizes. Excessive buffering would eventually pressure the flush mechanism or fail allocation.
Why does flush run before release instead of during release?
The Linux FUSE protocol guarantees a flush call before release for writable file descriptors. By performing the heavy VFS write work in flush, the driver ensures that release can complete quickly and that error codes from persistence propagate back to the kernel properly. This separation also lets fsync trigger the same durability path without closing the file.
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 →