eden/fs/docs/InodeGarbageCollection.md
EdenFS implements a garbage collection (GC) mechanism to manage memory usage by deleting inodes that are no longer actively used. This document explains the GC process, its entry points, and how it differs across the three filesystem interfaces: FUSE, NFS, and PrjFS.
┌─────────────────────────────────────────────────────────────────────────────┐
│ EdenServer │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ garbageCollectWorkingCopy() │ │
│ │ │ │
│ │ 1. Acquire GC lease (prevents concurrent GC on same mount) │ │
│ │ 2. Call handleChildrenNotAccessedRecently() on root TreeInode │ │
│ │ 3. Call unloadChildrenUnreferencedByFs() to clean up │ │
│ │ 4. Log metrics and release lease │ │
│ └───────────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ handleChildrenNotAccessedRecently() │ │
│ │ (TreeInode) │ │
│ │ │ │
│ │ Platform-specific dispatch: │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────────────┐ ┌──────────────────────┐ │ │
│ │ │ FUSE │ │ NFS │ │ PrjFS │ │ │
│ │ └─────────────┘ └─────────────────────┘ └──────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ unloadChildrenUnreferencedByFs() │ │
│ │ │ │
│ │ Final cleanup: Unload inodes with zero FS reference count │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
garbageCollectWorkingCopyFile: eden/fs/service/EdenServer.cpp
The GC process is initiated by EdenServer::garbageCollectWorkingCopy(). This
function:
mount.tryStartWorkingCopyGC(inode)handleChildrenNotAccessedRecently() on the root
TreeInodeunloadChildrenUnreferencedByFs() for final
cleanuphandleChildrenNotAccessedRecentlyFile: eden/fs/inodes/TreeInode.cpp
This function dispatches to platform-specific implementations based on the filesystem channel type:
ImmediateFuture<uint64_t> TreeInode::handleChildrenNotAccessedRecently(
std::chrono::system_clock::time_point cutoff,
const ObjectFetchContextPtr& context,
folly::CancellationToken cancellationToken) {
if (getMount()->getNfsdChannel()) {
// NFS path (currently only supported on macOS)
return invalidateChildrenNotMaterializedNFS(cutoff, context, cancellationToken);
} else if (getMount()->getPrjfsChannel()) {
// PrjFS path (currently only supported on Windows)
return invalidateChildrenNotMaterializedPrjFS(cutoff, context, cancellationToken);
}
// FUSE path (currently only supported on Linux)
auto unloaded = unloadChildrenLastAccessedBefore(folly::to<timespec>(cutoff));
return ImmediateFuture<uint64_t>{0ULL};
}
unloadChildrenUnreferencedByFsFile: eden/fs/inodes/TreeInode.cpp
The final cleanup phase that removes inodes no longer referenced by the filesystem. This function:
Behavior: FUSE automatically manages FS refcounts. The kernel sends
FUSE_FORGET messages when files are no longer referenced, which decreases the
FS refcount. Therefore, EdenFS doesn't need to explicitly invalidate inodes.
GC Strategy:
unloadChildrenLastAccessedBefore() to unload inodes based on their
access time (atime)Behavior: NFS (NFSv3) does not notify EdenFS when file handles are closed. This means EdenFS cannot rely on the kernel to decrement FS refcounts automatically.
GC Strategy:
processTreeChildren()nfsInvalidateCacheEntryForGC()Key Considerations:
lastUsedTime to determine if an inode is stalecompleteInvalidations() waits for all to
finishBehavior: PrjFS manages placeholders on disk. Unlike FUSE, it doesn't automatically notify EdenFS when files are closed.
GC Strategy:
processTreeChildren()_wstat64()invalidateChannelEntryCache()Key Considerations:
invalidateChannelEntryCache() failing for non-empty directories to
prevent data lossprocessTreeChildrenA template function that recursively processes tree children with cancellation support.
getLoadedOrRememberedTreeChildrenA helper function that is called from processTreeChildren to get the list of
tree's children (both loaded and unloaded).
shouldCancelGCChecks for early termination conditions.
| Aspect | FUSE (Linux) | NFS (macOS) | PrjFS (Windows) |
|---|---|---|---|
| Kernel Notification | Yes (FUSE_FORGET) | No | No |
| Refcount Management | Automatic by kernel | Manual via GC | Manual via GC |
| Invalidation Required | No | Yes | Yes |
| Time Tracking | In-memory atime | lastUsedTime field | On-disk atime |
| First GC Phase | unloadChildrenLastAccessedBefore() | invalidateChildrenNotMaterializedNFS() | invalidateChildrenNotMaterializedPrjFS() |
| Invalidation Method | N/A | nfsInvalidateCacheEntryForGC() | invalidateChannelEntryCache() |
| Invalidation Scope | N/A | Non-materialized directories | Non-materialized files/directories |
| Data Safety | N/A | Skip materialized inodes | Skip materialized inodes; relies on failure for non-empty dirs |
┌─────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐
│ EdenServer │ │ TreeInode │ │ InodeMap │ │ FsChannel│
│ │ │ (Root) │ │ │ │ │
└──────┬──────┘ └──────┬───────┘ └──────┬───────┘ └────┬─────┘
│ │ │ │
│ garbageCollectWorkingCopy() │ │
│──────────────────>│ │ │
│ │ │ │
│ │ handleChildrenNotAccessedRecently() │
│ │───────────────────────────────────────>
│ │ │ │
│ │ [Platform-specific invalidation] │
│ │<──────────────────────────────────────│
│ │ │ │
│ │ invalidate/unload │ │
│ │ children │ │
│ │───────────────────>│ │
│ │ │ │
│ │ unloadChildrenUnreferencedByFs() │
│ │───────────────────>│ │
│ │ │ │
│ │ [Unload inodes with refcount=0] │
│ │<───────────────────│ │
│ │ │ │
│<──────────────────│ │ │
│ (return count) │ │ │
│ │ │ │
The GC behavior can be configured via EdenConfig:
| Config Key | Description | Default |
|---|---|---|
enableGc | Enable periodic garbage collection | Platform-dependent |
gcPeriod | Interval between GC runs | Configured per platform |
gcCutoff | Time threshold for considering inodes stale | 6 hours |
The GC process supports cancellation at multiple points:
gcCancelSource_.requestCancellation()stopAllGarbageCollections() is called before
takeovereden/fs/service/EdenServer.cpp - GC entry point and schedulingeden/fs/inodes/TreeInode.cpp - Core GC logic and platform dispatcheden/fs/inodes/TreeInode.h - TreeInode class declarationeden/fs/inodes/InodeMap.cpp - Inode tracking and refcount managementeden/fs/nfs/Nfsd3.cpp - NFS invalidation implementationeden/fs/prjfs/PrjfsChannel.cpp - PrjFS invalidation implementation