Skip to content

Don't copy flakes to the store unnecessarily (maybe 3rd time's the charm) - #15711

Merged
Mic92 merged 3 commits into
masterfrom
lazy-store-paths-for-flakes
Apr 27, 2026
Merged

Mic92 merged 3 commits into
masterfrom
lazy-store-paths-for-flakes

Conversation

@xokdvium

@xokdvium xokdvium commented Apr 19, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

3rd time's the charm!

This builds on top of #14050 to
actually make flakes get lazily copied to the store. This repurposes
a slightly less lazy (but also more deterministic) approach than
determinate nix has taken. We do still pay to cost of hashing an input
once to compute the store path and narHash daemon-client-side. This
could be improved in follow-ups in case we don't actually need to check
the narHash (like during local development).

We need certain backwards compatibility hacks for getFlake with a discarded string
context, those are similar to what detnix does.

Context


Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

@xokdvium
xokdvium requested a review from edolstra as a code owner April 19, 2026 11:32
@github-actions github-actions Bot added new-cli Relating to the "nix" command with-tests Issues related to testing. PRs with tests have some priority labels Apr 19, 2026
@xokdvium

Copy link
Copy Markdown
Contributor Author

Lol, we have a pre-existing UAF in the C API with readOnlyMode.... Why was it even done in such a sucky way.

@github-actions github-actions Bot added the c api Nix as a C library with a stable interface label Apr 19, 2026
Comment thread src/libexpr-c/nix_api_expr.cc
@xokdvium
xokdvium force-pushed the lazy-store-paths-for-flakes branch 2 times, most recently from 59d7d81 to be29086 Compare April 19, 2026 13:43
@Mic92

Mic92 commented Apr 20, 2026

Copy link
Copy Markdown
Member

@xokdvium do you have numbers for evaluating nixpkgs on this?

@xokdvium

Copy link
Copy Markdown
Contributor Author

On my machine I got a 3x improvement for a fresh instance of nixpkgs (github:nixos/nixpkgs) when it's not already in the tarball-cache and not pre-hashed. So 30s -> 10s wall time, though a good chunk of that is just downloading the tarball.

With a local nixpkgs checkout and a warm page cache I got git+file:///path/to/nixpkgs?shallow=1#hello to run in 3s.

@xokdvium

xokdvium commented Apr 20, 2026 •

Copy link
Copy Markdown
Contributor Author

One slight wrinkle here is that I'd need to port over the Path contexts from #13225 to handle toString ./., otherwise quite a bit broken code might start fail to evaluate.

Scratch that, it's not necessary - this PR doesn't have the issue with store path rewriting.

@xokdvium xokdvium mentioned this pull request Apr 20, 2026
Comment thread src/libexpr/include/nix/expr/eval-settings.hh
@Mic92

Mic92 commented Apr 20, 2026

Copy link
Copy Markdown
Member

This was some leftovers from detnix cherry-pick, we ban string contexts
in getFlake and have always.
@xokdvium
xokdvium force-pushed the lazy-store-paths-for-flakes branch from be29086 to 7163570 Compare April 25, 2026 21:28
@xokdvium

Copy link
Copy Markdown
Contributor Author

@Mic92, fixed both of the issues (the global lstat cache was dropped in dirFd accessor PRs, storePath issue is fixed here).

@xokdvium
xokdvium force-pushed the lazy-store-paths-for-flakes branch from 7163570 to 0b2b331 Compare April 25, 2026 21:30
This builds on top of #14050 to
actually make flakes get lazily copied to the store. This repurposes
a slightly less lazy (but also more deterministic) approach than
determinate nix has taken. We do still pay to cost of hashing an input
once to compute the store path and narHash daemon-client-side. This
could be improved in follow-ups in case we don't actually need to check
the narHash (like during local development).

We need certain backwards compatibility hacks for getFlake with a discarded string
context, those are similar to what detnix does.

See: DeterminateSystems#422
See: DeterminateSystems#402

Co-authored-by: Eelco Dolstra <edolstra@gmail.com>
@xokdvium
xokdvium force-pushed the lazy-store-paths-for-flakes branch from 0b2b331 to 891ef14 Compare April 25, 2026 21:32
@Mic92

Mic92 commented Apr 26, 2026 •

Copy link
Copy Markdown
Member

will test this in my own fork now: Mic92/dotfiles#5528

@Mic92
Mic92 added this pull request to the merge queue Apr 27, 2026
Merged via the queue into master with commit c2acffe Apr 27, 2026
20 checks passed
@Mic92
Mic92 deleted the lazy-store-paths-for-flakes branch April 27, 2026 20:23
Comment thread src/libexpr/paths.cc
Comment on lines +48 to +53
panic(fmt(
"hashed store path computed by the evaluator ('%1%') does not match what was computed when copying to the store ('%2%'), this is a bug",
store->printStorePath(path),
store->printStorePath(storePath)));
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It occurs to me that this should probably be a hash mismatch error instead...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yup, I hit this assert when modifying a dirty tree while stuff was being evaluated. Will put up a PR making this an error instead. It's not that big of an issue of course, since that's a trade off we must make if we want to avoid copying stuff to the store eagerly.

@xokdvium xokdvium mentioned this pull request Jun 24, 2026
@xokdvium xokdvium mentioned this pull request Jul 16, 2026
andrewgazelka added a commit to indexable-inc/index-bak that referenced this pull request Jul 19, 2026
Backports the lazy trees variant upstream merged to master
(NixOS/nix#15711 plus its two post-merge fixes, NixOS/nix#15950 and
NixOS/nix#16078) onto the 2.34.7 base as patches 0024-0027, and gates
the behavior behind a new off-by-default `lazy-trees` eval setting
(patch 0028). With the setting off, mountInput() copies eagerly exactly
like the base and the ensure functions return immediately; with it on,
flake inputs mount at their final content-addressed store paths (hashed
via dry run, so paths, hashes, and lock files are unchanged by
construction) and only materialize when something forces them.

Both sources named in the issue were examined: edolstra's lazy-trees-v2
(NixOS/nix#13225, random virtual store paths plus on-demand
devirtualization) was closed unmerged on 2026-07-16 after roberth's
determinism objection, pointing at #15711 as the mergeable subset; and
Determinate's fork shipped lazy trees via staged rollouts since 3.8.0,
but its current v2.35.1 tree has dropped the random-virtual-path
implementation entirely and rides the same upstream mechanism vendored
here. The pushback (roberth's impurity objection and rope-string
lazy-hashing alternative, the toString-without-context wrinkle) is
documented in the patch 0025 and 0028 bodies and the site update page.

packages/site/src/lib/updates/nix-fork-lazy-trees-setting.svx

Closes #3645

(PR authored by an AI agent via Claude Code, Claude Opus 4.5)
andrewgazelka added a commit to indexable-inc/index-bak that referenced this pull request Jul 19, 2026
Backports the lazy trees variant upstream merged to master
(NixOS/nix#15711 plus its two post-merge fixes, NixOS/nix#15950 and
NixOS/nix#16078) onto the 2.34.7 base as patches 0024-0027, and gates
the behavior behind a new off-by-default `lazy-trees` eval setting
(patch 0028). With the setting off, mountInput() copies eagerly exactly
like the base and the ensure functions return immediately; with it on,
flake inputs mount at their final content-addressed store paths (hashed
via dry run, so paths, hashes, and lock files are unchanged by
construction) and only materialize when something forces them.

Both sources named in the issue were examined: edolstra's lazy-trees-v2
(NixOS/nix#13225, random virtual store paths plus on-demand
devirtualization) was closed unmerged on 2026-07-16 after roberth's
determinism objection, pointing at #15711 as the mergeable subset; and
Determinate's fork shipped lazy trees via staged rollouts since 3.8.0,
but its current v2.35.1 tree has dropped the random-virtual-path
implementation entirely and rides the same upstream mechanism vendored
here. The pushback (roberth's impurity objection and rope-string
lazy-hashing alternative, the toString-without-context wrinkle) is
documented in the patch 0025 and 0028 bodies and the site update page.

packages/site/src/lib/updates/nix-fork-lazy-trees-setting.svx

Closes #3645

(PR authored by an AI agent via Claude Code, Claude Opus 4.5)
@nixos-discourse

Copy link
Copy Markdown

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/nixpkgs-multiverse-every-version-that-ever-existed/79490/6

@nixos-discourse

Copy link
Copy Markdown

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/nixpkgs-multiverse-every-version-that-ever-existed/79490/26

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c api Nix as a C library with a stable interface new-cli Relating to the "nix" command with-tests Issues related to testing. PRs with tests have some priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants