Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

derivationStrict #554

Merged
merged 9 commits into from
Jan 1, 2021
Merged

Conversation

layus
Copy link
Collaborator

@layus layus commented May 13, 2020

Implement derivationStrict primOp

Closes #364

@jwiegley jwiegley requested review from ryantrinkle and kmicklas and removed request for ryantrinkle May 16, 2020 00:47
@jwiegley
Copy link
Member

This looks like great work, @kmicklas, I wonder if @shlevy and @ryantrinkle might have some time to take a look. If not, we can merge later next week and fix whatever is left to be fixed.

@kmicklas
Copy link
Collaborator

I think you meant to tag @layus 😄

@jwiegley
Copy link
Member

Oh, right, thank you @kmicklas !

Right pathName -> do
-- todo: pass the hash explicitly
-- TODO: Handle the filter
res <- Store.runStore $ Store.addToStore pathName path recursive (Proxy :: Proxy Store.SHA256) (const False) repair
Copy link
Member

Choose a reason for hiding this comment

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

This also changed type to omit proxy so you can just do Store.addToStore @'Store.SHA256 pathName ..

@sorki
Copy link
Member

sorki commented May 19, 2020

haskell-nix/hnix-store#59 now also contains parser for Derivations but needs Gabriella439/Haskell-Nix-Derivation-Library#8

There's also support for building derivations now if you want to give it a try.

@layus
Copy link
Collaborator Author

layus commented May 21, 2020

Build failure due to the dependencies on newer versions of the hnix-store... We should realy integrate everything in one single repo.

@sorki
Copy link
Member

sorki commented May 21, 2020

Now buildable from hnix-overlay with GHC 8.10 using merged branches - sorki/hnix-overlay@4f3454b

Seems to work but eats all my ram 😄

Be careful as this does access real system store via daemon now (tests do as well).

@layus
Copy link
Collaborator Author

layus commented May 22, 2020

This PR is very inefficient. We need a way to cache hashDerivationModulo results. Otherwise we are doomed with an exponential computing time. It means that in practice computing nixpkgs.hello.drvPath does not complete in reasonable time on my machine.

It may also be related to the memory consumption issue a we load several times all the derivations from memory, parse everything and compute hashes. enforcing some strictness may also help.

From my point of view, this state should reside in the store itself. We should considr moving some of the code to hnix-store.

@Anton-Latukha
Copy link
Collaborator

@layus Above message may decerve to be an idea, so it is good to preseve it in Report.

@Anton-Latukha Anton-Latukha added the challenging May require somewhat complex changes; not recommend if you aren't familiar with the codebase label May 25, 2020
@layus layus force-pushed the derivationStrict branch 2 times, most recently from 33815b6 to d38db1c Compare October 27, 2020 03:20
@layus
Copy link
Collaborator Author

layus commented Oct 27, 2020

Hi, I think this has reached a mergeable state. 🎉 🎉 🎉

The main issue with exponential memory usage (in the size of the closure) is now addressed by adding a cache for hashDerivationModulo results, next to the cache of parsed nix files (see last commit). The bulk of the implementation resides in the second to last commit. Lots of code, but standalone for the most part.
The rest of the commits are refactoring, fixes and changes in various locations. Some of these changes are not strictly needed for this PR. If they are deemed undesirable, I can of course redact them.

There are no tests, but nixpkgs.hello gives the same hash with nix and hnix. Achieving this was quite a feat 🎉 and requires the fixes to hnix-store that are idling in haskell-nix/hnix-store#64. Also, tracking down the issue with the substring builtin was quite a pain.

Anyway, I think this is quite an achievement as it bridges the gap between hnix an nix. We can now correctly evaluate derivations ! 🥇

/cc @jwiegley, @shlevy, @ryantrinkle Would you mind having a look at this PR ?

@layus
Copy link
Collaborator Author

layus commented Oct 27, 2020

Pending questions to be resolved later on, in other PRs:

  • Consider moving hashDerivationModulo to hnix-store. It would also require to move the cache there.
  • Decide which store we want by default in hnix (readonly, standalone, daemon).
  • Implement proper back-and-forth communication between MonadStore (hnix-store) and MonadNix++ (hnix). @imalsogreg made a great job of tossing monads around for streaming nars in Stream to and from NAR format hnix-store#61. Maybe he could give some advice here too.
    • Implement a proper path filter based on a nix function.
    • Keep a single daemon socket for the whole hnix lifetime. Currently we open a new one on each store operation.
    • I am more and more considering a handle-based scheme for the hnix-store API.

@jwiegley
Copy link
Member

It's a fair bit going on, but I like it. What do you think @ryantrinkle?

@Ericson2314
Copy link
Member

Ericson2314 commented Oct 27, 2020

Pending questions to be resolved later on, in other PRs

These things do further my suspicion that it's time to move to a monorepo so it's easier to experiment without how to integrate the various libraries while keeping things modular.

@jwiegley
Copy link
Member

jwiegley commented Oct 27, 2020

I'm generally a fan of monorepos.

@expipiplus1 expipiplus1 mentioned this pull request Nov 1, 2020
@layus
Copy link
Collaborator Author

layus commented Nov 1, 2020

Short update about performance:

$ NIX_DATA_DIR=$PWD/hnix/data command time ./dist-newstyle/build/x86_64-linux/ghc-8.10.1/hnix-0.10.1/x/hnix/build/hnix/hnix --eval -E '"${(import  { }).hello}"'
"/nix/store/w9yy7v61ipb5rx6i35zq1mvc2iqfmps1-hello-2.10"
22.21user 0.67system 0:22.95elapsed 99%CPU (0avgtext+0avgdata 1615464maxresident)k
0inputs+0outputs (0major+400263minor)pagefaults 0swaps
$ command time nix-instantiate --eval -E '"${(import  {}).hello}"'                      (nix)
"/nix/store/w9yy7v61ipb5rx6i35zq1mvc2iqfmps1-hello-2.10"
0.22user 0.03system 0:00.25elapsed 100%CPU (0avgtext+0avgdata 111252maxresident)k
0inputs+0outputs (0major+25171minor)pagefaults 0swaps

22 each time :-). And yet 100 times slower. Good news is that we have some room for improvement 😂

@jwiegley
Copy link
Member

jwiegley commented Nov 1, 2020

@layus Parsing and evaluating are each still very slow.

@layus
Copy link
Collaborator Author

layus commented Nov 1, 2020

@jwiegley Any known culprit for that ?

M  default.nix
M  hnix.cabal
M  src/Nix/Effects/Derivation.hs

default.nix: fx overlay call
@Anton-Latukha
Copy link
Collaborator

Anton-Latukha commented Dec 31, 2020

Reshuffled the history, cleaned it up, and minimized transitions as much as I could.

Figured-out and fixed a couple of things in config changes.

Now, it looks strangely familiar:


On macOS compilations fails due to non-existent IPC calls

command was: /nix/store/0nlwbj5qbdf7r58x2iqz0a17az2j5fdn-clang-wrapper-7.1.0/bin/clang -c dist/build/System/Linux/Namespaces_hsc_make.c -o dist/build/System/Linux/Namespaces_hsc_make.o -D__GLASGOW_HASKELL__=810 -Ddarwin_BUILD_OS=1 -Dx86_64_BUILD_ARCH=1 -Ddarwin_HOST_OS=1 -Dx86_64_HOST_ARCH=1 -I/nix/store/0jj05cyma647rywsqpq3darnzl6a520s-libc++-7.1.0/include -I/nix/store/ns7f13gldnk83l2fgjlzbx9bnfvc2g72-libc++abi-7.1.0/include -I/nix/store/6snmf1z43ay6m1b8jv8dr4h8im056a62-compiler-rt-7.1.0-dev/include -I/nix/store/q8xcp8hsa35wjki8mad0sbk4nzqrdkb5-libiconv-osx-10.12.6/include -I/nix/store/0jj05cyma647rywsqpq3darnzl6a520s-libc++-7.1.0/include -I/nix/store/ns7f13gldnk83l2fgjlzbx9bnfvc2g72-libc++abi-7.1.0/include -I/nix/store/6snmf1z43ay6m1b8jv8dr4h8im056a62-compiler-rt-7.1.0-dev/include -I/nix/store/q8xcp8hsa35wjki8mad0sbk4nzqrdkb5-libiconv-osx-10.12.6/include -Idist/build/autogen -Idist/build/global-autogen -include dist/build/autogen/cabal_macros.h -I/nix/store/lj34zl1kfjd2556aslrn8hsyn0ng1hzb-ghc-8.10.3/lib/ghc-8.10.3/unix-2.7.2.2/include -I/nix/store/lj34zl1kfjd2556aslrn8hsyn0ng1hzb-ghc-8.10.3/lib/ghc-8.10.3/time-1.9.3/include -I/nix/store/lj34zl1kfjd2556aslrn8hsyn0ng1hzb-ghc-8.10.3/lib/ghc-8.10.3/bytestring-0.10.12.0/include -I/nix/store/q8xcp8hsa35wjki8mad0sbk4nzqrdkb5-libiconv-osx-10.12.6/include -I/nix/store/lj34zl1kfjd2556aslrn8hsyn0ng1hzb-ghc-8.10.3/lib/ghc-8.10.3/base-4.14.1.0/include -I/nix/store/9m9q7bmn937d15p8q9m26fpxypi8vg92-gmp-6.2.1-dev/include -I/nix/store/lj34zl1kfjd2556aslrn8hsyn0ng1hzb-ghc-8.10.3/lib/ghc-8.10.3/integer-gmp-1.0.3.0/include -I/nix/store/lj34zl1kfjd2556aslrn8hsyn0ng1hzb-ghc-8.10.3/lib/ghc-8.10.3/include -I/nix/store/jikzfsnc21fsfzn991md0vi8j14p02sm-libffi-3.3-dev/include -I/nix/store/lj34zl1kfjd2556aslrn8hsyn0ng1hzb-ghc-8.10.3/lib/ghc-8.10.3/include/
error: Namespaces.hsc:79:16: error: use of undeclared identifier 'CLONE_NEWIPC'
    hsc_const (CLONE_NEWIPC);
               ^
Namespaces.hsc:79:16: error: use of undeclared identifier 'CLONE_NEWIPC'
Namespaces.hsc:79:16: error: use of undeclared identifier 'CLONE_NEWIPC'
Namespaces.hsc:80:16: error: use of undeclared identifier 'CLONE_NEWNET'

In Nix builds fail because they use network access in tests

  Eval comparison tests
    builtins.toJSON.nix:                                             FAIL
      Exception: Network.Socket.connect: <socket: 13>: does not exist (No such file or directory)
    builtins.lessThan-01.nix:                                        FAIL
      Exception: Network.Socket.connect: <socket: 14>: does not exist (No such file or directory)
    builtins.getContext.nix:                                         FAIL
      Exception: Network.Socket.connect: <socket: 13>: does not exist (No such file or directory)
    builtins.replaceStrings-01.nix:                                  FAIL
      Exception: Network.Socket.connect: <socket: 13>: does not exist (No such file or directory)
      
  Nix (upstream) language tests
    eval okay
      eval-okay-eq-derivations:                                      FAIL
        Exception: Network.Socket.connect: <socket: 13>: does not exist (No such file or directory)
      eval-okay-context:                                             FAIL
        Exception: Network.Socket.connect: <socket: 13>: does not exist (No such file or directory)

On GHC 8.10 GitHub decided no matter what PR is - to take a New Year break after 10 minutes of work

10 minutes...

Error: The operation was canceled.

And what really rings true is the Cabal build

   Eval comparison tests
    nixpkgs.hello.nix:                                               FAIL
      Exception: /nix/store/q5xckc93ra0fahpj6z01ffrxj3pa0qa8-source: openBinaryFile: does not exist (No such file or directory)

What would you do - every build is familiar in its own way. But only the last one is, as always, being true. If GitHub was not lazy today - the GHC 8.10 build would've told us the exact same story (since it tells it locally).

It compiles, but fails on that little test.

Test group defined as:

genEvalCompareTests = do
    td <- D.listDirectory testDir

    let unmaskedFiles = filter ((==".nix") . takeExtension) td
    let files = unmaskedFiles \\ maskedFiles

    pure $ testGroup "Eval comparison tests" $ map (mkTestCase testDir) files
  where
    mkTestCase td f = testCase f $ assertEvalFileMatchesNix (td </> f)

What we essentially have is a 1 test to fix. And that is a test introduced here: nixpkgs.hello.nix(open):

/*
FIXME: TODO: This is a bad test because:
1. We may not have access to the network in test environments (e.g. Nix :-))
2. fetchTarball is not yet implemented in hnix and call the real nix
3. We want all the tests that rely on nixpkgs to use the same fetched path
4. We want a single place to update the nixpkgs revision we test.
5. Currently hnix tests are not sandboxed as the ones in hnix-store-remote,
so all the .drv we build en up in the host store.
XXX: NIXPKGS_TESTS are broken for now. Fix that too when fixing this.
*/
let
nixpkgs = builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/1dc37370c489b610f8b91d7fdd40633163ffbafd.tar.gz";
};
in
with import nixpkgs {};
"${pkgs.hello.drvPath}"

