Skip to content

Conversation

@edolstra
Copy link
Member

Motivation

fetchToStore() caching is broken because it uses the fingerprint of the accessor, but now that the accessor (typically storeFS) is a composite (like MountedSourceAccessor or AllowListSourceAccessor), there is no fingerprint anymore.

So fetchToStore() now uses the new getFingerprint() method to get the specific fingerprint for the subpath. We now also mount inputs on top of storeFS to make their fingerprints visible to fetchToStore().

To prevent future regressions like this, this PR also adds tests to make sure that most paths are cacheable.

Context

Mostly cherry-picked from DetSys Nix and #13225.


Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

@github-actions github-actions bot added new-cli Relating to the "nix" command with-tests Issues related to testing. PRs with tests have some priority fetching Networking with the outside (non-Nix) world, input locking labels Sep 22, 2025
Comment on lines 7 to 15
struct MountedSourceAccessor : SourceAccessor
{
virtual void mount(CanonPath mountPoint, ref<SourceAccessor> accessor) = 0;

/**
* Return the accessor mounted on `mountPoint`, or `nullptr` if
* there is no such mount point.
*/
virtual std::shared_ptr<SourceAccessor> getMount(CanonPath mountPoint) = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is great. Like this exactly what's needed for in-memory dummy store.

Copy link
Member

Choose a reason for hiding this comment

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

We'll need to use this with care. E.g. the path ${m}/foo != getMount m foo, so if both are observable in the language, that would be a problem. (And they are substantially different in terms of root and path components)

Copy link
Member Author

Choose a reason for hiding this comment

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

This shouldn't be a problem here since we don't have lazy trees yet. I.e. identical inputs are deterministically mounted on the same store paths.


if (originalInput.getNarHash() && narHash != *originalInput.getNarHash())
throw Error(
(unsigned int) 102,
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't there an enum for this?

Copy link
Member Author

Choose a reason for hiding this comment

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

Don't think so... There is Worker::failingExitStatus() which should probably be factored out.

Copy link
Member

Choose a reason for hiding this comment

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

I was meaning to clean that up and unify with BuildResult statuses at some point, fwiw.

@github-project-automation github-project-automation bot moved this to Triage in Nix team Sep 24, 2025
{
auto storePath = fetchToStore(fetchSettings, *store, accessor, FetchMode::Copy, input.getName());

allowPath(storePath); // FIXME: should just whitelist the entire virtual store
Copy link
Member

Choose a reason for hiding this comment

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

I would like this part done sooner rather than later

@Ericson2314
Copy link
Member

I took the "easy parts" of this and did some other cleanups and put in #14080.

Comment on lines 240 to 241
auto realStoreDir = dirOf(store->toRealPath(StorePath::dummy));
if (settings.pureEval || store->storeDir != realStoreDir) {
accessor = settings.pureEval ? storeFS : makeUnionSourceAccessor({accessor, storeFS});
}
accessor = settings.pureEval ? storeFS.cast<SourceAccessor>() : makeUnionSourceAccessor({accessor, storeFS});
Copy link
Member

@Ericson2314 Ericson2314 Sep 25, 2025

Choose a reason for hiding this comment

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

I don't think this is quite right --- evidently we need better unit tests to catch this.

#14080 removes redundant branches while keeping the store->storeDir != realStoreDir fix.

Copy link
Member Author

Choose a reason for hiding this comment

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

No, this change is necessary. Otherwise, if pureEval isn't enabled, rootFS ends up not containing storeFS, so the mounts on storeFS won't be visible in rootFS.

@Mic92
Copy link
Member

Mic92 commented Sep 25, 2025

@Ericson2314 now this one needs a rebase?

With FilteringSourceAccessor, lstat() needs to throw a different
exception if the path is inaccessible than if it doesn't exist.
This returns the fingerprint for a specific subpath. This is intended
for "composite" accessors like MountedSourceAccessor, where different
subdirectories can have different fingerprints.
@Ericson2314
Copy link
Member

I'll rebase it

fetchToStore() caching was broken because it uses the fingerprint of
the accessor, but now that the accessor (typically storeFS) is a
composite (like MountedSourceAccessor or AllowListSourceAccessor),
there was no fingerprint anymore. So fetchToStore now uses the new
getFingerprint() method to get the specific fingerprint for the
subpath.
This is to test the non-functional property that most paths should be
cacheable. We've had frequent cases where caching broken but we didn't
notice.
@Ericson2314 Ericson2314 force-pushed the fix-fetch-to-store-caching branch from 7618094 to 4b9735b Compare September 25, 2025 15:30
Comment on lines +3123 to +3127

// Backward compatibility hack: throw an exception if access
// to this path is not allowed.
if (auto accessor = res.accessor.dynamic_pointer_cast<FilteringSourceAccessor>())
accessor->checkAccess(res.path);
Copy link
Member

Choose a reason for hiding this comment

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

This stuff has been on lazy trees for a while I know, but I guess a this point I am skeptical it is still needed? Especially with #14081

Copy link
Member Author

Choose a reason for hiding this comment

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

No, they're still needed.

@Mic92 Mic92 requested a review from Ericson2314 September 26, 2025 18:45
@Ericson2314
Copy link
Member

Ericson2314 commented Sep 30, 2025

So my current plan (as of yesterday) is that after @xokdvium fixes some bugs, I think #14081 will pass tests, and then I hope we can do that, and then I think we will be better prepared to review this. I want avoid a mixture of mounting and filtering in the pure eval case because I think that is hard to reason about.

@edolstra
Copy link
Member Author

edolstra commented Oct 6, 2025

and then I think we will be better prepared to review this.

Let's please not block pretty important bug fixes on nice-to-have refactorings. Currently the fetchToStore cache is broken on master, which is a pretty major regression.

Copy link
Contributor

@xokdvium xokdvium left a comment

Choose a reason for hiding this comment

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

Looks good. Other nice-to-haves we can improve on in follow-ups

@edolstra edolstra merged commit 1e70955 into master Oct 6, 2025
32 checks passed
@edolstra edolstra deleted the fix-fetch-to-store-caching branch October 6, 2025 17:39
@github-project-automation github-project-automation bot moved this from Triage to Done in Nix team Oct 6, 2025
@xokdvium
Copy link
Contributor

This seems to have regressed: nix eval --expr "builtins.readDir /nix/store" --impure

nix eval --expr "builtins.readDir /nix/store" --impure
error:
       … while calling the 'readDir' builtin
         at «string»:1:1:
            1| builtins.readDir /nix/store
             | ^

       error: path '/nix/store/' is not in the Nix store

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/nix-2-32-0-released/70528/9

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

Labels

fetching Networking with the outside (non-Nix) world, input locking new-cli Relating to the "nix" command with-tests Issues related to testing. PRs with tests have some priority

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

6 participants