Skip to content

Give memory back, and release 0.9.0 - #190

Merged
lydakis merged 12 commits into
mainfrom
claude/project-thread-dorvpr
Oct 28, 2026
Merged

lydakis merged 12 commits into
mainfrom
claude/project-thread-dorvpr

Conversation

@lydakis

@lydakis lydakis commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

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).

  • Bounded caches (sandfs-core): listings (32 MiB), sizes listings (8 MiB), directories found (8 MiB per mount) and whole-file bytes (16 MiB) are each kept in two halves. When the young half reaches half the budget it becomes the old half and the previous old half goes; an entry used from the old half moves back. Everything there can be fetched again (the Source contract says answers never change). Prefetch and walk notes start over past 65,536 directories.
  • Trim: Workspace::trim drops all of it and calls the new Source::trim (default: nothing); GitSource ends its idle cat-file readers. Shared::trim saves changes first, then trims. The server trims a mount nobody has used for a minute, and on request: sandfs trim [NAME], Home::trim (returns Trimmed).
  • Handing memory back: memory::give_back calls malloc_zone_pressure_relief(NULL, 0) on macOS and malloc_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 from forbid to deny, with #[allow(unsafe_code)] on that function only.
  • File table: VPath is now an Arc<str>, and a mount's path-to-inode table is keyed by VPath. 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):

Heap
Peak, before this change 30.7 MB
Peak, after this change 25.3 MB
After a trim 13.2 MB

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)

  • sandfs: Home::trim and Trimmed are new.
  • sandfs-core: Workspace::trim is new, and Source::trim is new with a default. VPath's representation changed; its API did not.

🤖 Generated with Claude Code

https://claude.ai/code/session_013jkdGETmgGUb8hcSEpppQK

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
@lydakis lydakis self-assigned this Oct 28, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-28T03:25:31.015569Z 08191eb New commits
🔒 Security Review ✅ Completed 2026-10-28T03:27:11.791091Z 08191eb New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@lydakis
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
@lydakis lydakis changed the title Release 0.9.0 Give memory back, and release 0.9.0 Oct 28, 2026
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
lydakis marked this pull request as ready for review October 28, 2026 03:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread crates/sandfs/src/memory.rs Resolved
Comment thread crates/sandfs-core/src/workspace.rs Resolved
…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
@lydakis
lydakis merged commit 16ce565 into main Oct 28, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants