Repository navigation
Give memory back, and release 0.9.0 - #190
Merged
Merged
Conversation
Box the rarely set removed_from of each inode, keep only directories in the core's path-to-entry cache (files are one listing lookup further), share that cache's keys with the entries' own, shrink the inode table once the kernel forgets most of it, and cap a git mount's recent whole file reads at 64 MiB. A 200k-file walk goes from 147 MB to 89 MB with the same walk times. With files out of the path-entry cache, a recheck of a shrunk file changes only its listing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK
Files clone only within one disk, so a fork of a plain folder on another disk used to fail with CrossDevice unless allow_copy made a full copy. Its frozen copy now goes in .sandfs/copies at the top of the folder's own disk, registered in the home's copies-elsewhere file, and is cleaned up with the home's other copies. The .sandfs folder is left out of forks of that disk's top. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK
…ttributes A full copy (allow_copy) opened each copied file read-only to set its modification time, which Windows refuses, and opened folders without backup semantics, which Windows can't. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK
…disk's top A .sandfs or .sandfs/copies at a disk's top that is a link could point into the folder being copied, which would then copy into itself; such a disk's copies stay in the home. A folder's own .sandfs/copies, away from a disk's top, is listed as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK
Anyone who could write .sandfs or .sandfs/copies at a disk's top, or rename them there, could swap a home's folder for a link, and copies would be written, and deleted, wherever it led. Those folders must be the home owner's and writable by no one else, at a top that is not writable by others or is sticky; otherwise the copy stays in the home, and cleanup leaves such a folder alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
lydakis
marked this pull request as draft
October 28, 2026 02:34
Folder listings, directories found and file bytes a workspace keeps stay within a budget, least used first out. A mount unused for a minute, or asked with `sandfs trim` / `Home::trim`, saves its changes, drops those caches, ends idle git readers and asks the allocator to hand freed memory back to the system, as it now does after every unmount. Paths in a mount's table of files are held once instead of twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK
The test summed this test process's own peak with its child's and compared that with the tree's, read a moment later. Linux updates a process's peak lazily, so once memory is freed (as other tests, and now a mount's hand-back, do) the later reading can be lower. A shell with two sleeping children frees nothing meanwhile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK
… log idle trims On macOS 27 the allocator answered 0 while it handed the pages back over the next seconds, so "0 B given back" read as a failure. The walk test also times each walk now, against the cache budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK
lydakis
marked this pull request as ready for review
October 28, 2026 03:19
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e2ec7534da
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…the unsafe exception A slow load whose slot moved to the old half had its bytes dropped, so once it moved back it counted as an empty slot and the cache could pass its budget. Its bytes now go on the old entry when it is the same slot. AGENTS.md names the one allowed unsafe function. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two commits: the 0.9.0 release (versions, changelog) and the memory work George asked to ship in it.
Giving memory back
The process serving mounts grew with every file a walk asked about and never shrank: about 1 GB after a walk of a 700,000-file folder on macOS, and 337 MB still held after the unmount (freed, but kept by macOS's allocator).
Sourcecontract says answers never change). Prefetch and walk notes start over past 65,536 directories.Workspace::trimdrops all of it and calls the newSource::trim(default: nothing);GitSourceends its idlecat-filereaders.Shared::trimsaves changes first, then trims. The server trims a mount nobody has used for a minute, and on request:sandfs trim [NAME],Home::trim(returnsTrimmed).memory::give_backcallsmalloc_zone_pressure_relief(NULL, 0)on macOS andmalloc_trim(0)on glibc (musl returns memory itself). It runs after a trim and after every unmount. This is the one unsafe call, approved by George. The workspace lint goes fromforbidtodeny, with#[allow(unsafe_code)]on that function only.VPathis now anArc<str>, and a mount's path-to-inode table is keyed byVPath. Each path is held once instead of twice.Measured on Linux with the ignored test
nfs::tests::a_walk_of_a_big_folder(50,000 files, valgrind massif):What remains after a trim is the per-file table. On macOS it lasts until the unmount, since the NFS client never forgets a file. Spilling that table to disk is a possible follow-up, depending on the Mac measurement.
API changes (called out)
Home::trimandTrimmedare new.Workspace::trimis new, andSource::trimis new with a default.VPath's representation changed; its API did not.🤖 Generated with Claude Code
https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK