NixOS / nix

Nix, the purely functional package manager
https://nixos.org/
GNU Lesser General Public License v2.1
12.94k stars 1.53k forks source link

Missing clarity around when nix caches evaluation for local filesystem flakes #10437

Open dsilva opened 7 months ago

dsilva commented 7 months ago

Problem

The documentation at these sections seems to be missing a description of how nix flake caching works for flakes in the local filesystem referenced via path: or git+file:

With nix 2.21.0 on aarch64-darwin, define a flake and lock it:

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/89653a03e0915e4a872788d10680e7eec92f8600";
  };
  outputs = { self, nixpkgs }:
    let pkgs = nixpkgs.legacyPackages.aarch64-darwin;
    in {
      packages.aarch64-darwin.default = pkgs.buildEnv {
        name = "repro-env";
        paths = with pkgs; [
          findutils
        ];
      };
    };
}
nix flake lock

Try building it several times, each time with:

time nix build --no-link --no-update-lock-file "path:$(realpath $PWD)"

It takes multiple seconds every time, so it looks like nix isn't using the evaluation cache in this case. Same if repeat with . instead of path:$(realpath $PWD).

Now make the current directory into a git repository and try again:

git init
git add flake.nix flake.lock
git commit -m 'initial' flake.nix flake.lock
time nix build --no-link --no-update-lock-file .
time nix build --no-link --no-update-lock-file .
time nix build --no-link --no-update-lock-file .
time nix build --no-link --no-update-lock-file .

The first two builds are slow (over 3 seconds) and subsequent ones are fast (under 200ms). It would be useful for the documentation to explain when/why caching kicks in (seemingly starting with the third build).

Now update flake.nix to include gnugrep after findutils. Building the installable . now multiple times doesn't faster -- nix spends multiple seconds in evaluating derivation 'git+file:///private/tmp/example#packages.aarch64-darwin.default'.

If you commit that change with git commit -m 'grep' flake.nix and retry the builds, now you get the earlier behaviour of two slow builds followed by sub-200ms ones.

Proposal

Should the docs explain that nix flake evaluation caching for local files only works for source-controlled flake.nix files? (Not path:, not local tarballs, and not . or git+file with uncommitted changes). If I remember correctly, this worked with earlier versions of nix (2.10?), so it might be something people don't know they shouldn't rely on anymore.

It's also unclear if/when adding a ref (like ?ref=HEAD) or ?narHash=... makes a difference.

Checklist

Priorities

Add :+1: to issues you find important.

dsilva commented 7 months ago

I just checked, and the behaviour was different in nix 2.20 and below. Nix would cache flakes referenced on the nix build command line with the path: scheme, and it stopped doing that in 2.21.0.

andreykaipov commented 7 months ago

Should the docs explain that nix flake evaluation caching for local files only works for source-controlled flake.nix files

I've ran into this as well. I'm also on aarch64-darwin. After upgrading to Nix 2.22, subsequent evaluations of my flake with unstaged changes became very slow. Even if I didn't make any other changes, just the presence of having a dirty worktree made evaluation times take like 2mins. This made iteration on my flake incredibly cumbersome and frustrating. After I committed my changes, subsequent evaluation times went back down to a reasonable amount of time (<1s).

After downgrading to Nix 2.20 things are quick once again. Unstaged changes still cause evaluation to happen, but the evaluation takes at most 5s. And subsequent evaluations after that take <1s. I haven't tested this anywhere else but on aarch64-darwin.

I know this specific issue is for documentation, but it would be really great to opt out of the new evaluation/caching behaviour. I'm not sure if it was intended, but it feels like a worse experience to me and I'd love it there were a flag in future versions or something.