Content-addressed Nix − call for testers

I have below in my configuration:

  nix = {
    package = pkgs.nixUnstable;
    extraOptions = ''
      experimental-features = nix-command flakes ca-derivations ca-references
    '';
}

And I’ve been getting this while doing any operation (flake update, nixos-rebuild):

warning: unknown experimental feature 'ca-references'

I have tried searching but haven’t found anything for a fix. I’m on unstable NixOS channel if its of any relevance.

I had the same and after ogling the source a bit I decided that it was removed and now baked-in.

So I took it out.

Shouldn’t need it anymore:

https://github.com/NixOS/nix/pull/5247

2 Likes

Thanks @wmertens and @armeenm

Trying to build the discord-plugged package from the overlay here GitHub - LavaDesu/powercord-overlay: A Nix flake to install Discord with Powercord fails and I don’t know where to start debugging.

$ nix-env --version
nix-env (Nix) 2.5.0pre20211206_d1aaa7e
$ nix run .#caPkgs.discord-plugged
error: access to absolute path '/0vfggackkifmvxhxpaapx26lswwg5xcr54dk1cmdgg90j4jw9njc' is forbidden in pure eval mode (use '--impure' to override)

https://hastebin.com/faqutiqufa.txt

I had a similar problem because my NixOS config used import-from-derivation (IFD, builtins.readFile drv in my case). This also seems to do that here https://github.com/LavaDesu/powercord-overlay/blob/7fa092ccd95c81bdd28802aec77649e02c34aaa9/drvs/powercord.nix#L13

If you can find a way to make it work without the readFile, I think it would solve your problem

Indeed as @iago said, that looks like an import-from-derivation, and IFD is broken with CA derivations right now (confusingly enough, I never noticed that until now). I’ve opened CA-derivations aren’t compatible with IFD · Issue #5805 · NixOS/nix · GitHub for that. Hopefully that’s relatively easy to fix.

4 Likes

Are people using content-addressed Nix successfully? Is it fast?

I didn’t play with it in a while, and I’m thinking of trying it again.

It works quite well to skip builds if the input is the same (especially with hydra). Disadvantage for now is that it requires rebuilding a lot of things (although there is an option to convert your store), haven’t dared trying that though.

Haven’t had issues with speed, but it seems to be querying the cache more often, may just be me though.

The UX can also sometimes be kinda weird, where it says things need to be built but then skips all of them, things like that.

In general it seems quite usable though, so that’s nice :slight_smile:

2 Likes

I was able to remove that one IFD and immediately ran into another with yarn2nix. Going to try soon with the IFD fix which got merged rather than trying to remove it.

Am I understanding correctly that this won’t help in a case where:

I change the value of one constant in libinput → new output, new content, new address in nix-store.
Something which depends on libinput directly is rebuilt. It references the new libinput.
It is bit for bit identical, except for a reference to libinput’s new address in the nix store, so now it has a new hash and a new address.
Now the entire tree of reverse dependencies of libinput is still rebuilt.

So this helps avoid rebuilds when the content doesn’t change at all, but it doesn’t avoid rebuilds in cases like the above where the only change the content is a reference to somewhere else in the nix store.
I find it really slow to develop a package like libinput using nixos in a running system because I have to wait for a huge number of desktop environment packages to build. I was initially hoping CA derivations would help with this but I guess I got that wrong.

Are there any related efforts to make testing changes to a single package with many reverse dependencies faster?

1 Like

Yep. How could we not rebuild reverse dependencies in this case? If reverse dependencies refer to the old libinput path, they can only refer to the old behavior, because we can’t have two different versions of libinput at the same path.

It might be possible to copy the last derivation and only change the references to libinput in this example. To automate this maybe some sort of ABI hash? If the ABI hash of all packages you depend on matches that of another build already in the store, you can copy that build and replace references to the store with the correct version. I think this would have to be some sort of optional optimisation, and it’s risky as if whatever your ABI hash looks like is wrong. Not sure this is a good idea at all.

My current idea for speeding this up in local dev scenarios is launching with a bind mount over /nix/store/***-libinput which replaces the old one with whatever dev one I’m testing, using bubblewrap. No need to make sweeping changes that also kinda break nix’s model, no risk of damaging my local nix store.

Hacky but should be fairly easy to set up.

1 Like

ABI is well outside the scope of the Nix package manager IMO. What that even means varies from language to language, and programming languages aren’t even the full extent of the kinds of things Nix can manage.

That’s the cost you pay for deterministic and reproducible (system) builds.

When dependent packages get the same ca-hash, all their dependencies don’t need to be rebuilt though, so using ca might still help. If dependent packages do change, it’s good that they’re being rebuilt.

I find it really slow to develop a package like libinput using nixos in a running system because I have to wait for a huge number of desktop environment packages to build.

There’s a somewhat hidden feature on NixOS to allow for preventing a huge amount of rebuilds for such a development use-case you mention: system.replaceRuntimeDependencies

2 Likes

Since the hash includes other references to the nix store, this will in practice never happen.

For example rebuilding libinput:

$ nix-store --query --referrers /nix/store/wdb0ahcggiv0p2mhk6d6z86aihvgk1yi-libinput-1.19.1
/nix/store/wdb0ahcggiv0p2mhk6d6z86aihvgk1yi-libinput-1.19.1
/nix/store/05wm03dkvsafkwxszgh8z7naarhkjp4g-unit-systemd-udevd.service
/nix/store/q77d0xbxs2wfksyp60rk3p5agh23jrch-qtbase-5.15.3
/nix/store/rvvwdafk9x49crbyga799lndl7mrnfxz-qtbase-5.15.3-bin
/nix/store/7cycmb00z500wg2mbns4vl4waycybn49-kwin-5.23.3
/nix/store/d4lgmvzwq3xxc835b7k3h82sis4yw7r3-xf86-input-libinput-1.2.0
/nix/store/lmcbbywyy13gysyw4vrxw3iw91lahgds-qtbase-5.15.3-dev
/nix/store/ma8nb0z778zpiddd8grybafksd3yykfv-mutter-41.2
/nix/store/wbdd7zam435zwx78gqxsz61c9hw0zlr4-udev-rules

When any of those reverse dependencies rebuild, they will have a reference to the new libinput’s path in them. It can produce an output that’s almost bit identical, except the path to libinput in the nix store in it somewhere is different, so it has a different hash and has been rebuilt. So now qtbase5 has a different hash and everything dependent on it still needs rebuilt, and this will continue all the way through the reverse deps. qtbase5 has 29 things referring to it, each of which will end up with a different hash even if all their content except for references into the nix store is identical.

When dependent packages get the same ca-hash, all their dependencies don’t need to be rebuilt though

I don’t see how ca derivations can ever stop this chain of reverse dependencies from needing fully rebuilt in the current implementation. If one dep at the start has a single bit different its hash will be different, and then anything that references it will have a different hash, and this will repeat all the way through the chain of dependencies.

1 Like

Oh yeah, fair point. That’s true. It’s only useful when outputs keep the same hash.

I thought about this a lot for RFC 17 and in the end I decided it is impossible to generally shortcut these kinds of references, since a build can always decide to copy things from a dependency’s references. For example a static build.

However, we can make a store that de-duplicates content by patching out store paths before saving.

I flesh out that idea in https://gist.github.com/wmertens/eceebe0fc05461ebdc8fb106d90a6871

Thoughts?

4 Likes

The problem could be solved at the nixpkgs level by having C(++) style derivations only take their dependencies’ headers as inputs, producing shared libraries that don’t actually use absolute paths to refer to other libraries, and having separate derivations up the chain take on the responsibility of linking together shared libraries correctly. This has a good number of drawbacks, so this is not a serious suggestion, but it would have the side benefit of allowing dependent packages to be built in parallel if they could output their headers in separate derivations.

2 Likes