I would think and also wait on thoughts on what is the thing with this test.

@Anton-Latukha
Copy link
Collaborator

This happens because:

fetch 'nix/fetchurl.nix` was not found in the Nix search path

Dolstra recently moves/reduces the helper .nix files.
Recently afair he reduced the derivation.nix, which we internally use.
In derivation it now lives in the ./share/nix/corepkgs/fetchurl.nix.

@Anton-Latukha
Copy link
Collaborator

It is somewhere between functions fetchTarball, it's fetch and instantiateExpr.

@Anton-Latukha
Copy link
Collaborator

Anton-Latukha commented Dec 31, 2020

Why and how it looks into it.
corepkgs/fetchurl.nix needs inclusion into cabal file.
...
Maybe it is foldNixPath.

@layus
Copy link
Collaborator Author

layus commented Dec 31, 2020

That's a lot of small issues bundled together. Let's try them one at a time:


fetch 'nix/fetchurl.nix` was not found in the Nix search path comes is an issue I am unsure how to solve. I ran all my tests with
NIX_DATA_DIR=/path/to/hnix/data env var. The tests expect to have it in the same location. (See https://github.com/layus/hnix/blob/2ec374d19e4a4090abed5513faf6ad6644f0ff8b/tests/Main.hs#L99 ). If it fails, it may be because we do not bundle these files. Or something else. Not quite sure.

For this PR, we need to bundle the files correctly so that hnix invocations can find them by default in the hnix installation.

As for the

Dolstra recently moves/reduces the helper .nix files.
Recently afair he reduced the derivation.nix, which we internally use.
In derivation it now lives in the ./share/nix/corepkgs/fetchurl.nix.

This can wait for a future PR. It is not part of the nix interface. It's implementation specific. We could also decide to bundle it in a single value just like we do for builtins.derivation


In nix, the build fails because we need network access. That was already the case before. Worth to be fixed, but not necessarily here.

But they also fail because the need a working nix daemon. In hnix-store @sorki make a great work at both i) hiding these tests behind a flag and ii) wrapping everything in namespaces to provide a sandboxed nix daemon.

We could import either. And possibly both. So we would have flags tho disable tests that require networking, and provide a sandboxed nix daemon for tests.

It is a bit sad that the current implementation relies on the remote version of the hnix-store. a read-only version would fit the tests better. But it does not exist yet. Among other things because it would require some internal state that needs some architectural decisions about where/how to keep it. So for now we need the real nix daemon somewhere.


I guess that already some stuff to grind :-)

@layus
Copy link
Collaborator Author

layus commented Dec 31, 2020

As for <nix/fetchurl.nix> it is called from nixpkgs @ https://github.com/NixOS/nixpkgs/blob/1dc37370c489b610f8b91d7fdd40633163ffbafd/pkgs/build-support/fetchurl/boot.nix#L11

The link references the same nixpkgs commit as the one we fetch with fetchTarball.

The solution is a proper NIX_DATA_DIR being set because that gets used as the <nix/...> prefix in the default nix path: https://github.com/layus/hnix/blob/2ec374d19e4a4090abed5513faf6ad6644f0ff8b/src/Nix/Builtins.hs#L313-L326

@layus
Copy link
Collaborator Author

layus commented Dec 31, 2020

Or rather a preperly filled getDataDir which is generated automatically in Paths_hnix.hs and gets filled by Data-Files: cabal directive.

@layus
Copy link
Collaborator Author

layus commented Dec 31, 2020

builtins.fetchTarball is broken because we have https://github.com/layus/hnix/blob/77859172494967a9de6292b4021844efe3b547ab/tests/Main.hs#L98 which downloads to $PWD while still printing a path in /nix/store/... . I wonder if it is a new nix feature. local?root= seems a hack that could be bo more supported in new nix builds.

@layus
Copy link
Collaborator Author

layus commented Dec 31, 2020

Indeed. local?root=... is now local?store=... 🐪

@Anton-Latukha
Copy link
Collaborator

Anton-Latukha commented Dec 31, 2020

Great read. I never thought that it might be the Nixpkgs call of the file, automatically assumed it is or Haskell code or nix-instantiate invocations, so dug there.

