Skip to content

Conversation

@getchoo
Copy link
Member

@getchoo getchoo commented Sep 22, 2025

Motivation

Previously, Nix would not create a cache entry for substituted/cached
inputs

This led to severe slowdowns in some scenarios where a large input (like
Nixpkgs) had already been unpacked to the store but didn't exist in a
users cache, as described in #11228

In my testing (on WSL) the speedup can be pretty drastic. As an example, here's
running nix eval in this repo after already ensuring inputs are in the store
with nix flake prefetch-inputs

Before:

$ nix --version
nix (Nix) 2.32.0pre20250921_f66b56a
$ hyperfine -N -p "rm -rf $HOME/.cache/nix" 'nix eval'
Benchmark 1: nix eval
  Time (mean ± σ):     21.665 s ±  3.579 s    [User: 3.417 s, System: 5.434 s]
  Range (min … max):   18.952 s … 29.814 s    10 runs

  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.

After:

$ hyperfine -N -p "rm -rf $HOME/.cache/nix" "$PWD/outputs/out/bin/nix eval"
Benchmark 1: /home/seth/repos/nix/outputs/out/bin/nix eval
  Time (mean ± σ):      1.940 s ±  0.058 s    [User: 1.163 s, System: 0.498 s]
  Range (min … max):    1.868 s …  2.014 s    10 runs

This is a ~91% increase in performance!

Speaking of nix flake prefetch-inputs, it seems this bug also affected it, and paths would be re-copied on any uncached invocation. In practice on my machine, this made a run of take 45.5s with this repo's flake (after running rm -rf ~/.cache/nix) even though all inputs were already in the store. With this PR, it now takes about 300ms (...and I assume this is just evaluating the flake itself)

Context

Hopefully fixes #11228

This is basically the fix introduced in #12911, but adopted in a different, similarly affected code path

I'm not exactly sure if this the best solution to do that...but from what I
understand, I think it should be safe to keep the original Input information
and pair it with an accessor with a fingerprint of a technically different
input. After all, they should have the same contents given the same hash, and
attributes like lastModified go unaffected as they aren't stored in the
accessor anyways


Add 👍 to pull requests you find important.

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

@getchoo getchoo requested a review from edolstra as a code owner September 22, 2025 11:05
@github-actions github-actions bot added the fetching Networking with the outside (non-Nix) world, input locking label Sep 22, 2025
@edolstra
Copy link
Member

This doesn't seem right to me, since it leads to an input returning a different fingerprint depending on whether it's in the Nix store or not.

@getchoo getchoo force-pushed the getchoo/cache-substituted-inputs branch from 5e90b64 to c17e3ba Compare September 24, 2025 05:39
@getchoo
Copy link
Member Author

getchoo commented Sep 24, 2025

Took a different approach by just copying the fix from #12911 directly here instead of trying to re-use the whole code path. Seems to have the same effect!

Previously, Nix would not create a cache entry for substituted/cached
inputs

This led to severe slowdowns in some scenarios where a large input (like
Nixpkgs) had already been unpacked to the store but didn't exist in a
users cache, as described in NixOS#11228

Using the same method as NixOS#12911, we can
create a cache entry for the fingerprint of substituted/cached inputs
and avoid this problem entirely
@getchoo getchoo force-pushed the getchoo/cache-substituted-inputs branch from c17e3ba to 74305d5 Compare September 25, 2025 08:05
@getchoo
Copy link
Member Author

getchoo commented Sep 25, 2025

Added a release note as well, since it seems this bug has a bit of a following 😄

@xokdvium
Copy link
Contributor

This will be better fixed by #14050, right?

@Mic92
Copy link
Member

Mic92 commented Sep 25, 2025

I actually don't know. @edolstra ?

@getchoo
Copy link
Member Author

getchoo commented Sep 25, 2025

While #14050 is an obvious improvement to fetchToStore() caching generally, AFAICT it doesn't fix this specific issue (tested by merging it into 0175f7e and running nix eval on this repo again, same as above)

This sorta makes sense? As it only touches the unsubstituted code path there, plus the explicit caching originally introduced in #12911 remains

Now I don't really know much about libfetchers yet (or how Nix works generally...), so take this with a grain of salt: I'm pretty sure this patch is still necessary since the accessor we're constructing here hasn't undergone any previous fingerprinting or received an entry to the cache. This ends up similar to what the regular getAccessor methods for each InputScheme does, which is ensure the content exists in the store -> fingerprint it -> create an entry in the cache for that fingerprint. The unsubstituted code path handles this by simply calling to those getAccessor methods which handle all the caching for us, but since here we're grabbing an accessor from the store directly, we need to implement that same caching logic (similar to how we already implement the second step of fingerprinting)

@Mic92 Mic92 merged commit 099a74e into NixOS:master Sep 25, 2025
15 checks passed
@getchoo getchoo deleted the getchoo/cache-substituted-inputs branch September 25, 2025 12:05
@nixos-discourse
Copy link

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

https://discourse.nixos.org/t/nix-2-32-0-released/70528/1

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

Labels

documentation fetching Networking with the outside (non-Nix) world, input locking

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nix copies inputs already in the nix store

5 participants