How `vfs_nodes.rev` Relates to the Sync Changes Table for Tombstones in Cloudflare Computer
vfs_nodes.rev tracks the latest revision for live inodes, while vfs_changes.rev records tombstone deletions using the same global monotonic counter, enabling the sync driver to merge live updates with historical deletions into a consistent ordered stream.
In the Cloudflare Computer project's distributed object filesystem (DOFS), revision tracking forms the backbone of reliable synchronization. The VFS maintains a single global revision counter in vfs_meta.rev, and understanding how this counter flows into both live node records and tombstone entries is essential for grasping how the system propagates deletions across distributed clients.
The Global Revision Counter: vfs_meta.rev
Every filesystem mutation—whether mkdir, writeFile, or rm—increments a global, monotonically increasing revision counter. This counter lives in vfs_meta.rev and serves as the single source of chronological truth for the entire VFS.
// packages/dofs/src/rev.ts
export function incrementRev(db: Database): number {
const row = db.one<{ v: number }>(
"UPDATE vfs_meta SET v = v + 1 WHERE k = 'rev' RETURNING v"
);
return row.v; // <- new global revision
}
The incrementRev() function in packages/dofs/src/rev.ts atomically bumps this counter and returns the new value. Every subsequent operation uses this returned revision to tag its effects.
How vfs_nodes.rev Tracks Live State
When an inode is created or modified, the current global revision is copied into vfs_nodes.rev for that live record. This column answers: "What is the most recent revision at which this file or directory existed in its current form?"
await db.transactionSync(async (tx) => {
const rev = incrementRev(tx); // bumps global rev
tx.run(
"INSERT INTO vfs_nodes (inode, rev, ...) VALUES (?, ?, ...)",
inode, rev
);
});
The vfs_nodes_by_rev index allows efficient range queries: the sync driver scans vfs_nodes WHERE rev > ? ORDER BY rev to find all live updates since a client's last sync point.
Tombstones in vfs_changes: Preserving Delete History
When a file is deleted, the system faces a problem: simply removing the row from vfs_nodes would cause sync clients to never learn of the deletion. The solution is tombstone entries in vfs_changes.
// packages/dofs/src/sync/changes.ts
await db.transactionSync(async (tx) => {
const rev = incrementRev(tx); // same rev is used for the tombstone
tx.run(
"INSERT INTO vfs_changes (rev, path, op) VALUES (?, ?, 'delete')",
rev, "/foo.txt"
);
// vfs_nodes entry for /foo.txt is removed elsewhere
});
Key design properties of this approach:
vfs_changes.revuses the same global revision asvfs_nodes.rev, ensuring comparable ordering- The tombstone survives even after the
vfs_nodesrow is purged - Multiple operations on the same path can be coalesced by taking
MAX(rev)
Merging Live Updates and Tombstones in the Sync Driver
The sync driver in packages/dofs/src/sync/coalesce.ts merges two revision streams to present clients with a complete, ordered change log:
// packages/dofs/src/sync/coalesce.ts
// live updates – scan by rev
db.all("SELECT inode, rev FROM vfs_nodes WHERE rev > ? ORDER BY rev");
// tombstones – scan changes table
db.all(
"SELECT path, MAX(rev) AS rev FROM vfs_changes WHERE rev > ? AND op = 'delete' GROUP BY path"
);
Because both tables use the same monotonic revision counter, the sync driver can reliably interleave these results. A tombstone's revision will always be greater than or equal to any preceding live mutation for the same inode, preventing race conditions where a client might "resurrect" a deleted file.
Revision Safety Guarantees
The unified revision system provides critical guarantees for distributed synchronization:
- Ordering: All changes—creations, updates, deletions—are totally ordered by
rev - Idempotency: Clients can resume sync from any watermark without missing changes
- Compaction: Old tombstones can eventually be garbage-collected once all clients have synced past them
The sync layer reads the current watermark from vfs_meta.rev via packages/dofs/src/sync/watermarks.ts, then compares against vfs_nodes.rev for live state and vfs_changes.rev for historical deletions.
summary
vfs_nodes.revstores the latest revision for each live inode, enabling efficient incremental sync of existing filesvfs_changes.revstores tombstone deletions using the same global revision counter, ensuring deletions propagate to all clients- Both values originate from
vfs_meta.revviaincrementRev()inpackages/dofs/src/rev.ts - The sync driver merges these streams in
packages/dofs/src/sync/coalesce.tsto present a unified, ordered change log - Tombstone revisions are always ≥ preceding live mutations, preventing synchronization anomalies
frequently asked questions
What happens if two clients delete the same file simultaneously?
Both insert tombstones into vfs_changes with different rev values from sequential calls to incrementRev(). The sync driver coalesces these using MAX(rev), and clients see the deletion at the later revision. Duplicate tombstones are harmless.
Can vfs_nodes.rev ever decrease?
No. The revision counter in vfs_meta.rev only increments, and vfs_nodes.rev is set to copies of this value. Even when an inode is modified, its rev column is updated to a higher value, never lower.
How does the system clean up old tombstones?
Once all connected clients have synced past a tombstone's revision (tracked via watermarks in packages/dofs/src/sync/watermarks.ts), the entry can be safely deleted from vfs_changes. The revision monotonicity ensures no client will request changes from before its acknowledged watermark.
Why not just keep deleted rows in vfs_nodes with a deleted flag?
Separating tombstones into vfs_changes allows the live filesystem queries to remain fast and simple while keeping historical data strictly for synchronization. It also enables compaction strategies that don't require scanning mixed live/deleted tables.
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 →