1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-09 03:56:01 +01:00

Avoid isValidPath(), use queryPathInfo() instead

Since recently we call isValidPath() a lot from the evaluator,
specifically from
LocalStoreAccessor::requireStoreObject(). Unfortunately, isValidPath()
uses but does not populate the in-memory path info cache; only
queryPathInfo() does that. So isValidPath() is fast *if* we happened
to call queryPathInfo() on the same path previously. This is not the
case when lazy-trees is enabled, so we got a lot of superfluous,
high-latency calls to the daemon (which show up in verbose output as
`performing daemon worker op: 1`).

Similarly, `fetchToStore()` called `isValidPath()` as well.

The fix is to use `queryPathInfo()`, which for one particular eval
reduced the number of daemon calls from 15246 to 2324. This may cause
Nix to fetch some unnecessary information from the daemon, but that
probably doesn't matter much given the high latency.
This commit is contained in:
Eelco Dolstra 2025-07-21 19:48:20 +02:00
parent 47281531ec
commit 19f89eb684
4 changed files with 32 additions and 4 deletions

View file

@ -52,7 +52,7 @@ std::pair<StorePath, Hash> fetchToStore2(
auto hash = Hash::parseSRI(fetchers::getStrAttr(*res, "hash"));
auto storePath = store.makeFixedOutputPathFromCA(name,
ContentAddressWithReferences::fromParts(method, hash, {}));
if (mode == FetchMode::DryRun || store.isValidPath(storePath)) {
if (mode == FetchMode::DryRun || store.maybeQueryPathInfo(storePath)) {
debug("source path '%s' cache hit in '%s' (hash '%s')", path, store.printStorePath(storePath), hash.to_string(HashFormat::SRI, true));
return {storePath, hash};
}

View file

@ -269,7 +269,9 @@ public:
StorePath followLinksToStorePath(std::string_view path) const;
/**
* Check whether a path is valid.
* Check whether a path is valid. NOTE: this function does not
* generally cache whether a path is valid. You may want to use
* `maybeQueryPathInfo()`, which does cache.
*/
bool isValidPath(const StorePath & path);
@ -308,10 +310,17 @@ public:
/**
* Query information about a valid path. It is permitted to omit
* the name part of the store path.
* the name part of the store path. Throws an exception if the
* path is not valid.
*/
ref<const ValidPathInfo> queryPathInfo(const StorePath & path);
/**
* Like `queryPathInfo()`, but returns `nullptr` if the path is
* not valid.
*/
std::shared_ptr<const ValidPathInfo> maybeQueryPathInfo(const StorePath & path);
/**
* Asynchronous version of queryPathInfo().
*/

View file

@ -44,7 +44,7 @@ struct LocalStoreAccessor : PosixSourceAccessor
void requireStoreObject(const CanonPath & path)
{
auto [storePath, rest] = store->toStorePath(store->storeDir + path.abs());
if (requireValidPath && !store->isValidPath(storePath))
if (requireValidPath && !store->maybeQueryPathInfo(storePath))
throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
}

View file

@ -633,6 +633,25 @@ ref<const ValidPathInfo> Store::queryPathInfo(const StorePath & storePath)
}
std::shared_ptr<const ValidPathInfo> Store::maybeQueryPathInfo(const StorePath & storePath)
{
std::promise<std::shared_ptr<const ValidPathInfo>> promise;
queryPathInfo(storePath,
{[&](std::future<ref<const ValidPathInfo>> result) {
try {
promise.set_value(result.get());
} catch (InvalidPath &) {
promise.set_value(nullptr);
} catch (...) {
promise.set_exception(std::current_exception());
}
}});
return promise.get_future().get();
}
static bool goodStorePath(const StorePath & expected, const StorePath & actual)
{
return