Move path info caching from BinaryCacheStore to Store
Caching path info is generally useful. For instance, it speeds up "nix path-info -rS /run/current-system" (i.e. showing the closure sizes of all paths in the closure of the current system) from 5.6s to 0.15s. This also eliminates some APIs like Store::queryDeriver() and Store::queryReferences().
This commit is contained in:
		
							parent
							
								
									608b0265e1
								
							
						
					
					
						commit
						e0204f8d46
					
				
					 21 changed files with 318 additions and 353 deletions
				
			
		|  | @ -70,8 +70,7 @@ int isValidPath(char * path) | ||||||
| SV * queryReferences(char * path) | SV * queryReferences(char * path) | ||||||
|     PPCODE: |     PPCODE: | ||||||
|         try { |         try { | ||||||
|             PathSet paths; |             PathSet paths = store()->queryPathInfo(path)->references; | ||||||
|             store()->queryReferences(path, paths); |  | ||||||
|             for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) |             for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) | ||||||
|                 XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0))); |                 XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0))); | ||||||
|         } catch (Error & e) { |         } catch (Error & e) { | ||||||
|  | @ -82,7 +81,7 @@ SV * queryReferences(char * path) | ||||||
| SV * queryPathHash(char * path) | SV * queryPathHash(char * path) | ||||||
|     PPCODE: |     PPCODE: | ||||||
|         try { |         try { | ||||||
|             Hash hash = store()->queryPathHash(path); |             auto hash = store()->queryPathInfo(path)->narHash; | ||||||
|             string s = "sha256:" + printHash32(hash); |             string s = "sha256:" + printHash32(hash); | ||||||
|             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); |             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); | ||||||
|         } catch (Error & e) { |         } catch (Error & e) { | ||||||
|  | @ -93,7 +92,7 @@ SV * queryPathHash(char * path) | ||||||
| SV * queryDeriver(char * path) | SV * queryDeriver(char * path) | ||||||
|     PPCODE: |     PPCODE: | ||||||
|         try { |         try { | ||||||
|             Path deriver = store()->queryDeriver(path); |             auto deriver = store()->queryPathInfo(path)->deriver; | ||||||
|             if (deriver == "") XSRETURN_UNDEF; |             if (deriver == "") XSRETURN_UNDEF; | ||||||
|             XPUSHs(sv_2mortal(newSVpv(deriver.c_str(), 0))); |             XPUSHs(sv_2mortal(newSVpv(deriver.c_str(), 0))); | ||||||
|         } catch (Error & e) { |         } catch (Error & e) { | ||||||
|  | @ -104,17 +103,17 @@ SV * queryDeriver(char * path) | ||||||
| SV * queryPathInfo(char * path, int base32) | SV * queryPathInfo(char * path, int base32) | ||||||
|     PPCODE: |     PPCODE: | ||||||
|         try { |         try { | ||||||
|             ValidPathInfo info = store()->queryPathInfo(path); |             auto info = store()->queryPathInfo(path); | ||||||
|             if (info.deriver == "") |             if (info->deriver == "") | ||||||
|                 XPUSHs(&PL_sv_undef); |                 XPUSHs(&PL_sv_undef); | ||||||
|             else |             else | ||||||
|                 XPUSHs(sv_2mortal(newSVpv(info.deriver.c_str(), 0))); |                 XPUSHs(sv_2mortal(newSVpv(info->deriver.c_str(), 0))); | ||||||
|             string s = "sha256:" + (base32 ? printHash32(info.narHash) : printHash(info.narHash)); |             string s = "sha256:" + (base32 ? printHash32(info->narHash) : printHash(info->narHash)); | ||||||
|             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); |             XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); | ||||||
|             mXPUSHi(info.registrationTime); |             mXPUSHi(info->registrationTime); | ||||||
|             mXPUSHi(info.narSize); |             mXPUSHi(info->narSize); | ||||||
|             AV * arr = newAV(); |             AV * arr = newAV(); | ||||||
|             for (PathSet::iterator i = info.references.begin(); i != info.references.end(); ++i) |             for (PathSet::iterator i = info->references.begin(); i != info->references.end(); ++i) | ||||||
|                 av_push(arr, newSVpv(i->c_str(), 0)); |                 av_push(arr, newSVpv(i->c_str(), 0)); | ||||||
|             XPUSHs(sv_2mortal(newRV((SV *) arr))); |             XPUSHs(sv_2mortal(newRV((SV *) arr))); | ||||||
|         } catch (Error & e) { |         } catch (Error & e) { | ||||||
|  |  | ||||||
|  | @ -40,11 +40,6 @@ void BinaryCacheStore::notImpl() | ||||||
|     throw Error("operation not implemented for binary cache stores"); |     throw Error("operation not implemented for binary cache stores"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const BinaryCacheStore::Stats & BinaryCacheStore::getStats() |  | ||||||
| { |  | ||||||
|     return stats; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Path BinaryCacheStore::narInfoFileFor(const Path & storePath) | Path BinaryCacheStore::narInfoFileFor(const Path & storePath) | ||||||
| { | { | ||||||
|     assertStorePath(storePath); |     assertStorePath(storePath); | ||||||
|  | @ -100,67 +95,15 @@ void BinaryCacheStore::addToCache(const ValidPathInfo & info, | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto state_(state.lock()); |         auto state_(state.lock()); | ||||||
|         state_->narInfoCache.upsert(narInfo->path, narInfo); |         state_->pathInfoCache.upsert(narInfo->path, std::shared_ptr<NarInfo>(narInfo)); | ||||||
|         stats.narInfoCacheSize = state_->narInfoCache.size(); |         stats.pathInfoCacheSize = state_->pathInfoCache.size(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     stats.narInfoWrite++; |     stats.narInfoWrite++; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| NarInfo BinaryCacheStore::readNarInfo(const Path & storePath) | bool BinaryCacheStore::isValidPathUncached(const Path & storePath) | ||||||
| { | { | ||||||
|     { |  | ||||||
|         auto state_(state.lock()); |  | ||||||
|         auto res = state_->narInfoCache.get(storePath); |  | ||||||
|         if (res) { |  | ||||||
|             stats.narInfoReadAverted++; |  | ||||||
|             if (!*res) |  | ||||||
|                 throw InvalidPath(format("path ‘%s’ is not valid") % storePath); |  | ||||||
|             return **res; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     auto narInfoFile = narInfoFileFor(storePath); |  | ||||||
|     auto data = getFile(narInfoFile); |  | ||||||
|     if (!data) { |  | ||||||
|         stats.narInfoMissing++; |  | ||||||
|         auto state_(state.lock()); |  | ||||||
|         state_->narInfoCache.upsert(storePath, 0); |  | ||||||
|         stats.narInfoCacheSize = state_->narInfoCache.size(); |  | ||||||
|         throw InvalidPath(format("path ‘%s’ is not valid") % storePath); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     auto narInfo = make_ref<NarInfo>(*data, narInfoFile); |  | ||||||
|     if (narInfo->path != storePath) |  | ||||||
|         throw Error(format("NAR info file for store path ‘%1%’ does not match ‘%2%’") % narInfo->path % storePath); |  | ||||||
| 
 |  | ||||||
|     stats.narInfoRead++; |  | ||||||
| 
 |  | ||||||
|     if (publicKeys) { |  | ||||||
|         if (!narInfo->checkSignatures(*publicKeys)) |  | ||||||
|             throw Error(format("no good signature on NAR info file ‘%1%’") % narInfoFile); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     { |  | ||||||
|         auto state_(state.lock()); |  | ||||||
|         state_->narInfoCache.upsert(storePath, narInfo); |  | ||||||
|         stats.narInfoCacheSize = state_->narInfoCache.size(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return *narInfo; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool BinaryCacheStore::isValidPath(const Path & storePath) |  | ||||||
| { |  | ||||||
|     { |  | ||||||
|         auto state_(state.lock()); |  | ||||||
|         auto res = state_->narInfoCache.get(storePath); |  | ||||||
|         if (res) { |  | ||||||
|             stats.narInfoReadAverted++; |  | ||||||
|             return *res != 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // FIXME: this only checks whether a .narinfo with a matching hash
 |     // FIXME: this only checks whether a .narinfo with a matching hash
 | ||||||
|     // part exists. So ‘f4kb...-foo’ matches ‘f4kb...-bar’, even
 |     // part exists. So ‘f4kb...-foo’ matches ‘f4kb...-bar’, even
 | ||||||
|     // though they shouldn't. Not easily fixed.
 |     // though they shouldn't. Not easily fixed.
 | ||||||
|  | @ -169,20 +112,20 @@ bool BinaryCacheStore::isValidPath(const Path & storePath) | ||||||
| 
 | 
 | ||||||
| void BinaryCacheStore::narFromPath(const Path & storePath, Sink & sink) | void BinaryCacheStore::narFromPath(const Path & storePath, Sink & sink) | ||||||
| { | { | ||||||
|     auto res = readNarInfo(storePath); |     auto info = queryPathInfo(storePath).cast<const NarInfo>(); | ||||||
| 
 | 
 | ||||||
|     auto nar = getFile(res.url); |     auto nar = getFile(info->url); | ||||||
| 
 | 
 | ||||||
|     if (!nar) throw Error(format("file ‘%s’ missing from binary cache") % res.url); |     if (!nar) throw Error(format("file ‘%s’ missing from binary cache") % info->url); | ||||||
| 
 | 
 | ||||||
|     stats.narRead++; |     stats.narRead++; | ||||||
|     stats.narReadCompressedBytes += nar->size(); |     stats.narReadCompressedBytes += nar->size(); | ||||||
| 
 | 
 | ||||||
|     /* Decompress the NAR. FIXME: would be nice to have the remote
 |     /* Decompress the NAR. FIXME: would be nice to have the remote
 | ||||||
|        side do this. */ |        side do this. */ | ||||||
|     if (res.compression == "none") |     if (info->compression == "none") | ||||||
|         ; |         ; | ||||||
|     else if (res.compression == "xz") |     else if (info->compression == "xz") | ||||||
|         nar = decompressXZ(*nar); |         nar = decompressXZ(*nar); | ||||||
|     else |     else | ||||||
|         throw Error(format("unknown NAR compression type ‘%1%’") % nar); |         throw Error(format("unknown NAR compression type ‘%1%’") % nar); | ||||||
|  | @ -200,13 +143,13 @@ void BinaryCacheStore::exportPath(const Path & storePath, bool sign, Sink & sink | ||||||
| { | { | ||||||
|     assert(!sign); |     assert(!sign); | ||||||
| 
 | 
 | ||||||
|     auto res = readNarInfo(storePath); |     auto res = queryPathInfo(storePath); | ||||||
| 
 | 
 | ||||||
|     narFromPath(storePath, sink); |     narFromPath(storePath, sink); | ||||||
| 
 | 
 | ||||||
|     // FIXME: check integrity of NAR.
 |     // FIXME: check integrity of NAR.
 | ||||||
| 
 | 
 | ||||||
|     sink << exportMagic << storePath << res.references << res.deriver << 0; |     sink << exportMagic << storePath << res->references << res->deriver << 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Paths BinaryCacheStore::importPaths(bool requireSignature, Source & source, | Paths BinaryCacheStore::importPaths(bool requireSignature, Source & source, | ||||||
|  | @ -244,9 +187,24 @@ struct NopSink : ParseSink | ||||||
| { | { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| ValidPathInfo BinaryCacheStore::queryPathInfo(const Path & storePath) | std::shared_ptr<ValidPathInfo> BinaryCacheStore::queryPathInfoUncached(const Path & storePath) | ||||||
| { | { | ||||||
|     return ValidPathInfo(readNarInfo(storePath)); |     auto narInfoFile = narInfoFileFor(storePath); | ||||||
|  |     auto data = getFile(narInfoFile); | ||||||
|  |     if (!data) return 0; | ||||||
|  | 
 | ||||||
|  |     auto narInfo = make_ref<NarInfo>(*data, narInfoFile); | ||||||
|  |     if (narInfo->path != storePath) | ||||||
|  |         throw Error(format("NAR info file for store path ‘%1%’ does not match ‘%2%’") % narInfo->path % storePath); | ||||||
|  | 
 | ||||||
|  |     stats.narInfoRead++; | ||||||
|  | 
 | ||||||
|  |     if (publicKeys) { | ||||||
|  |         if (!narInfo->checkSignatures(*publicKeys)) | ||||||
|  |             throw Error(format("no good signature on NAR info file ‘%1%’") % narInfoFile); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return std::shared_ptr<NarInfo>(narInfo); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void BinaryCacheStore::querySubstitutablePathInfos(const PathSet & paths, | void BinaryCacheStore::querySubstitutablePathInfos(const PathSet & paths, | ||||||
|  | @ -257,16 +215,16 @@ void BinaryCacheStore::querySubstitutablePathInfos(const PathSet & paths, | ||||||
|     if (!localStore) return; |     if (!localStore) return; | ||||||
| 
 | 
 | ||||||
|     for (auto & storePath : paths) { |     for (auto & storePath : paths) { | ||||||
|         if (!localStore->isValidPath(storePath)) { |         try { | ||||||
|  |             auto info = localStore->queryPathInfo(storePath); | ||||||
|  |             SubstitutablePathInfo sub; | ||||||
|  |             sub.references = info->references; | ||||||
|  |             sub.downloadSize = 0; | ||||||
|  |             sub.narSize = info->narSize; | ||||||
|  |             infos.emplace(storePath, sub); | ||||||
|  |         } catch (InvalidPath &) { | ||||||
|             left.insert(storePath); |             left.insert(storePath); | ||||||
|             continue; |  | ||||||
|         } |         } | ||||||
|         ValidPathInfo info = localStore->queryPathInfo(storePath); |  | ||||||
|         SubstitutablePathInfo sub; |  | ||||||
|         sub.references = info.references; |  | ||||||
|         sub.downloadSize = 0; |  | ||||||
|         sub.narSize = info.narSize; |  | ||||||
|         infos.emplace(storePath, sub); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (settings.useSubstitutes) |     if (settings.useSubstitutes) | ||||||
|  | @ -332,16 +290,16 @@ void BinaryCacheStore::buildPaths(const PathSet & paths, BuildMode buildMode) | ||||||
|         if (!localStore->isValidPath(storePath)) |         if (!localStore->isValidPath(storePath)) | ||||||
|             localStore->ensurePath(storePath); |             localStore->ensurePath(storePath); | ||||||
| 
 | 
 | ||||||
|         ValidPathInfo info = localStore->queryPathInfo(storePath); |         auto info = localStore->queryPathInfo(storePath); | ||||||
| 
 | 
 | ||||||
|         for (auto & ref : info.references) |         for (auto & ref : info->references) | ||||||
|             if (ref != storePath) |             if (ref != storePath) | ||||||
|                 ensurePath(ref); |                 ensurePath(ref); | ||||||
| 
 | 
 | ||||||
|         StringSink sink; |         StringSink sink; | ||||||
|         dumpPath(storePath, sink); |         dumpPath(storePath, sink); | ||||||
| 
 | 
 | ||||||
|         addToCache(info, *sink.s); |         addToCache(*info, *sink.s); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,8 +3,6 @@ | ||||||
| #include "crypto.hh" | #include "crypto.hh" | ||||||
| #include "store-api.hh" | #include "store-api.hh" | ||||||
| 
 | 
 | ||||||
| #include "lru-cache.hh" |  | ||||||
| #include "sync.hh" |  | ||||||
| #include "pool.hh" | #include "pool.hh" | ||||||
| 
 | 
 | ||||||
| #include <atomic> | #include <atomic> | ||||||
|  | @ -22,13 +20,6 @@ private: | ||||||
| 
 | 
 | ||||||
|     std::shared_ptr<Store> localStore; |     std::shared_ptr<Store> localStore; | ||||||
| 
 | 
 | ||||||
|     struct State |  | ||||||
|     { |  | ||||||
|         LRUCache<Path, std::shared_ptr<NarInfo>> narInfoCache{64 * 1024}; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     Sync<State> state; |  | ||||||
| 
 |  | ||||||
| protected: | protected: | ||||||
| 
 | 
 | ||||||
|     BinaryCacheStore(std::shared_ptr<Store> localStore, const Path & secretKeyFile); |     BinaryCacheStore(std::shared_ptr<Store> localStore, const Path & secretKeyFile); | ||||||
|  | @ -47,42 +38,17 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual void init(); |     virtual void init(); | ||||||
| 
 | 
 | ||||||
|     struct Stats |  | ||||||
|     { |  | ||||||
|         std::atomic<uint64_t> narInfoRead{0}; |  | ||||||
|         std::atomic<uint64_t> narInfoReadAverted{0}; |  | ||||||
|         std::atomic<uint64_t> narInfoMissing{0}; |  | ||||||
|         std::atomic<uint64_t> narInfoWrite{0}; |  | ||||||
|         std::atomic<uint64_t> narInfoCacheSize{0}; |  | ||||||
|         std::atomic<uint64_t> narRead{0}; |  | ||||||
|         std::atomic<uint64_t> narReadBytes{0}; |  | ||||||
|         std::atomic<uint64_t> narReadCompressedBytes{0}; |  | ||||||
|         std::atomic<uint64_t> narWrite{0}; |  | ||||||
|         std::atomic<uint64_t> narWriteAverted{0}; |  | ||||||
|         std::atomic<uint64_t> narWriteBytes{0}; |  | ||||||
|         std::atomic<uint64_t> narWriteCompressedBytes{0}; |  | ||||||
|         std::atomic<uint64_t> narWriteCompressionTimeMs{0}; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     const Stats & getStats(); |  | ||||||
| 
 |  | ||||||
| private: | private: | ||||||
| 
 | 
 | ||||||
|     Stats stats; |  | ||||||
| 
 |  | ||||||
|     std::string narMagic; |     std::string narMagic; | ||||||
| 
 | 
 | ||||||
|     std::string narInfoFileFor(const Path & storePath); |     std::string narInfoFileFor(const Path & storePath); | ||||||
| 
 | 
 | ||||||
|     void addToCache(const ValidPathInfo & info, const string & nar); |     void addToCache(const ValidPathInfo & info, const string & nar); | ||||||
| 
 | 
 | ||||||
| protected: |  | ||||||
| 
 |  | ||||||
|     NarInfo readNarInfo(const Path & storePath); |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|     bool isValidPath(const Path & path) override; |     bool isValidPathUncached(const Path & path) override; | ||||||
| 
 | 
 | ||||||
|     PathSet queryValidPaths(const PathSet & paths) override |     PathSet queryValidPaths(const PathSet & paths) override | ||||||
|     { notImpl(); } |     { notImpl(); } | ||||||
|  | @ -90,18 +56,12 @@ public: | ||||||
|     PathSet queryAllValidPaths() override |     PathSet queryAllValidPaths() override | ||||||
|     { notImpl(); } |     { notImpl(); } | ||||||
| 
 | 
 | ||||||
|     ValidPathInfo queryPathInfo(const Path & path) override; |     std::shared_ptr<ValidPathInfo> queryPathInfoUncached(const Path & path) override; | ||||||
| 
 |  | ||||||
|     Hash queryPathHash(const Path & path) override |  | ||||||
|     { notImpl(); } |  | ||||||
| 
 | 
 | ||||||
|     void queryReferrers(const Path & path, |     void queryReferrers(const Path & path, | ||||||
|         PathSet & referrers) override |         PathSet & referrers) override | ||||||
|     { notImpl(); } |     { notImpl(); } | ||||||
| 
 | 
 | ||||||
|     Path queryDeriver(const Path & path) override |  | ||||||
|     { return ""; } |  | ||||||
| 
 |  | ||||||
|     PathSet queryValidDerivers(const Path & path) override |     PathSet queryValidDerivers(const Path & path) override | ||||||
|     { return {}; } |     { return {}; } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2724,7 +2724,7 @@ void DerivationGoal::registerOutputs() | ||||||
| 
 | 
 | ||||||
|         if (buildMode == bmCheck) { |         if (buildMode == bmCheck) { | ||||||
|             if (!worker.store.isValidPath(path)) continue; |             if (!worker.store.isValidPath(path)) continue; | ||||||
|             ValidPathInfo info = worker.store.queryPathInfo(path); |             auto info = *worker.store.queryPathInfo(path); | ||||||
|             if (hash.first != info.narHash) { |             if (hash.first != info.narHash) { | ||||||
|                 if (settings.keepFailed) { |                 if (settings.keepFailed) { | ||||||
|                     Path dst = path + checkSuffix; |                     Path dst = path + checkSuffix; | ||||||
|  | @ -3778,14 +3778,14 @@ bool Worker::pathContentsGood(const Path & path) | ||||||
|     std::map<Path, bool>::iterator i = pathContentsGoodCache.find(path); |     std::map<Path, bool>::iterator i = pathContentsGoodCache.find(path); | ||||||
|     if (i != pathContentsGoodCache.end()) return i->second; |     if (i != pathContentsGoodCache.end()) return i->second; | ||||||
|     printMsg(lvlInfo, format("checking path ‘%1%’...") % path); |     printMsg(lvlInfo, format("checking path ‘%1%’...") % path); | ||||||
|     ValidPathInfo info = store.queryPathInfo(path); |     auto info = store.queryPathInfo(path); | ||||||
|     bool res; |     bool res; | ||||||
|     if (!pathExists(path)) |     if (!pathExists(path)) | ||||||
|         res = false; |         res = false; | ||||||
|     else { |     else { | ||||||
|         HashResult current = hashPath(info.narHash.type, path); |         HashResult current = hashPath(info->narHash.type, path); | ||||||
|         Hash nullHash(htSHA256); |         Hash nullHash(htSHA256); | ||||||
|         res = info.narHash == nullHash || info.narHash == current.first; |         res = info->narHash == nullHash || info->narHash == current.first; | ||||||
|     } |     } | ||||||
|     pathContentsGoodCache[path] = res; |     pathContentsGoodCache[path] = res; | ||||||
|     if (!res) printMsg(lvlError, format("path ‘%1%’ is corrupted or missing!") % path); |     if (!res) printMsg(lvlError, format("path ‘%1%’ is corrupted or missing!") % path); | ||||||
|  | @ -3881,7 +3881,7 @@ void LocalStore::repairPath(const Path & path) | ||||||
|     if (goal->getExitCode() != Goal::ecSuccess) { |     if (goal->getExitCode() != Goal::ecSuccess) { | ||||||
|         /* Since substituting the path didn't work, if we have a valid
 |         /* Since substituting the path didn't work, if we have a valid
 | ||||||
|            deriver, then rebuild the deriver. */ |            deriver, then rebuild the deriver. */ | ||||||
|         Path deriver = queryDeriver(path); |         auto deriver = queryPathInfo(path)->deriver; | ||||||
|         if (deriver != "" && isValidPath(deriver)) { |         if (deriver != "" && isValidPath(deriver)) { | ||||||
|             goals.clear(); |             goals.clear(); | ||||||
|             goals.insert(worker.makeDerivationGoal(deriver, StringSet(), bmRepair)); |             goals.insert(worker.makeDerivationGoal(deriver, StringSet(), bmRepair)); | ||||||
|  |  | ||||||
|  | @ -407,7 +407,7 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path) | ||||||
|         queryReferrers(path, referrers); |         queryReferrers(path, referrers); | ||||||
|         for (auto & i : referrers) |         for (auto & i : referrers) | ||||||
|             if (i != path) deletePathRecursive(state, i); |             if (i != path) deletePathRecursive(state, i); | ||||||
|         size = queryPathInfo(path).narSize; |         size = queryPathInfo(path)->narSize; | ||||||
|         invalidatePathChecked(path); |         invalidatePathChecked(path); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -485,7 +485,7 @@ bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & p | ||||||
|     if (state.gcKeepDerivations && isDerivation(path)) { |     if (state.gcKeepDerivations && isDerivation(path)) { | ||||||
|         PathSet outputs = queryDerivationOutputs(path); |         PathSet outputs = queryDerivationOutputs(path); | ||||||
|         for (auto & i : outputs) |         for (auto & i : outputs) | ||||||
|             if (isValidPath(i) && queryDeriver(i) == path) |             if (isValidPath(i) && queryPathInfo(i)->deriver == path) | ||||||
|                 incoming.insert(i); |                 incoming.insert(i); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -577,6 +577,12 @@ uint64_t LocalStore::addValidPath(State & state, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     { | ||||||
|  |         auto state_(Store::state.lock()); | ||||||
|  |         state_->pathInfoCache.upsert(info.path, std::make_shared<ValidPathInfo>(info)); | ||||||
|  |         stats.pathInfoCacheSize = state_->pathInfoCache.size(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return id; |     return id; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -595,44 +601,44 @@ Hash parseHashField(const Path & path, const string & s) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ValidPathInfo LocalStore::queryPathInfo(const Path & path) | std::shared_ptr<ValidPathInfo> LocalStore::queryPathInfoUncached(const Path & path) | ||||||
| { | { | ||||||
|     ValidPathInfo info; |     auto info = std::make_shared<ValidPathInfo>(); | ||||||
|     info.path = path; |     info->path = path; | ||||||
| 
 | 
 | ||||||
|     assertStorePath(path); |     assertStorePath(path); | ||||||
| 
 | 
 | ||||||
|     return retrySQLite<ValidPathInfo>([&]() { |     return retrySQLite<std::shared_ptr<ValidPathInfo>>([&]() { | ||||||
|         auto state(_state.lock()); |         auto state(_state.lock()); | ||||||
| 
 | 
 | ||||||
|         /* Get the path info. */ |         /* Get the path info. */ | ||||||
|         auto useQueryPathInfo(state->stmtQueryPathInfo.use()(path)); |         auto useQueryPathInfo(state->stmtQueryPathInfo.use()(path)); | ||||||
| 
 | 
 | ||||||
|         if (!useQueryPathInfo.next()) |         if (!useQueryPathInfo.next()) | ||||||
|             throw Error(format("path ‘%1%’ is not valid") % path); |             return std::shared_ptr<ValidPathInfo>(); | ||||||
| 
 | 
 | ||||||
|         info.id = useQueryPathInfo.getInt(0); |         info->id = useQueryPathInfo.getInt(0); | ||||||
| 
 | 
 | ||||||
|         info.narHash = parseHashField(path, useQueryPathInfo.getStr(1)); |         info->narHash = parseHashField(path, useQueryPathInfo.getStr(1)); | ||||||
| 
 | 
 | ||||||
|         info.registrationTime = useQueryPathInfo.getInt(2); |         info->registrationTime = useQueryPathInfo.getInt(2); | ||||||
| 
 | 
 | ||||||
|         auto s = (const char *) sqlite3_column_text(state->stmtQueryPathInfo, 3); |         auto s = (const char *) sqlite3_column_text(state->stmtQueryPathInfo, 3); | ||||||
|         if (s) info.deriver = s; |         if (s) info->deriver = s; | ||||||
| 
 | 
 | ||||||
|         /* Note that narSize = NULL yields 0. */ |         /* Note that narSize = NULL yields 0. */ | ||||||
|         info.narSize = useQueryPathInfo.getInt(4); |         info->narSize = useQueryPathInfo.getInt(4); | ||||||
| 
 | 
 | ||||||
|         info.ultimate = useQueryPathInfo.getInt(5) == 1; |         info->ultimate = useQueryPathInfo.getInt(5) == 1; | ||||||
| 
 | 
 | ||||||
|         s = (const char *) sqlite3_column_text(state->stmtQueryPathInfo, 6); |         s = (const char *) sqlite3_column_text(state->stmtQueryPathInfo, 6); | ||||||
|         if (s) info.sigs = tokenizeString<StringSet>(s, " "); |         if (s) info->sigs = tokenizeString<StringSet>(s, " "); | ||||||
| 
 | 
 | ||||||
|         /* Get the references. */ |         /* Get the references. */ | ||||||
|         auto useQueryReferences(state->stmtQueryReferences.use()(info.id)); |         auto useQueryReferences(state->stmtQueryReferences.use()(info->id)); | ||||||
| 
 | 
 | ||||||
|         while (useQueryReferences.next()) |         while (useQueryReferences.next()) | ||||||
|             info.references.insert(useQueryReferences.getStr(0)); |             info->references.insert(useQueryReferences.getStr(0)); | ||||||
| 
 | 
 | ||||||
|         return info; |         return info; | ||||||
|     }); |     }); | ||||||
|  | @ -661,17 +667,17 @@ uint64_t LocalStore::queryValidPathId(State & state, const Path & path) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| bool LocalStore::isValidPath(State & state, const Path & path) | bool LocalStore::isValidPath_(State & state, const Path & path) | ||||||
| { | { | ||||||
|     return state.stmtQueryPathInfo.use()(path).next(); |     return state.stmtQueryPathInfo.use()(path).next(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| bool LocalStore::isValidPath(const Path & path) | bool LocalStore::isValidPathUncached(const Path & path) | ||||||
| { | { | ||||||
|     return retrySQLite<bool>([&]() { |     return retrySQLite<bool>([&]() { | ||||||
|         auto state(_state.lock()); |         auto state(_state.lock()); | ||||||
|         return isValidPath(*state, path); |         return isValidPath_(*state, path); | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -716,12 +722,6 @@ void LocalStore::queryReferrers(const Path & path, PathSet & referrers) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Path LocalStore::queryDeriver(const Path & path) |  | ||||||
| { |  | ||||||
|     return queryPathInfo(path).deriver; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| PathSet LocalStore::queryValidDerivers(const Path & path) | PathSet LocalStore::queryValidDerivers(const Path & path) | ||||||
| { | { | ||||||
|     assertStorePath(path); |     assertStorePath(path); | ||||||
|  | @ -996,12 +996,6 @@ void LocalStore::querySubstitutablePathInfos(const PathSet & paths, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Hash LocalStore::queryPathHash(const Path & path) |  | ||||||
| { |  | ||||||
|     return queryPathInfo(path).narHash; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| void LocalStore::registerValidPath(const ValidPathInfo & info) | void LocalStore::registerValidPath(const ValidPathInfo & info) | ||||||
| { | { | ||||||
|     ValidPathInfos infos; |     ValidPathInfos infos; | ||||||
|  | @ -1026,7 +1020,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos) | ||||||
| 
 | 
 | ||||||
|         for (auto & i : infos) { |         for (auto & i : infos) { | ||||||
|             assert(i.narHash.type == htSHA256); |             assert(i.narHash.type == htSHA256); | ||||||
|             if (isValidPath(*state, i.path)) |             if (isValidPath_(*state, i.path)) | ||||||
|                 updatePathInfo(*state, i); |                 updatePathInfo(*state, i); | ||||||
|             else |             else | ||||||
|                 addValidPath(*state, i, false); |                 addValidPath(*state, i, false); | ||||||
|  | @ -1071,6 +1065,12 @@ void LocalStore::invalidatePath(State & state, const Path & path) | ||||||
| 
 | 
 | ||||||
|     /* Note that the foreign key constraints on the Refs table take
 |     /* Note that the foreign key constraints on the Refs table take
 | ||||||
|        care of deleting the references entries for `path'. */ |        care of deleting the references entries for `path'. */ | ||||||
|  | 
 | ||||||
|  |     { | ||||||
|  |         auto state_(Store::state.lock()); | ||||||
|  |         state_->pathInfoCache.erase(path); | ||||||
|  |         stats.pathInfoCacheSize = state_->pathInfoCache.size(); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1225,8 +1225,7 @@ void LocalStore::exportPath(const Path & path, bool sign, | ||||||
| 
 | 
 | ||||||
|     printMsg(lvlTalkative, format("exporting path ‘%1%’") % path); |     printMsg(lvlTalkative, format("exporting path ‘%1%’") % path); | ||||||
| 
 | 
 | ||||||
|     if (!isValidPath(path)) |     auto info = queryPathInfo(path); | ||||||
|         throw Error(format("path ‘%1%’ is not valid") % path); |  | ||||||
| 
 | 
 | ||||||
|     HashAndWriteSink hashAndWriteSink(sink); |     HashAndWriteSink hashAndWriteSink(sink); | ||||||
| 
 | 
 | ||||||
|  | @ -1236,15 +1235,11 @@ void LocalStore::exportPath(const Path & path, bool sign, | ||||||
|        filesystem corruption from spreading to other machines. |        filesystem corruption from spreading to other machines. | ||||||
|        Don't complain if the stored hash is zero (unknown). */ |        Don't complain if the stored hash is zero (unknown). */ | ||||||
|     Hash hash = hashAndWriteSink.currentHash(); |     Hash hash = hashAndWriteSink.currentHash(); | ||||||
|     Hash storedHash = queryPathHash(path); |     if (hash != info->narHash && info->narHash != Hash(info->narHash.type)) | ||||||
|     if (hash != storedHash && storedHash != Hash(storedHash.type)) |  | ||||||
|         throw Error(format("hash of path ‘%1%’ has changed from ‘%2%’ to ‘%3%’!") % path |         throw Error(format("hash of path ‘%1%’ has changed from ‘%2%’ to ‘%3%’!") % path | ||||||
|             % printHash(storedHash) % printHash(hash)); |             % printHash(info->narHash) % printHash(hash)); | ||||||
| 
 | 
 | ||||||
|     PathSet references; |     hashAndWriteSink << exportMagic << path << info->references << info->deriver; | ||||||
|     queryReferences(path, references); |  | ||||||
| 
 |  | ||||||
|     hashAndWriteSink << exportMagic << path << references << queryDeriver(path); |  | ||||||
| 
 | 
 | ||||||
|     if (sign) { |     if (sign) { | ||||||
|         Hash hash = hashAndWriteSink.currentHash(); |         Hash hash = hashAndWriteSink.currentHash(); | ||||||
|  | @ -1440,7 +1435,7 @@ void LocalStore::invalidatePathChecked(const Path & path) | ||||||
| 
 | 
 | ||||||
|         SQLiteTxn txn(state->db); |         SQLiteTxn txn(state->db); | ||||||
| 
 | 
 | ||||||
|         if (isValidPath(*state, path)) { |         if (isValidPath_(*state, path)) { | ||||||
|             PathSet referrers; queryReferrers(*state, path, referrers); |             PathSet referrers; queryReferrers(*state, path, referrers); | ||||||
|             referrers.erase(path); /* ignore self-references */ |             referrers.erase(path); /* ignore self-references */ | ||||||
|             if (!referrers.empty()) |             if (!referrers.empty()) | ||||||
|  | @ -1486,38 +1481,38 @@ bool LocalStore::verifyStore(bool checkContents, bool repair) | ||||||
| 
 | 
 | ||||||
|         for (auto & i : validPaths) { |         for (auto & i : validPaths) { | ||||||
|             try { |             try { | ||||||
|                 ValidPathInfo info = queryPathInfo(i); |                 auto info = std::const_pointer_cast<ValidPathInfo>(std::shared_ptr<const ValidPathInfo>(queryPathInfo(i))); | ||||||
| 
 | 
 | ||||||
|                 /* Check the content hash (optionally - slow). */ |                 /* Check the content hash (optionally - slow). */ | ||||||
|                 printMsg(lvlTalkative, format("checking contents of ‘%1%’") % i); |                 printMsg(lvlTalkative, format("checking contents of ‘%1%’") % i); | ||||||
|                 HashResult current = hashPath(info.narHash.type, i); |                 HashResult current = hashPath(info->narHash.type, i); | ||||||
| 
 | 
 | ||||||
|                 if (info.narHash != nullHash && info.narHash != current.first) { |                 if (info->narHash != nullHash && info->narHash != current.first) { | ||||||
|                     printMsg(lvlError, format("path ‘%1%’ was modified! " |                     printMsg(lvlError, format("path ‘%1%’ was modified! " | ||||||
|                             "expected hash ‘%2%’, got ‘%3%’") |                             "expected hash ‘%2%’, got ‘%3%’") | ||||||
|                         % i % printHash(info.narHash) % printHash(current.first)); |                         % i % printHash(info->narHash) % printHash(current.first)); | ||||||
|                     if (repair) repairPath(i); else errors = true; |                     if (repair) repairPath(i); else errors = true; | ||||||
|                 } else { |                 } else { | ||||||
| 
 | 
 | ||||||
|                     bool update = false; |                     bool update = false; | ||||||
| 
 | 
 | ||||||
|                     /* Fill in missing hashes. */ |                     /* Fill in missing hashes. */ | ||||||
|                     if (info.narHash == nullHash) { |                     if (info->narHash == nullHash) { | ||||||
|                         printMsg(lvlError, format("fixing missing hash on ‘%1%’") % i); |                         printMsg(lvlError, format("fixing missing hash on ‘%1%’") % i); | ||||||
|                         info.narHash = current.first; |                         info->narHash = current.first; | ||||||
|                         update = true; |                         update = true; | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     /* Fill in missing narSize fields (from old stores). */ |                     /* Fill in missing narSize fields (from old stores). */ | ||||||
|                     if (info.narSize == 0) { |                     if (info->narSize == 0) { | ||||||
|                         printMsg(lvlError, format("updating size field on ‘%1%’ to %2%") % i % current.second); |                         printMsg(lvlError, format("updating size field on ‘%1%’ to %2%") % i % current.second); | ||||||
|                         info.narSize = current.second; |                         info->narSize = current.second; | ||||||
|                         update = true; |                         update = true; | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     if (update) { |                     if (update) { | ||||||
|                         auto state(_state.lock()); |                         auto state(_state.lock()); | ||||||
|                         updatePathInfo(*state, info); |                         updatePathInfo(*state, *info); | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                 } |                 } | ||||||
|  | @ -1656,11 +1651,11 @@ void LocalStore::addSignatures(const Path & storePath, const StringSet & sigs) | ||||||
| 
 | 
 | ||||||
|         SQLiteTxn txn(state->db); |         SQLiteTxn txn(state->db); | ||||||
| 
 | 
 | ||||||
|         auto info = queryPathInfo(storePath); |         auto info = std::const_pointer_cast<ValidPathInfo>(std::shared_ptr<const ValidPathInfo>(queryPathInfo(storePath))); | ||||||
| 
 | 
 | ||||||
|         info.sigs.insert(sigs.begin(), sigs.end()); |         info->sigs.insert(sigs.begin(), sigs.end()); | ||||||
| 
 | 
 | ||||||
|         updatePathInfo(*state, info); |         updatePathInfo(*state, *info); | ||||||
| 
 | 
 | ||||||
|         txn.commit(); |         txn.commit(); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  | @ -102,20 +102,16 @@ public: | ||||||
| 
 | 
 | ||||||
|     /* Implementations of abstract store API methods. */ |     /* Implementations of abstract store API methods. */ | ||||||
| 
 | 
 | ||||||
|     bool isValidPath(const Path & path) override; |     bool isValidPathUncached(const Path & path) override; | ||||||
| 
 | 
 | ||||||
|     PathSet queryValidPaths(const PathSet & paths) override; |     PathSet queryValidPaths(const PathSet & paths) override; | ||||||
| 
 | 
 | ||||||
|     PathSet queryAllValidPaths() override; |     PathSet queryAllValidPaths() override; | ||||||
| 
 | 
 | ||||||
|     ValidPathInfo queryPathInfo(const Path & path) override; |     std::shared_ptr<ValidPathInfo> queryPathInfoUncached(const Path & path) override; | ||||||
| 
 |  | ||||||
|     Hash queryPathHash(const Path & path) override; |  | ||||||
| 
 | 
 | ||||||
|     void queryReferrers(const Path & path, PathSet & referrers) override; |     void queryReferrers(const Path & path, PathSet & referrers) override; | ||||||
| 
 | 
 | ||||||
|     Path queryDeriver(const Path & path) override; |  | ||||||
| 
 |  | ||||||
|     PathSet queryValidDerivers(const Path & path) override; |     PathSet queryValidDerivers(const Path & path) override; | ||||||
| 
 | 
 | ||||||
|     PathSet queryDerivationOutputs(const Path & path) override; |     PathSet queryDerivationOutputs(const Path & path) override; | ||||||
|  | @ -270,7 +266,7 @@ private: | ||||||
|     void optimisePath_(OptimiseStats & stats, const Path & path, InodeHash & inodeHash); |     void optimisePath_(OptimiseStats & stats, const Path & path, InodeHash & inodeHash); | ||||||
| 
 | 
 | ||||||
|     // Internal versions that are not wrapped in retry_sqlite.
 |     // Internal versions that are not wrapped in retry_sqlite.
 | ||||||
|     bool isValidPath(State & state, const Path & path); |     bool isValidPath_(State & state, const Path & path); | ||||||
|     void queryReferrers(State & state, const Path & path, PathSet & referrers); |     void queryReferrers(State & state, const Path & path, PathSet & referrers); | ||||||
| 
 | 
 | ||||||
|     /* Add signatures to a ValidPathInfo using the secret keys
 |     /* Add signatures to a ValidPathInfo using the secret keys
 | ||||||
|  |  | ||||||
|  | @ -35,12 +35,13 @@ void Store::computeFSClosure(const Path & path, | ||||||
|         if (includeDerivers && isDerivation(path)) { |         if (includeDerivers && isDerivation(path)) { | ||||||
|             PathSet outputs = queryDerivationOutputs(path); |             PathSet outputs = queryDerivationOutputs(path); | ||||||
|             for (auto & i : outputs) |             for (auto & i : outputs) | ||||||
|                 if (isValidPath(i) && queryDeriver(i) == path) |                 if (isValidPath(i) && queryPathInfo(i)->deriver == path) | ||||||
|                     edges.insert(i); |                     edges.insert(i); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     } else { |     } else { | ||||||
|         queryReferences(path, edges); |         auto info = queryPathInfo(path); | ||||||
|  |         edges = info->references; | ||||||
| 
 | 
 | ||||||
|         if (includeOutputs && isDerivation(path)) { |         if (includeOutputs && isDerivation(path)) { | ||||||
|             PathSet outputs = queryDerivationOutputs(path); |             PathSet outputs = queryDerivationOutputs(path); | ||||||
|  | @ -48,10 +49,8 @@ void Store::computeFSClosure(const Path & path, | ||||||
|                 if (isValidPath(i)) edges.insert(i); |                 if (isValidPath(i)) edges.insert(i); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (includeDerivers) { |         if (includeDerivers && isValidPath(info->deriver)) | ||||||
|             Path deriver = queryDeriver(path); |             edges.insert(info->deriver); | ||||||
|             if (isValidPath(deriver)) edges.insert(deriver); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     for (auto & i : edges) |     for (auto & i : edges) | ||||||
|  | @ -189,8 +188,10 @@ Paths Store::topoSortPaths(const PathSet & paths) | ||||||
|         parents.insert(path); |         parents.insert(path); | ||||||
| 
 | 
 | ||||||
|         PathSet references; |         PathSet references; | ||||||
|         if (isValidPath(path)) |         try { | ||||||
|             queryReferences(path, references); |             references = queryPathInfo(path)->references; | ||||||
|  |         } catch (InvalidPath &) { | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         for (auto & i : references) |         for (auto & i : references) | ||||||
|             /* Don't traverse into paths that don't exist.  That can
 |             /* Don't traverse into paths that don't exist.  That can
 | ||||||
|  |  | ||||||
|  | @ -141,7 +141,7 @@ void RemoteStore::setOptions(ref<Connection> conn) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| bool RemoteStore::isValidPath(const Path & path) | bool RemoteStore::isValidPathUncached(const Path & path) | ||||||
| { | { | ||||||
|     auto conn(connections->get()); |     auto conn(connections->get()); | ||||||
|     conn->to << wopIsValidPath << path; |     conn->to << wopIsValidPath << path; | ||||||
|  | @ -239,48 +239,27 @@ void RemoteStore::querySubstitutablePathInfos(const PathSet & paths, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ValidPathInfo RemoteStore::queryPathInfo(const Path & path) | std::shared_ptr<ValidPathInfo> RemoteStore::queryPathInfoUncached(const Path & path) | ||||||
| { | { | ||||||
|     auto conn(connections->get()); |     auto conn(connections->get()); | ||||||
|     conn->to << wopQueryPathInfo << path; |     conn->to << wopQueryPathInfo << path; | ||||||
|     conn->processStderr(); |     conn->processStderr(); | ||||||
|     ValidPathInfo info; |     auto info = std::make_shared<ValidPathInfo>(); | ||||||
|     info.path = path; |     info->path = path; | ||||||
|     info.deriver = readString(conn->from); |     info->deriver = readString(conn->from); | ||||||
|     if (info.deriver != "") assertStorePath(info.deriver); |     if (info->deriver != "") assertStorePath(info->deriver); | ||||||
|     info.narHash = parseHash(htSHA256, readString(conn->from)); |     info->narHash = parseHash(htSHA256, readString(conn->from)); | ||||||
|     info.references = readStorePaths<PathSet>(conn->from); |     info->references = readStorePaths<PathSet>(conn->from); | ||||||
|     info.registrationTime = readInt(conn->from); |     info->registrationTime = readInt(conn->from); | ||||||
|     info.narSize = readLongLong(conn->from); |     info->narSize = readLongLong(conn->from); | ||||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) { |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) { | ||||||
|         info.ultimate = readInt(conn->from) != 0; |         info->ultimate = readInt(conn->from) != 0; | ||||||
|         info.sigs = readStrings<StringSet>(conn->from); |         info->sigs = readStrings<StringSet>(conn->from); | ||||||
|     } |     } | ||||||
|     return info; |     return info; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Hash RemoteStore::queryPathHash(const Path & path) |  | ||||||
| { |  | ||||||
|     auto conn(connections->get()); |  | ||||||
|     conn->to << wopQueryPathHash << path; |  | ||||||
|     conn->processStderr(); |  | ||||||
|     string hash = readString(conn->from); |  | ||||||
|     return parseHash(htSHA256, hash); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| void RemoteStore::queryReferences(const Path & path, |  | ||||||
|     PathSet & references) |  | ||||||
| { |  | ||||||
|     auto conn(connections->get()); |  | ||||||
|     conn->to << wopQueryReferences << path; |  | ||||||
|     conn->processStderr(); |  | ||||||
|     PathSet references2 = readStorePaths<PathSet>(conn->from); |  | ||||||
|     references.insert(references2.begin(), references2.end()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| void RemoteStore::queryReferrers(const Path & path, | void RemoteStore::queryReferrers(const Path & path, | ||||||
|     PathSet & referrers) |     PathSet & referrers) | ||||||
| { | { | ||||||
|  | @ -292,17 +271,6 @@ void RemoteStore::queryReferrers(const Path & path, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Path RemoteStore::queryDeriver(const Path & path) |  | ||||||
| { |  | ||||||
|     auto conn(connections->get()); |  | ||||||
|     conn->to << wopQueryDeriver << path; |  | ||||||
|     conn->processStderr(); |  | ||||||
|     Path drvPath = readString(conn->from); |  | ||||||
|     if (drvPath != "") assertStorePath(drvPath); |  | ||||||
|     return drvPath; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| PathSet RemoteStore::queryValidDerivers(const Path & path) | PathSet RemoteStore::queryValidDerivers(const Path & path) | ||||||
| { | { | ||||||
|     auto conn(connections->get()); |     auto conn(connections->get()); | ||||||
|  | @ -517,6 +485,12 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results) | ||||||
|     results.paths = readStrings<PathSet>(conn->from); |     results.paths = readStrings<PathSet>(conn->from); | ||||||
|     results.bytesFreed = readLongLong(conn->from); |     results.bytesFreed = readLongLong(conn->from); | ||||||
|     readLongLong(conn->from); // obsolete
 |     readLongLong(conn->from); // obsolete
 | ||||||
|  | 
 | ||||||
|  |     { | ||||||
|  |         auto state_(Store::state.lock()); | ||||||
|  |         state_->pathInfoCache.clear(); | ||||||
|  |         stats.pathInfoCacheSize = 0; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,22 +26,16 @@ public: | ||||||
| 
 | 
 | ||||||
|     /* Implementations of abstract store API methods. */ |     /* Implementations of abstract store API methods. */ | ||||||
| 
 | 
 | ||||||
|     bool isValidPath(const Path & path) override; |     bool isValidPathUncached(const Path & path) override; | ||||||
| 
 | 
 | ||||||
|     PathSet queryValidPaths(const PathSet & paths) override; |     PathSet queryValidPaths(const PathSet & paths) override; | ||||||
| 
 | 
 | ||||||
|     PathSet queryAllValidPaths() override; |     PathSet queryAllValidPaths() override; | ||||||
| 
 | 
 | ||||||
|     ValidPathInfo queryPathInfo(const Path & path) override; |     std::shared_ptr<ValidPathInfo> queryPathInfoUncached(const Path & path) override; | ||||||
| 
 |  | ||||||
|     Hash queryPathHash(const Path & path) override; |  | ||||||
| 
 |  | ||||||
|     void queryReferences(const Path & path, PathSet & references) override; |  | ||||||
| 
 | 
 | ||||||
|     void queryReferrers(const Path & path, PathSet & referrers) override; |     void queryReferrers(const Path & path, PathSet & referrers) override; | ||||||
| 
 | 
 | ||||||
|     Path queryDeriver(const Path & path) override; |  | ||||||
| 
 |  | ||||||
|     PathSet queryValidDerivers(const Path & path) override; |     PathSet queryValidDerivers(const Path & path) override; | ||||||
| 
 | 
 | ||||||
|     PathSet queryDerivationOutputs(const Path & path) override; |     PathSet queryDerivationOutputs(const Path & path) override; | ||||||
|  |  | ||||||
|  | @ -225,10 +225,48 @@ Path computeStorePathForText(const string & name, const string & s, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void Store::queryReferences(const Path & path, PathSet & references) | bool Store::isValidPath(const Path & storePath) | ||||||
| { | { | ||||||
|     ValidPathInfo info = queryPathInfo(path); |     { | ||||||
|     references.insert(info.references.begin(), info.references.end()); |         auto state_(state.lock()); | ||||||
|  |         auto res = state_->pathInfoCache.get(storePath); | ||||||
|  |         if (res) { | ||||||
|  |             stats.narInfoReadAverted++; | ||||||
|  |             return *res != 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return isValidPathUncached(storePath); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ref<const ValidPathInfo> Store::queryPathInfo(const Path & storePath) | ||||||
|  | { | ||||||
|  |     { | ||||||
|  |         auto state_(state.lock()); | ||||||
|  |         auto res = state_->pathInfoCache.get(storePath); | ||||||
|  |         if (res) { | ||||||
|  |             stats.narInfoReadAverted++; | ||||||
|  |             if (!*res) | ||||||
|  |                 throw InvalidPath(format("path ‘%s’ is not valid") % storePath); | ||||||
|  |             return ref<ValidPathInfo>(*res); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto info = queryPathInfoUncached(storePath); | ||||||
|  | 
 | ||||||
|  |     { | ||||||
|  |         auto state_(state.lock()); | ||||||
|  |         state_->pathInfoCache.upsert(storePath, info); | ||||||
|  |         stats.pathInfoCacheSize = state_->pathInfoCache.size(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!info) { | ||||||
|  |         stats.narInfoMissing++; | ||||||
|  |         throw InvalidPath(format("path ‘%s’ is not valid") % storePath); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ref<ValidPathInfo>(info); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -243,19 +281,19 @@ string Store::makeValidityRegistration(const PathSet & paths, | ||||||
|     for (auto & i : paths) { |     for (auto & i : paths) { | ||||||
|         s += i + "\n"; |         s += i + "\n"; | ||||||
| 
 | 
 | ||||||
|         ValidPathInfo info = queryPathInfo(i); |         auto info = queryPathInfo(i); | ||||||
| 
 | 
 | ||||||
|         if (showHash) { |         if (showHash) { | ||||||
|             s += printHash(info.narHash) + "\n"; |             s += printHash(info->narHash) + "\n"; | ||||||
|             s += (format("%1%\n") % info.narSize).str(); |             s += (format("%1%\n") % info->narSize).str(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Path deriver = showDerivers ? info.deriver : ""; |         Path deriver = showDerivers ? info->deriver : ""; | ||||||
|         s += deriver + "\n"; |         s += deriver + "\n"; | ||||||
| 
 | 
 | ||||||
|         s += (format("%1%\n") % info.references.size()).str(); |         s += (format("%1%\n") % info->references.size()).str(); | ||||||
| 
 | 
 | ||||||
|         for (auto & j : info.references) |         for (auto & j : info->references) | ||||||
|             s += j + "\n"; |             s += j + "\n"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -263,6 +301,12 @@ string Store::makeValidityRegistration(const PathSet & paths, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | const Store::Stats & Store::getStats() | ||||||
|  | { | ||||||
|  |     return stats; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven) | ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven) | ||||||
| { | { | ||||||
|     ValidPathInfo info; |     ValidPathInfo info; | ||||||
|  |  | ||||||
|  | @ -3,11 +3,14 @@ | ||||||
| #include "hash.hh" | #include "hash.hh" | ||||||
| #include "serialise.hh" | #include "serialise.hh" | ||||||
| #include "crypto.hh" | #include "crypto.hh" | ||||||
|  | #include "lru-cache.hh" | ||||||
|  | #include "sync.hh" | ||||||
| 
 | 
 | ||||||
| #include <string> | #include <atomic> | ||||||
| #include <limits> | #include <limits> | ||||||
| #include <map> | #include <map> | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <string> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| namespace nix { | namespace nix { | ||||||
|  | @ -130,6 +133,8 @@ struct ValidPathInfo | ||||||
| 
 | 
 | ||||||
|     /* Verify a single signature. */ |     /* Verify a single signature. */ | ||||||
|     bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const; |     bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const; | ||||||
|  | 
 | ||||||
|  |     virtual ~ValidPathInfo() { } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef list<ValidPathInfo> ValidPathInfos; | typedef list<ValidPathInfo> ValidPathInfos; | ||||||
|  | @ -169,12 +174,27 @@ class FSAccessor; | ||||||
| 
 | 
 | ||||||
| class Store : public std::enable_shared_from_this<Store> | class Store : public std::enable_shared_from_this<Store> | ||||||
| { | { | ||||||
|  | protected: | ||||||
|  | 
 | ||||||
|  |     struct State | ||||||
|  |     { | ||||||
|  |         LRUCache<Path, std::shared_ptr<ValidPathInfo>> pathInfoCache{64 * 1024}; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Sync<State> state; | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|     virtual ~Store() { } |     virtual ~Store() { } | ||||||
| 
 | 
 | ||||||
|     /* Check whether a path is valid. */ |     /* Check whether a path is valid. */ | ||||||
|     virtual bool isValidPath(const Path & path) = 0; |     bool isValidPath(const Path & path); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 
 | ||||||
|  |     virtual bool isValidPathUncached(const Path & path) = 0; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
| 
 | 
 | ||||||
|     /* Query which of the given paths is valid. */ |     /* Query which of the given paths is valid. */ | ||||||
|     virtual PathSet queryValidPaths(const PathSet & paths) = 0; |     virtual PathSet queryValidPaths(const PathSet & paths) = 0; | ||||||
|  | @ -183,24 +203,19 @@ public: | ||||||
|     virtual PathSet queryAllValidPaths() = 0; |     virtual PathSet queryAllValidPaths() = 0; | ||||||
| 
 | 
 | ||||||
|     /* Query information about a valid path. */ |     /* Query information about a valid path. */ | ||||||
|     virtual ValidPathInfo queryPathInfo(const Path & path) = 0; |     ref<const ValidPathInfo> queryPathInfo(const Path & path); | ||||||
| 
 | 
 | ||||||
|     /* Query the hash of a valid path. */ | protected: | ||||||
|     virtual Hash queryPathHash(const Path & path) = 0; |  | ||||||
| 
 | 
 | ||||||
|     /* Query the set of outgoing FS references for a store path. The
 |     virtual std::shared_ptr<ValidPathInfo> queryPathInfoUncached(const Path & path) = 0; | ||||||
|        result is not cleared. */ | 
 | ||||||
|     virtual void queryReferences(const Path & path, PathSet & references); | public: | ||||||
| 
 | 
 | ||||||
|     /* Queries the set of incoming FS references for a store path.
 |     /* Queries the set of incoming FS references for a store path.
 | ||||||
|        The result is not cleared. */ |        The result is not cleared. */ | ||||||
|     virtual void queryReferrers(const Path & path, |     virtual void queryReferrers(const Path & path, | ||||||
|         PathSet & referrers) = 0; |         PathSet & referrers) = 0; | ||||||
| 
 | 
 | ||||||
|     /* Query the deriver of a store path.  Return the empty string if
 |  | ||||||
|        no deriver has been set. */ |  | ||||||
|     virtual Path queryDeriver(const Path & path) = 0; |  | ||||||
| 
 |  | ||||||
|     /* Return all currently valid derivations that have `path' as an
 |     /* Return all currently valid derivations that have `path' as an
 | ||||||
|        output.  (Note that the result of `queryDeriver()' is the |        output.  (Note that the result of `queryDeriver()' is the | ||||||
|        derivation that was actually used to produce `path', which may |        derivation that was actually used to produce `path', which may | ||||||
|  | @ -373,6 +388,29 @@ public: | ||||||
|        relation.  If p refers to q, then p preceeds q in this list. */ |        relation.  If p refers to q, then p preceeds q in this list. */ | ||||||
|     Paths topoSortPaths(const PathSet & paths); |     Paths topoSortPaths(const PathSet & paths); | ||||||
| 
 | 
 | ||||||
|  |     struct Stats | ||||||
|  |     { | ||||||
|  |         std::atomic<uint64_t> narInfoRead{0}; | ||||||
|  |         std::atomic<uint64_t> narInfoReadAverted{0}; | ||||||
|  |         std::atomic<uint64_t> narInfoMissing{0}; | ||||||
|  |         std::atomic<uint64_t> narInfoWrite{0}; | ||||||
|  |         std::atomic<uint64_t> pathInfoCacheSize{0}; | ||||||
|  |         std::atomic<uint64_t> narRead{0}; | ||||||
|  |         std::atomic<uint64_t> narReadBytes{0}; | ||||||
|  |         std::atomic<uint64_t> narReadCompressedBytes{0}; | ||||||
|  |         std::atomic<uint64_t> narWrite{0}; | ||||||
|  |         std::atomic<uint64_t> narWriteAverted{0}; | ||||||
|  |         std::atomic<uint64_t> narWriteBytes{0}; | ||||||
|  |         std::atomic<uint64_t> narWriteCompressedBytes{0}; | ||||||
|  |         std::atomic<uint64_t> narWriteCompressionTimeMs{0}; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const Stats & getStats(); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 
 | ||||||
|  |     Stats stats; | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,8 +14,8 @@ namespace nix { | ||||||
| typedef enum { | typedef enum { | ||||||
|     wopIsValidPath = 1, |     wopIsValidPath = 1, | ||||||
|     wopHasSubstitutes = 3, |     wopHasSubstitutes = 3, | ||||||
|     wopQueryPathHash = 4, |     wopQueryPathHash = 4, // obsolete
 | ||||||
|     wopQueryReferences = 5, |     wopQueryReferences = 5, // obsolete
 | ||||||
|     wopQueryReferrers = 6, |     wopQueryReferrers = 6, | ||||||
|     wopAddToStore = 7, |     wopAddToStore = 7, | ||||||
|     wopAddTextToStore = 8, |     wopAddTextToStore = 8, | ||||||
|  | @ -26,7 +26,7 @@ typedef enum { | ||||||
|     wopSyncWithGC = 13, |     wopSyncWithGC = 13, | ||||||
|     wopFindRoots = 14, |     wopFindRoots = 14, | ||||||
|     wopExportPath = 16, |     wopExportPath = 16, | ||||||
|     wopQueryDeriver = 18, |     wopQueryDeriver = 18, // obsolete
 | ||||||
|     wopSetOptions = 19, |     wopSetOptions = 19, | ||||||
|     wopCollectGarbage = 20, |     wopCollectGarbage = 20, | ||||||
|     wopQuerySubstitutablePathInfo = 21, |     wopQuerySubstitutablePathInfo = 21, | ||||||
|  |  | ||||||
|  | @ -79,6 +79,12 @@ public: | ||||||
|     { |     { | ||||||
|         return data.size(); |         return data.size(); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     void clear() | ||||||
|  |     { | ||||||
|  |         data.clear(); | ||||||
|  |         lru.clear(); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -43,6 +43,12 @@ public: | ||||||
|         return p; |         return p; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     template<typename T2> | ||||||
|  |     ref<T2> cast() | ||||||
|  |     { | ||||||
|  |         return ref<T2>(std::dynamic_pointer_cast<T2>(p)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     template<typename T2> |     template<typename T2> | ||||||
|     operator ref<T2> () |     operator ref<T2> () | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -199,7 +199,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe | ||||||
|     case wopQueryPathHash: { |     case wopQueryPathHash: { | ||||||
|         Path path = readStorePath(from); |         Path path = readStorePath(from); | ||||||
|         startWork(); |         startWork(); | ||||||
|         Hash hash = store->queryPathHash(path); |         auto hash = store->queryPathInfo(path)->narHash; | ||||||
|         stopWork(); |         stopWork(); | ||||||
|         to << printHash(hash); |         to << printHash(hash); | ||||||
|         break; |         break; | ||||||
|  | @ -213,7 +213,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe | ||||||
|         startWork(); |         startWork(); | ||||||
|         PathSet paths; |         PathSet paths; | ||||||
|         if (op == wopQueryReferences) |         if (op == wopQueryReferences) | ||||||
|             store->queryReferences(path, paths); |             paths = store->queryPathInfo(path)->references; | ||||||
|         else if (op == wopQueryReferrers) |         else if (op == wopQueryReferrers) | ||||||
|             store->queryReferrers(path, paths); |             store->queryReferrers(path, paths); | ||||||
|         else if (op == wopQueryValidDerivers) |         else if (op == wopQueryValidDerivers) | ||||||
|  | @ -237,7 +237,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe | ||||||
|     case wopQueryDeriver: { |     case wopQueryDeriver: { | ||||||
|         Path path = readStorePath(from); |         Path path = readStorePath(from); | ||||||
|         startWork(); |         startWork(); | ||||||
|         Path deriver = store->queryDeriver(path); |         auto deriver = store->queryPathInfo(path)->deriver; | ||||||
|         stopWork(); |         stopWork(); | ||||||
|         to << deriver; |         to << deriver; | ||||||
|         break; |         break; | ||||||
|  | @ -496,13 +496,13 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe | ||||||
|     case wopQueryPathInfo: { |     case wopQueryPathInfo: { | ||||||
|         Path path = readStorePath(from); |         Path path = readStorePath(from); | ||||||
|         startWork(); |         startWork(); | ||||||
|         ValidPathInfo info = store->queryPathInfo(path); |         auto info = store->queryPathInfo(path); | ||||||
|         stopWork(); |         stopWork(); | ||||||
|         to << info.deriver << printHash(info.narHash) << info.references |         to << info->deriver << printHash(info->narHash) << info->references | ||||||
|            << info.registrationTime << info.narSize; |            << info->registrationTime << info->narSize; | ||||||
|         if (GET_PROTOCOL_MINOR(clientVersion) >= 16) { |         if (GET_PROTOCOL_MINOR(clientVersion) >= 16) { | ||||||
|             to << info.ultimate |             to << info->ultimate | ||||||
|                << info.sigs; |                << info->sigs; | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -110,19 +110,13 @@ void printDotGraph(ref<Store> store, const PathSet & roots) | ||||||
| 
 | 
 | ||||||
|         cout << makeNode(path, symbolicName(path), "#ff0000"); |         cout << makeNode(path, symbolicName(path), "#ff0000"); | ||||||
| 
 | 
 | ||||||
|         PathSet references; |         for (auto & p : store->queryPathInfo(path)->references) { | ||||||
|         store->queryReferences(path, references); |             if (p != path) { | ||||||
| 
 |                 workList.insert(p); | ||||||
|         for (PathSet::iterator i = references.begin(); |                 cout << makeEdge(p, path); | ||||||
|              i != references.end(); ++i) |  | ||||||
|         { |  | ||||||
|             if (*i != path) { |  | ||||||
|                 workList.insert(*i); |  | ||||||
|                 cout << makeEdge(*i, path); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| #if 0 | #if 0 | ||||||
|         StoreExpr ne = storeExprFromPath(path); |         StoreExpr ne = storeExprFromPath(path); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -51,7 +51,7 @@ ref<LocalStore> ensureLocalStore() | ||||||
| static Path useDeriver(Path path) | static Path useDeriver(Path path) | ||||||
| { | { | ||||||
|     if (isDerivation(path)) return path; |     if (isDerivation(path)) return path; | ||||||
|     Path drvPath = store->queryDeriver(path); |     Path drvPath = store->queryPathInfo(path)->deriver; | ||||||
|     if (drvPath == "") |     if (drvPath == "") | ||||||
|         throw Error(format("deriver of path ‘%1%’ is not known") % path); |         throw Error(format("deriver of path ‘%1%’ is not known") % path); | ||||||
|     return drvPath; |     return drvPath; | ||||||
|  | @ -247,8 +247,7 @@ static void printTree(const Path & path, | ||||||
| 
 | 
 | ||||||
|     cout << format("%1%%2%\n") % firstPad % path; |     cout << format("%1%%2%\n") % firstPad % path; | ||||||
| 
 | 
 | ||||||
|     PathSet references; |     auto references = store->queryPathInfo(path)->references; | ||||||
|     store->queryReferences(path, references); |  | ||||||
| 
 | 
 | ||||||
|     /* Topologically sort under the relation A < B iff A \in
 |     /* Topologically sort under the relation A < B iff A \in
 | ||||||
|        closure(B).  That is, if derivation A is an (possibly indirect) |        closure(B).  That is, if derivation A is an (possibly indirect) | ||||||
|  | @ -335,7 +334,10 @@ static void opQuery(Strings opFlags, Strings opArgs) | ||||||
|                 PathSet ps = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise); |                 PathSet ps = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise); | ||||||
|                 for (auto & j : ps) { |                 for (auto & j : ps) { | ||||||
|                     if (query == qRequisites) store->computeFSClosure(j, paths, false, includeOutputs); |                     if (query == qRequisites) store->computeFSClosure(j, paths, false, includeOutputs); | ||||||
|                     else if (query == qReferences) store->queryReferences(j, paths); |                     else if (query == qReferences) { | ||||||
|  |                         for (auto & p : store->queryPathInfo(j)->references) | ||||||
|  |                             paths.insert(p); | ||||||
|  |                     } | ||||||
|                     else if (query == qReferrers) store->queryReferrers(j, paths); |                     else if (query == qReferrers) store->queryReferrers(j, paths); | ||||||
|                     else if (query == qReferrersClosure) store->computeFSClosure(j, paths, true); |                     else if (query == qReferrersClosure) store->computeFSClosure(j, paths, true); | ||||||
|                 } |                 } | ||||||
|  | @ -349,7 +351,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | ||||||
| 
 | 
 | ||||||
|         case qDeriver: |         case qDeriver: | ||||||
|             for (auto & i : opArgs) { |             for (auto & i : opArgs) { | ||||||
|                 Path deriver = store->queryDeriver(followLinksToStorePath(i)); |                 Path deriver = store->queryPathInfo(followLinksToStorePath(i))->deriver; | ||||||
|                 cout << format("%1%\n") % |                 cout << format("%1%\n") % | ||||||
|                     (deriver == "" ? "unknown-deriver" : deriver); |                     (deriver == "" ? "unknown-deriver" : deriver); | ||||||
|             } |             } | ||||||
|  | @ -372,12 +374,12 @@ static void opQuery(Strings opFlags, Strings opArgs) | ||||||
|             for (auto & i : opArgs) { |             for (auto & i : opArgs) { | ||||||
|                 PathSet paths = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise); |                 PathSet paths = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise); | ||||||
|                 for (auto & j : paths) { |                 for (auto & j : paths) { | ||||||
|                     ValidPathInfo info = store->queryPathInfo(j); |                     auto info = store->queryPathInfo(j); | ||||||
|                     if (query == qHash) { |                     if (query == qHash) { | ||||||
|                         assert(info.narHash.type == htSHA256); |                         assert(info->narHash.type == htSHA256); | ||||||
|                         cout << format("sha256:%1%\n") % printHash32(info.narHash); |                         cout << format("sha256:%1%\n") % printHash32(info->narHash); | ||||||
|                     } else if (query == qSize) |                     } else if (query == qSize) | ||||||
|                         cout << format("%1%\n") % info.narSize; |                         cout << format("%1%\n") % info->narSize; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|  | @ -782,14 +784,14 @@ static void opVerifyPath(Strings opFlags, Strings opArgs) | ||||||
|     for (auto & i : opArgs) { |     for (auto & i : opArgs) { | ||||||
|         Path path = followLinksToStorePath(i); |         Path path = followLinksToStorePath(i); | ||||||
|         printMsg(lvlTalkative, format("checking path ‘%1%’...") % path); |         printMsg(lvlTalkative, format("checking path ‘%1%’...") % path); | ||||||
|         ValidPathInfo info = store->queryPathInfo(path); |         auto info = store->queryPathInfo(path); | ||||||
|         HashSink sink(info.narHash.type); |         HashSink sink(info->narHash.type); | ||||||
|         store->narFromPath(path, sink); |         store->narFromPath(path, sink); | ||||||
|         auto current = sink.finish(); |         auto current = sink.finish(); | ||||||
|         if (current.first != info.narHash) { |         if (current.first != info->narHash) { | ||||||
|             printMsg(lvlError, |             printMsg(lvlError, | ||||||
|                 format("path ‘%1%’ was modified! expected hash ‘%2%’, got ‘%3%’") |                 format("path ‘%1%’ was modified! expected hash ‘%2%’, got ‘%3%’") | ||||||
|                 % path % printHash(info.narHash) % printHash(current.first)); |                 % path % printHash(info->narHash) % printHash(current.first)); | ||||||
|             status = 1; |             status = 1; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -901,13 +903,14 @@ static void opServe(Strings opFlags, Strings opArgs) | ||||||
|                 PathSet paths = readStorePaths<PathSet>(in); |                 PathSet paths = readStorePaths<PathSet>(in); | ||||||
|                 // !!! Maybe we want a queryPathInfos?
 |                 // !!! Maybe we want a queryPathInfos?
 | ||||||
|                 for (auto & i : paths) { |                 for (auto & i : paths) { | ||||||
|                     if (!store->isValidPath(i)) |                     try { | ||||||
|                         continue; |                         auto info = store->queryPathInfo(i); | ||||||
|                     ValidPathInfo info = store->queryPathInfo(i); |                         out << info->path << info->deriver << info->references; | ||||||
|                     out << info.path << info.deriver << info.references; |                         // !!! Maybe we want compression?
 | ||||||
|                     // !!! Maybe we want compression?
 |                         out << info->narSize // downloadSize
 | ||||||
|                     out << info.narSize // downloadSize
 |                             << info->narSize; | ||||||
|                         << info.narSize; |                     } catch (InvalidPath &) { | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|                 out << ""; |                 out << ""; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|  | @ -50,15 +50,10 @@ void printXmlGraph(ref<Store> store, const PathSet & roots) | ||||||
| 
 | 
 | ||||||
|         cout << makeNode(path); |         cout << makeNode(path); | ||||||
| 
 | 
 | ||||||
|         PathSet references; |         for (auto & p : store->queryPathInfo(path)->references) { | ||||||
|         store->queryReferences(path, references); |             if (p != path) { | ||||||
| 
 |                 workList.insert(p); | ||||||
|         for (PathSet::iterator i = references.begin(); |                 cout << makeEdge(p, path); | ||||||
|              i != references.end(); ++i) |  | ||||||
|         { |  | ||||||
|             if (*i != path) { |  | ||||||
|                 workList.insert(*i); |  | ||||||
|                 cout << makeEdge(*i, path); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -64,19 +64,21 @@ struct CmdCopySigs : StorePathsCommand | ||||||
|             StringSet newSigs; |             StringSet newSigs; | ||||||
| 
 | 
 | ||||||
|             for (auto & store2 : substituters) { |             for (auto & store2 : substituters) { | ||||||
|                 if (!store2->isValidPath(storePath)) continue; |                 try { | ||||||
|                 auto info2 = store2->queryPathInfo(storePath); |                     auto info2 = store2->queryPathInfo(storePath); | ||||||
| 
 | 
 | ||||||
|                 /* Don't import signatures that don't match this
 |                     /* Don't import signatures that don't match this
 | ||||||
|                    binary. */ |                        binary. */ | ||||||
|                 if (info.narHash != info2.narHash || |                     if (info->narHash != info2->narHash || | ||||||
|                     info.narSize != info2.narSize || |                         info->narSize != info2->narSize || | ||||||
|                     info.references != info2.references) |                         info->references != info2->references) | ||||||
|                     continue; |                         continue; | ||||||
| 
 | 
 | ||||||
|                 for (auto & sig : info2.sigs) |                     for (auto & sig : info2->sigs) | ||||||
|                     if (!info.sigs.count(sig)) |                         if (!info->sigs.count(sig)) | ||||||
|                         newSigs.insert(sig); |                             newSigs.insert(sig); | ||||||
|  |                 } catch (InvalidPath &) { | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (!newSigs.empty()) { |             if (!newSigs.empty()) { | ||||||
|  | @ -122,8 +124,8 @@ struct CmdQueryPathSigs : StorePathsCommand | ||||||
|         for (auto & storePath : storePaths) { |         for (auto & storePath : storePaths) { | ||||||
|             auto info = store->queryPathInfo(storePath); |             auto info = store->queryPathInfo(storePath); | ||||||
|             std::cout << storePath << " "; |             std::cout << storePath << " "; | ||||||
|             if (info.ultimate) std::cout << "ultimate "; |             if (info->ultimate) std::cout << "ultimate "; | ||||||
|             for (auto & sig : info.sigs) |             for (auto & sig : info->sigs) | ||||||
|                 std::cout << sig << " "; |                 std::cout << sig << " "; | ||||||
|             std::cout << "\n"; |             std::cout << "\n"; | ||||||
|         } |         } | ||||||
|  | @ -163,12 +165,12 @@ struct CmdSignPaths : StorePathsCommand | ||||||
|         for (auto & storePath : storePaths) { |         for (auto & storePath : storePaths) { | ||||||
|             auto info = store->queryPathInfo(storePath); |             auto info = store->queryPathInfo(storePath); | ||||||
| 
 | 
 | ||||||
|             auto info2(info); |             auto info2(*info); | ||||||
|             info2.sigs.clear(); |             info2.sigs.clear(); | ||||||
|             info2.sign(secretKey); |             info2.sign(secretKey); | ||||||
|             assert(!info2.sigs.empty()); |             assert(!info2.sigs.empty()); | ||||||
| 
 | 
 | ||||||
|             if (!info.sigs.count(*info2.sigs.begin())) { |             if (!info->sigs.count(*info2.sigs.begin())) { | ||||||
|                 store->addSignatures(storePath, info2.sigs); |                 store->addSignatures(storePath, info2.sigs); | ||||||
|                 added++; |                 added++; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -83,16 +83,16 @@ struct CmdVerify : StorePathsCommand | ||||||
| 
 | 
 | ||||||
|                 if (!noContents) { |                 if (!noContents) { | ||||||
| 
 | 
 | ||||||
|                     HashSink sink(info.narHash.type); |                     HashSink sink(info->narHash.type); | ||||||
|                     store->narFromPath(storePath, sink); |                     store->narFromPath(storePath, sink); | ||||||
| 
 | 
 | ||||||
|                     auto hash = sink.finish(); |                     auto hash = sink.finish(); | ||||||
| 
 | 
 | ||||||
|                     if (hash.first != info.narHash) { |                     if (hash.first != info->narHash) { | ||||||
|                         corrupted = 1; |                         corrupted = 1; | ||||||
|                         printMsg(lvlError, |                         printMsg(lvlError, | ||||||
|                             format("path ‘%s’ was modified! expected hash ‘%s’, got ‘%s’") |                             format("path ‘%s’ was modified! expected hash ‘%s’, got ‘%s’") | ||||||
|                             % storePath % printHash(info.narHash) % printHash(hash.first)); |                             % storePath % printHash(info->narHash) % printHash(hash.first)); | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                 } |                 } | ||||||
|  | @ -101,7 +101,7 @@ struct CmdVerify : StorePathsCommand | ||||||
| 
 | 
 | ||||||
|                     bool good = false; |                     bool good = false; | ||||||
| 
 | 
 | ||||||
|                     if (info.ultimate && !sigsNeeded) |                     if (info->ultimate && !sigsNeeded) | ||||||
|                         good = true; |                         good = true; | ||||||
| 
 | 
 | ||||||
|                     else { |                     else { | ||||||
|  | @ -114,18 +114,18 @@ struct CmdVerify : StorePathsCommand | ||||||
|                             for (auto sig : sigs) { |                             for (auto sig : sigs) { | ||||||
|                                 if (sigsSeen.count(sig)) continue; |                                 if (sigsSeen.count(sig)) continue; | ||||||
|                                 sigsSeen.insert(sig); |                                 sigsSeen.insert(sig); | ||||||
|                                 if (info.checkSignature(publicKeys, sig)) |                                 if (info->checkSignature(publicKeys, sig)) | ||||||
|                                     validSigs++; |                                     validSigs++; | ||||||
|                             } |                             } | ||||||
|                         }; |                         }; | ||||||
| 
 | 
 | ||||||
|                         doSigs(info.sigs); |                         doSigs(info->sigs); | ||||||
| 
 | 
 | ||||||
|                         for (auto & store2 : substituters) { |                         for (auto & store2 : substituters) { | ||||||
|                             if (validSigs >= actualSigsNeeded) break; |                             if (validSigs >= actualSigsNeeded) break; | ||||||
|                             try { |                             try { | ||||||
|                                 if (!store2->isValidPath(storePath)) continue; |                                 doSigs(store2->queryPathInfo(storePath)->sigs); | ||||||
|                                 doSigs(store2->queryPathInfo(storePath).sigs); |                             } catch (InvalidPath &) { | ||||||
|                             } catch (Error & e) { |                             } catch (Error & e) { | ||||||
|                                 printMsg(lvlError, format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what()); |                                 printMsg(lvlError, format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what()); | ||||||
|                             } |                             } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue