diff --git a/src/libstore-tests/binary-cache-store.cc b/src/libstore-tests/binary-cache-store.cc new file mode 100644 index 00000000000..17bfd94ece9 --- /dev/null +++ b/src/libstore-tests/binary-cache-store.cc @@ -0,0 +1,92 @@ +#include + +#include "nix/store/binary-cache-store.hh" +#include "nix/store/globals.hh" + +namespace nix { + +struct TestBinaryCacheStoreConfig : virtual Store::Config, BinaryCacheStoreConfig +{ + using Params = StoreReference::Params; + + TestBinaryCacheStoreConfig(const Params & params) + : Store::Config(params) + , BinaryCacheStoreConfig(params) + { + } + + ref openStore() const override + { + throw Unsupported("openStore"); + } +}; + +struct TestBinaryCacheStore : virtual BinaryCacheStore +{ + using Config = TestBinaryCacheStoreConfig; + + ref config; + std::atomic fileExistsCalls = 0; + std::atomic getFileCalls = 0; + std::set existingFiles; + + TestBinaryCacheStore(ref config) + : Store{*config} + , BinaryCacheStore{*config} + , config(config) + { + } + + std::optional isTrustedClient() override + { + return std::nullopt; + } + + bool fileExists(const std::string & path) override + { + fileExistsCalls++; + return existingFiles.contains(path); + } + + void upsertFile( + const std::string & path, RestartableSource & source, const std::string & mimeType, uint64_t sizeHint) override + { + throw Unsupported("upsertFile"); + } + + void getFile(const std::string & path, Sink & sink) override + { + getFileCalls++; + throw NoSuchBinaryCacheFile("file '%s' does not exist in binary cache", path); + } +}; + +TEST(BinaryCacheStore, queryValidPathsUsesExistenceChecks) +{ + initLibStore(false); + + auto config = make_ref(StoreReference::Params{}); + auto store = std::make_shared(config); + + StorePathSet paths{ + StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"}, + StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3r-bar"}, + StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3s-baz"}, + }; + + store->existingFiles = { + "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q.narinfo", + "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3s.narinfo", + }; + + auto valid = store->queryValidPaths(paths); + + EXPECT_EQ(valid.size(), 2u); + EXPECT_EQ(valid.count(StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"}), 1u); + EXPECT_EQ(valid.count(StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3s-baz"}), 1u); + + EXPECT_EQ(store->fileExistsCalls, paths.size()); + EXPECT_EQ(store->getFileCalls, 0u); +} + +} // namespace nix diff --git a/src/libstore-tests/meson.build b/src/libstore-tests/meson.build index 2d12b14d3cb..76583d0919c 100644 --- a/src/libstore-tests/meson.build +++ b/src/libstore-tests/meson.build @@ -54,6 +54,7 @@ deps_private += gtest subdir('nix-meson-build-support/common') sources = files( + 'binary-cache-store.cc', 'build-result.cc', 'common-protocol.cc', 'content-address.cc', diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 848669ae84f..ae5a88c1530 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -8,6 +8,7 @@ #include "nix/util/sync.hh" #include "nix/store/remote-fs-accessor.hh" #include "nix/store/nar-info-disk-cache.hh" +#include "nix/store/filetransfer.hh" #include "nix/util/nar-accessor.hh" #include "nix/util/thread-pool.hh" #include "nix/util/callback.hh" @@ -383,6 +384,61 @@ StorePath BinaryCacheStore::addToStoreFromDump( ->path; } +StorePathSet BinaryCacheStore::queryValidPaths(const StorePathSet & paths, SubstituteFlag maybeSubstitute) +{ + struct State + { + size_t left; + StorePathSet valid; + std::exception_ptr exc; + }; + + Sync state_(State{paths.size(), StorePathSet()}); + + std::condition_variable wakeup; + ThreadPool pool(fileTransferSettings.httpConnections); + + auto doQuery = [&](const StorePath & path) { + checkInterrupt(); + + bool exists = false; + std::exception_ptr newExc{}; + + try { + exists = isValidPath(path); + } catch (...) { + newExc = std::current_exception(); + } + + auto state(state_.lock()); + + if (exists) + state->valid.insert(path); + + if (newExc) + state->exc = newExc; + + assert(state->left); + if (!--state->left) + wakeup.notify_one(); + }; + + for (auto & path : paths) + pool.enqueue(std::bind(doQuery, path)); + + pool.process(); + + while (true) { + auto state(state_.lock()); + if (!state->left) { + if (state->exc) + std::rethrow_exception(state->exc); + return std::move(state->valid); + } + state.wait(wakeup); + } +} + bool BinaryCacheStore::isValidPathUncached(const StorePath & storePath) { // FIXME: this only checks whether a .narinfo with a matching hash diff --git a/src/libstore/include/nix/store/binary-cache-store.hh b/src/libstore/include/nix/store/binary-cache-store.hh index e7b3d07ebb6..da53190d28e 100644 --- a/src/libstore/include/nix/store/binary-cache-store.hh +++ b/src/libstore/include/nix/store/binary-cache-store.hh @@ -168,6 +168,8 @@ public: bool isValidPathUncached(const StorePath & path) override; + StorePathSet queryValidPaths(const StorePathSet & paths, SubstituteFlag maybeSubstitute = NoSubstitute) override; + void queryPathInfoUncached( const StorePath & path, Callback> callback) noexcept override;