A Commit refused for disk Capacity leaves its packs in the Store, and every later write is refused Capacity
Maintainer thường phản hồi trong vòng 1 ngày
@jiangkoumo đang làm issue này rồi.
Từ ngày 8/11/2026.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 43/100
Hướng nghiên cứu
Start with 05-storage.md and the Store pack persistence and reclamation paths; the issue identifies Handles::reclaim_space and a reproducible failure during workspace.commit. Trace how packs written before a refused commit are retained and how fs.write maps a persistence Capacity error. Done means failed commits leave unreferenced packs reclaimable, a small write succeeds after the workspace shrinks, and Capacity is reported consistently.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Summary
When workspace.commit hits a full disk, it is refused Failed | pack persistence: Refused { status: "Capacity" }, and nothing is committed. That part is correct. But the packs it already wrote stay in store.sqlite (about 273 MB in this repro). Nothing references them, and no public or operator call removes them. After that, every write on that Store is refused Capacity: a 1 MB Commit, a write on another Branch, and a 5-byte fs.write from a new process. Deleting the large file from the Workspace does not help. The only way out is to free disk outside LayerFS or delete the Store.
Environment
main5fb68ddc1(1.0.0 plus #376–#378). Also reproduces on792715f.- x86_64 Debian 13, daemon and addon built on the host with
cargo +1.85.1 --offline --locked --release. - Store on ext4. Runtime image is Node 22 on trixie.
Reproduce
To fill the disk quickly, the Store volume is a plain local volume whose _data is bind-mounted from a 300 MiB ext4 loop disk. Any Store disk that fills up should behave the same way.
-
project.inita 26 KB project,branch.fork("f"), thenworkspace.mount("f"). -
In the Workspace:
head -c 400000000 /dev/urandom > big.bin. -
workspace.commit(ws)→Failed: physical Store I/O: pack persistence: Refused { status: "Capacity" }, withdetail.phase="Commit Construct".list_commits("f")is empty, which is correct. -
operator.store.stats():before after the refused Commit logical 26,260 26,260 canonical 30,259 272,529,372 stored 13,066 275,696,868 free — 0 The disk has 268 MiB used and 2 KiB free.
store.sqliteis 275,591,168 bytes. -
The same Workspace runs
rm big.binand writes 1 MB, then commits →Refused { status: "Capacity" }, with phase"Commit Begin". -
branch.fork("g")+fs.write("g", "/x.txt", "hello")→Capacity: capacity: persistence. -
A new process opens the Store fresh, with the old Sandbox gone.
stats()is unchanged.fs.write("f", "/tiny.txt", "hello")→Unknown: Service(History(Capacity("persistence")))fork+mount+commit→Capacity
Repro script
The Store volume lfsb-full is a local volume whose _data is bind-mounted from a 300 MiB ext4 loop disk (truncate -s 300M img; mkfs.ext4 -m 0 img; mount -o loop img /mnt/small; mount --bind /mnt/small/store /var/lib/docker/volumes/lfsb-full/_data). The LAYERFS_* env points at that volume.
import { LayerFS } from "./sdk/js/src/index.js";
import { LayerFSOperator } from "./sdk/js/operator/index.js";
const [project, src] = process.argv.slice(2);
const lfs = new LayerFS(project); const op = new LayerFSOperator(lfs);
const T = async (k, f) => { try { const r = await f(); console.log(k, "ok", JSON.stringify(r).slice(0, 400)); return r; } catch (e) { console.log(k, "FAIL", e.code, e.message, JSON.stringify(e.detail)); return null; } };
try {
await T("init", () => lfs.project.init(src));
await T("fork", () => lfs.branch.fork("f"));
const ws = await T("mount", () => lfs.workspace.mount("f"));
await T("write-400MB", () => lfs.workspace.exec(ws, "head -c 400000000 /dev/urandom > big.bin"));
await T("commit-full", () => lfs.workspace.commit(ws, "too big"));
await T("stats", () => op.store.stats());
await T("list-commits", () => lfs.branch.list_commits("f", 5, 0));
await T("shrink", () => lfs.workspace.exec(ws, "rm big.bin && head -c 1000000 /dev/urandom > mid.bin"));
await T("commit-small", () => lfs.workspace.commit(ws, "small"));
await T("fs.write-other-branch", () => lfs.branch.fork("g").then(() => lfs.fs.write("g", "/x.txt", "hello", "w")));
await T("unmount", () => lfs.workspace.unmount(ws, true));
await T("stats-end", () => op.store.stats());
} finally { await lfs.close(); }
The reopen step is a second process: stats(), fs.write("f", "/tiny.txt", "hello"), then fork + mount + commit.
Expected
- A Commit refused before publication should leave no persistent bytes. Its packs should be deleted on the definite failure, or reclaimed on the next open, or a documented operator call should be able to reclaim them. After the Workspace shrinks, a small Commit should succeed.
stats()should not count packs that nothing references.fs.writeshould refuseCapacityhere, notUnknown. Per #372,Unknownalso keeps its Store call Workspace.
Notes
- I checked issues and PRs through #378 and found no report of this. #311 and #321 cover Workspace-overlay reclamation, and they say that local overlay maintenance does not delete global canonical data. #372 is a different Capacity, the leak of call Workspaces, but it shares the
Unknownmapping in the last bullet above. 05-storage.mdmentionsHandles::reclaim_space. It does not seem to be reachable from the SDK or the operator, and I did not check whether it would release these packs.- I have not tried a fix. It seems to need Store-side custody of the attempt's packs, for example a pending owner row that is deleted on a definite failure plus a reclaim on open for crashed attempts. I would rather hear how you want it done first. I'm happy to send a PR for the
Unknown→Capacitymapping on its own.
- Ngôn ngữ chính
- Rust
- Star
- 2.1k
- Fork
- 110
- Merge trung bình
- 23 giờ 52 phút
- Pull request đã merge (30 ngày)
- 138
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của Ephemeral-AI-Lab/layerfs
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Ephemeral-AI-Lab/layerfs#689 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
Ephemeral-AI-Lab/layerfs#643 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
`branch.publish` accepts a stale base and overwrites `main` (lost update)Có thể đã có người làm @mengmengmengqiang đã nhận hôm nay. Đang mởapi/calls bug core/history
Ephemeral-AI-Lab/layerfs#691 · 1 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 8/100
Ephemeral-AI-Lab/layerfs#684 · 10 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
LayerFS Paper Write in PublicĐang mởdocs
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 15/100
Ephemeral-AI-Lab/layerfs#683 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của Ephemeral-AI-Lab/layerfs
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
pop-os/cosmic-settings#2296 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
jonathanong/no-mistakes#3431 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
Eyevinn/open-live-ingest#36 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày