From 74305d52606d21baeb4e07946b6e84fac87a6c52 Mon Sep 17 00:00:00 2001 From: Seth Flynn Date: Mon, 22 Sep 2025 05:59:11 -0400 Subject: [PATCH] libfetchers: avoid re-copying substituted inputs 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 https://github.com/NixOS/nix/issues/11228 Using the same method as https://github.com/NixOS/nix/pull/12911, we can create a cache entry for the fingerprint of substituted/cached inputs and avoid this problem entirely --- doc/manual/rl-next/cached-substituted-inputs.md | 10 ++++++++++ src/libfetchers/fetchers.cc | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 doc/manual/rl-next/cached-substituted-inputs.md diff --git a/doc/manual/rl-next/cached-substituted-inputs.md b/doc/manual/rl-next/cached-substituted-inputs.md new file mode 100644 index 000000000..b0b53a213 --- /dev/null +++ b/doc/manual/rl-next/cached-substituted-inputs.md @@ -0,0 +1,10 @@ +--- +synopsis: "Substituted flake inputs are no longer re-copied to the store" +prs: [14041] +--- + +Since 2.25, Nix would fail to store a cache entry for substituted flake inputs, +which in turn would cause them to be re-copied to the store on initial +evaluation. Caching these inputs results in a near doubling of a performance in +some cases — especially on I/O-bound machines and when using commands that +fetch many inputs, like `nix flake archive/prefetch-inputs` diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 54013bf55..b056c137d 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -5,6 +5,7 @@ #include "nix/util/json-utils.hh" #include "nix/fetchers/store-path-accessor.hh" #include "nix/fetchers/fetch-settings.hh" +#include "nix/fetchers/fetch-to-store.hh" #include @@ -336,6 +337,15 @@ std::pair, Input> Input::getAccessorUnchecked(ref sto accessor->fingerprint = getFingerprint(store); + // Store a cache entry for the substituted tree so later fetches + // can reuse the existing nar instead of copying the unpacked + // input back into the store on every evaluation. + if (accessor->fingerprint) { + ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive; + auto cacheKey = makeFetchToStoreCacheKey(getName(), *accessor->fingerprint, method, "/"); + settings->getCache()->upsert(cacheKey, *store, {}, storePath); + } + accessor->setPathDisplay("«" + to_string() + "»"); return {accessor, *this};