I wanted to propose simple separation of consciences: cherry-pick the test into the separate PR and merge current. And deal with fixing the envocation in the test separately.

@bqv
Copy link

bqv commented Dec 31, 2020

As for the

Dolstra recently moves/reduces the helper .nix files.
Recently afair he reduced the derivation.nix, which we internally use.
In derivation it now lives in the ./share/nix/corepkgs/fetchurl.nix.

This can wait for a future PR. It is not part of the nix interface. It's implementation specific. We could also decide to bundle it in a single value just like we do for builtins.derivation

If they're needed, and it's not going to leave this PR in purgatory for another seven months, I think this is the most sensible option

@layus
Copy link
Collaborator Author

layus commented Dec 31, 2020

I wanted to propose simple separation of consciences: cherry-pick the test into the separate PR and merge current. And deal with fixing the invocation in the test separately.

Ok, let's go with that. HNix does not yet support chroot stores, but runs the tests with it. It worked while we were only using the real nix for real store operations. Now it breaks.

layus added a commit to layus/hnix that referenced this pull request Dec 31, 2020
This reverts commit ac7450a for the
test it contains requires support for chroot stores in HNix, and it is
not yet implemented.
See comments starting at haskell-nix#554 (comment)
for details.
@bqv
Copy link

bqv commented Dec 31, 2020

Hooray, we might be merged before the new year!

Happy holidays, friends.

@layus
Copy link
Collaborator Author

layus commented Dec 31, 2020

@Anton-Latukha I think these two new changes are enough, even if not entirely satisfactory. It would be fun to merge this in late 2020. But getting the first merge of 2021 would be an achievement too.

I would appreciate if you could merge it yourself after running the checks you consider necessary :-D.

Cheers ! And happy new year :-)

@Anton-Latukha
Copy link
Collaborator

Yep, I'd already cleaned-up and merged it as #554 (comment) when I wrote it, but I actually wrote that comment from the TERM==linux, I had a hard a severe rare case of unsolvable by me hardware problem because of Linux Kerne team doing the right things with NVidia's "GPL condom" and those overall. I made test&code assessment in the Linux console, Emacs is a terminal OS, that saves the ball every time.
Now everything works fine, just a minute.

@Anton-Latukha
Copy link
Collaborator

I formed that test into a separate PR and dropped its revert here.

Cabal builds already worked - which is a minimal and enough requirement for the PR merges for me. There is no sense in breaking the master for everyone because some of us want to merge the patch that can not under the most basic conditions.


I am not an authority, but I recommend to think about our pathways - all our work and infrastructure, Nix & Nixpkgs Haskell, Stack also - are still, in the end, depend and use Hackage and its Cabal descriptions, and so - our Hackage community releases which are Cabal builds and descriptions. That is why it is the main thing for me - to keep Cabal builds working. On top of it - that keeps the history and the code open for any person in the community, it is a minimal requirement and enough to do Haskell work.

If I ever merge my or other PRs that had the Cabal builds broken but I merged them - you can beat my hands with a bamboo stick.


I wish one day to be as productive as you, @layus

@Anton-Latukha Anton-Latukha merged commit 8a6ff07 into haskell-nix:master Jan 1, 2021
@bqv
Copy link

bqv commented Jan 1, 2021

Thank you all!

@Anton-Latukha
Copy link
Collaborator

Anton-Latukha commented Jan 2, 2021

That is not all, do not put a cart before a horse.

We have a big API. And a big API change.
For the release and to respect people that use HNix - the changelog needs to be populated first-class. Putting in the short human form the cliffnotes to guide fellows what happened from what they may need to be guided in a human for to update to new HNix. There is a big list of changes to be parsed and simply explained in the changelog. It is late and I would not finish it today.
If our API is as big and open - we would need to do this guidance all the time for every function we add, and for any function type change - that is additionally also a major release breakage level. Since almost all of them are made public - so we must support what we provide and promise to support to the public.
That is also why I want for us to do an API clean at some point. We need to expose only what downstream needs, and put a disclaimer that if something is additionally needed - for people to open reports or PRs opening it, and issuing those minor releases fast.
The changelog effort and a number of breaking major releases are linear to the API size of the project.
Changelog is populated in: #788

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
challenging May require somewhat complex changes; not recommend if you aren't familiar with the codebase
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement derivationStrict without nix-instantiate
8 participants