Skip to content

Comments

pkgs.path: Avoid copying when used via flake#153594

Merged
pennae merged 5 commits intoNixOS:masterfrom
hercules-ci:flake-avoid-copying-nixpkgs-path
Jan 22, 2022
Merged

pkgs.path: Avoid copying when used via flake#153594
pennae merged 5 commits intoNixOS:masterfrom
hercules-ci:flake-avoid-copying-nixpkgs-path

Conversation

@roberth
Copy link
Member

@roberth roberth commented Jan 5, 2022

Motivation for this change
Things done
  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandbox = true set in nix.conf? (See Nix manual)
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 22.05 Release Notes (or backporting 21.11 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
    • (Release notes changes) Ran nixos/doc/manual/md-to-db.sh to update generated release notes
  • Fits CONTRIBUTING.md.

@roberth roberth requested a review from pennae January 5, 2022 13:04
@github-actions github-actions bot added 6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (update) This PR changes an existing module in `nixos/` labels Jan 5, 2022
@ofborg ofborg bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. labels Jan 5, 2022
@pennae
Copy link
Contributor

pennae commented Jan 5, 2022

we tried pretty much exactly this before giving up and copying subdirectories explicitly, since it doesn't seem to work:

locking path '/nix/store/0n8zdf8sn8hk1pdqczgczz09gf8rcx97-mf2c1hpfds2fv501d1jc6pxc07qym974-nixpkgs'
lock acquired on '/nix/store/0n8zdf8sn8hk1pdqczgczz09gf8rcx97-mf2c1hpfds2fv501d1jc6pxc07qym974-nixpkgs.lock'
'/nix/store/0n8zdf8sn8hk1pdqczgczz09gf8rcx97-mf2c1hpfds2fv501d1jc6pxc07qym974-nixpkgs/.editorconfig' has hash 'sha256:1wkj2wfy26ylp3dbrhbiw71y9fci11mbi8pj7zsdw8i1xncll2g3'
linking '/nix/store/0n8zdf8sn8hk1pdqczgczz09gf8rcx97-mf2c1hpfds2fv501d1jc6pxc07qym974-nixpkgs/.editorconfig' to '/nix/store/.links/1wkj2wfy26ylp3dbrhbiw71y9fci11mbi8pj7zsdw8i1xncll2g3'
'/nix/store/0n8zdf8sn8hk1pdqczgczz09gf8rcx97-mf2c1hpfds2fv501d1jc6pxc07qym974-nixpkgs/.git/HEAD' has hash 'sha256:0fq06xi94yqqr4snhcgax31da3rmyzd0wm6804xgks8psjgrr4wf'
[ repeat for all of nixpkgs ]

the only way to avoid having a redundant copy is to force the name of the resulting store path to be the same as the source store path (eg, path { name = "source"; path = pkgs.path; }), but that only avoids the duplication, not the copy. all the hashing and comparing will still take place, at which point it's much cheaper to only copy the necessary directories.

the resulting drv for lazy-options starts like this:

❯ nix show-derivation /nix/store/k5mzim7l3rdh02xpflgks82cb6scdbrn-lazy-options.json.drv
{
  "/nix/store/k5mzim7l3rdh02xpflgks82cb6scdbrn-lazy-options.json.drv": {
    "outputs": {
      "out": {
        "path": "/nix/store/lrzp2dipfv773wvf4mr5lk2rfrj2r07i-lazy-options.json"
      }
    },
    "inputSrcs": [
      "/nix/store/0n8zdf8sn8hk1pdqczgczz09gf8rcx97-mf2c1hpfds2fv501d1jc6pxc07qym974-nixpkgs",
      "/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"
    ],

@roberth
Copy link
Member Author

roberth commented Jan 5, 2022

Was that without the flake?

With the flake, it should use outPath, which is a string with context, so that should be efficient.
Without the flake, it defaults to a path is a "path value" such that isStorePath path == false, at least when using a local checkout. I think this is where it went wrong? Or maybe I was holding it wrong again.

I've pushed a commit. It's really kind of a flake-only optimization now, unless someone cares to set config.path.

@pennae
Copy link
Contributor

pennae commented Jan 5, 2022

yes, that was without flakes, we ran nix-instantiate -vvvvv /nix/store/mf2c1hpfds2fv501d1jc6pxc07qym974-nixpkgs/nixos -A config.system.build.manual, where the store path in there was a freshly added checkout of your branch. hadn't even checked the flake case because we remembered the non-flake case would suffer.

checked the flake case with your additional commit now though, and we still see a lot of copying happening:

{
  "/nix/store/6gjsp6s79qggw0sz9lnbl2bin8lhd6pq-lazy-options.json.drv": {
    "outputs": {
      "out": {
        "path": "/nix/store/za8h4fvkrnvyyrsf4936scfwq6afmad0-lazy-options.json"
      }
    },
    "inputSrcs": [
      "/nix/store/6b1v21x04fxr6w84nl8424xd2x6r304g-lib",
      "/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh",
      "/nix/store/an5jjcrjay229b0kfckyza3dnfif0kzh-nixos",
      "/nix/store/ns935b2vvx5i7hwmlz8c8bdxz1frhhxm-pkgs-lib"
    ],

not sure there's a good way to solve this if builtins.storePath isn't available from pure evaluations. the best we can think of right now is to inject a differentiator from the flake: if it's not present use storePath, if it is present pull lazy-options.json from a nixpkgs flake output that contains exactly the derivation required to build lazy-options

@roberth
Copy link
Member Author

roberth commented Jan 5, 2022

"inputSrcs": [
      "/nix/store/6b1v21x04fxr6w84nl8424xd2x6r304g-lib",
      "/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh",
      "/nix/store/an5jjcrjay229b0kfckyza3dnfif0kzh-nixos",
      "/nix/store/ns935b2vvx5i7hwmlz8c8bdxz1frhhxm-pkgs-lib"
    ],

This looks as expected for a non-flake eval. Without builtins.storePath we can't do better.

the best we can think of right now is to inject a differentiator from the flake

That's kind of what's happening after my last commit here, except it errs on the safe side, rather than assuming pure eval is off for non-flake usage.

It's not as good as we hoped, but at least it doesn't break and is more efficient for flake users.

@pennae
Copy link
Contributor

pennae commented Jan 5, 2022

This looks as expected for a non-flake eval.

that was a flake eval though 🙁

@pennae
Copy link
Contributor

pennae commented Jan 5, 2022

test operations done to get to that conclusion:

nix show-derivation $(nix eval --raw .\#nixosConfigurations.$(hostname).config.system.build.manual.optionsJSON --override-input nixpkgs github:hercules-ci/nixpkgs/flake-avoid-copying-nixpkgs-path)
# find the layz-options.json drv
nix show-derivation $that_drv # here: /nix/store/zga6r1jkxbrklp25s90x8iz1nyxb96qw-lazy-options.json.drv

we've confirmed that the eval did use your repo, our current flake lock for the test system does not include the docs split yet

@roberth
Copy link
Member Author

roberth commented Jan 5, 2022

pkgs isn't a legacyPackages. The latest commit sets config.path in NixOS-initialized Nixpkgs.

Copy link
Contributor

@pennae pennae left a comment

Choose a reason for hiding this comment

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

works as intended now 😸 flakes use the existing store path, non-flakes filter parts over.

we've tried a bit whether we can tryEval (storePath ...), but that's not possible. what we can do though is getEnv "_" != "" and use storePath if so, because getEnv will not fail in pure or restricted mode but simply return an empty string. we couldn't falsely detect that storePath is available, only that it isn't. do you think it's worth going for that too?

@pennae
Copy link
Contributor

pennae commented Jan 22, 2022

we finally got around to checking whether getEnv really is viable for this, and it is! 🙂 since PATH should always be set to something getEnv "PATH" != "" is a pretty good approximation of a hypothetical isImpureEval, so we can use storePath to avoid copies as well. here's a patch, we would've added it to the branch (and attempted to rebase) but that seems to be disallowed for this PR.

diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix
index 2ecdf68293e..b850d5f9ab5 100644
--- a/nixos/modules/misc/documentation.nix
+++ b/nixos/modules/misc/documentation.nix
@@ -74,13 +74,18 @@ let
         #
         # We can only avoid copying when pkgs.path is already a string. A path
         # value can not be converted to a store path without rehashing it.
-        # builtins.storePath would be a solution but is currently off-limits
-        # because of https://github.com/NixOS/nix/issues/1888
-        #        and https://github.com/NixOS/nix/issues/5868
-        pull = dir:
-          if builtins.typeOf pkgs.path == "string" && isStorePath pkgs.path
-          then "${pkgs.path}/${dir}"
-          else filter "${toString pkgs.path}/${dir}";
+        # builtins.storePath can only be used to avoid this when impure eval is
+        # allowed, which we approximate by getEnv returning something for a
+        # variable that should always be set.
+        # see https://github.com/NixOS/nix/issues/1888
+        # and https://github.com/NixOS/nix/issues/5868
+        pull =
+          if builtins.typeOf pkgs.path == "string" && isStorePath pkgs.path then
+            dir: "${pkgs.path}/${dir}"
+          else if builtins.getEnv "PATH" != "" && isStorePath pkgs.path then
+            dir: "${builtins.storePath pkgs.path}/${dir}"
+          else
+            dir: filter "${toString pkgs.path}/${dir}";
       in
         pkgs.runCommand "lazy-options.json" {
           libPath = pull "lib";

@roberth roberth force-pushed the flake-avoid-copying-nixpkgs-path branch from dbc837a to c1349dd Compare January 22, 2022 15:43
@roberth roberth force-pushed the flake-avoid-copying-nixpkgs-path branch from c1349dd to 5d29853 Compare January 22, 2022 15:49
@pennae
Copy link
Contributor

pennae commented Jan 22, 2022

we like this a lot. thanks for caring and for writing the extremely comprehensive comments, they're superb :)

@pennae
Copy link
Contributor

pennae commented Jan 22, 2022

tested it again, the copy elision seems to work fine, resulting json also looks good.

@pennae pennae merged commit 06a70d2 into NixOS:master Jan 22, 2022
@edolstra
Copy link
Member

Note that this makes nix-env produce a different evaluation result for a nixpkgs tree inside the Nix store compared to one outside of the Nix store, because the nixos-install-tools attribute will be different. That's an undesirable property IMHO (see #156527 and #156587).

In general we should avoid the use of isStorePath on sources since they cause divergent evaluation results. Perhaps we should ensure that builtins.isStorePath is false for inputs to the evaluator, even when the inputs happen to be in the store.

@roberth
Copy link
Member Author

roberth commented Jan 24, 2022

I agree that this is still not a great solution. Indeed this will produce one of two different derivations depending on how the NixOS/Nixpkgs invocation was made. Perhaps the filterSource based invocation shouldn't be automatic? That will make the drvs reproducible again.

@roberth
Copy link
Member Author

roberth commented Jan 27, 2022

I've reverted this PR, as it makes already fragile code like the installer tests even more fragile.

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

Labels

6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (update) This PR changes an existing module in `nixos/` 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants