Merge pull request #6220 from obsidiansystems/log-store

Factor out a `LogStore` interface
This commit is contained in:
Théophane Hufschmitt 2022-03-14 10:51:01 +01:00 committed by GitHub
commit 0e86ebf461
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 114 additions and 46 deletions

View file

@ -2,6 +2,7 @@
#include "crypto.hh" #include "crypto.hh"
#include "store-api.hh" #include "store-api.hh"
#include "log-store.hh"
#include "pool.hh" #include "pool.hh"
@ -28,7 +29,9 @@ struct BinaryCacheStoreConfig : virtual StoreConfig
"other than -1 which we reserve to indicate Nix defaults should be used"}; "other than -1 which we reserve to indicate Nix defaults should be used"};
}; };
class BinaryCacheStore : public virtual BinaryCacheStoreConfig, public virtual Store class BinaryCacheStore : public virtual BinaryCacheStoreConfig,
public virtual Store,
public virtual LogStore
{ {
private: private:

View file

@ -1340,6 +1340,12 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
next->queryMissing(allowed, willBuild, willSubstitute, next->queryMissing(allowed, willBuild, willSubstitute,
unknown, downloadSize, narSize); unknown, downloadSize, narSize);
} }
virtual std::optional<std::string> getBuildLog(const StorePath & path) override
{ return std::nullopt; }
virtual void addBuildLog(const StorePath & path, std::string_view log) override
{ unsupported("addBuildLog"); }
}; };

View file

@ -3,7 +3,9 @@
#include "worker-protocol.hh" #include "worker-protocol.hh"
#include "build-result.hh" #include "build-result.hh"
#include "store-api.hh" #include "store-api.hh"
#include "store-cast.hh"
#include "gc-store.hh" #include "gc-store.hh"
#include "log-store.hh"
#include "path-with-outputs.hh" #include "path-with-outputs.hh"
#include "finally.hh" #include "finally.hh"
#include "archive.hh" #include "archive.hh"
@ -645,7 +647,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
Path path = absPath(readString(from)); Path path = absPath(readString(from));
logger->startWork(); logger->startWork();
auto & gcStore = requireGcStore(*store); auto & gcStore = require<GcStore>(*store);
gcStore.addIndirectRoot(path); gcStore.addIndirectRoot(path);
logger->stopWork(); logger->stopWork();
@ -663,7 +665,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopFindRoots: { case wopFindRoots: {
logger->startWork(); logger->startWork();
auto & gcStore = requireGcStore(*store); auto & gcStore = require<GcStore>(*store);
Roots roots = gcStore.findRoots(!trusted); Roots roots = gcStore.findRoots(!trusted);
logger->stopWork(); logger->stopWork();
@ -695,7 +697,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
logger->startWork(); logger->startWork();
if (options.ignoreLiveness) if (options.ignoreLiveness)
throw Error("you are not allowed to ignore liveness"); throw Error("you are not allowed to ignore liveness");
auto & gcStore = requireGcStore(*store); auto & gcStore = require<GcStore>(*store);
gcStore.collectGarbage(options, results); gcStore.collectGarbage(options, results);
logger->stopWork(); logger->stopWork();
@ -953,11 +955,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
logger->startWork(); logger->startWork();
if (!trusted) if (!trusted)
throw Error("you are not privileged to add logs"); throw Error("you are not privileged to add logs");
auto & logStore = require<LogStore>(*store);
{ {
FramedSource source(from); FramedSource source(from);
StringSink sink; StringSink sink;
source.drainInto(sink); source.drainInto(sink);
store->addBuildLog(path, sink.s); logStore.addBuildLog(path, sink.s);
} }
logger->stopWork(); logger->stopWork();
to << 1; to << 1;

View file

@ -1,13 +0,0 @@
#include "gc-store.hh"
namespace nix {
GcStore & requireGcStore(Store & store)
{
auto * gcStore = dynamic_cast<GcStore *>(&store);
if (!gcStore)
throw UsageError("Garbage collection not supported by this store");
return *gcStore;
}
}

View file

@ -61,6 +61,8 @@ struct GCResults
struct GcStore : public virtual Store struct GcStore : public virtual Store
{ {
inline static std::string operationName = "Garbage collection";
/* Add an indirect root, which is merely a symlink to `path' from /* Add an indirect root, which is merely a symlink to `path' from
/nix/var/nix/gcroots/auto/<hash of `path'>. `path' is supposed /nix/var/nix/gcroots/auto/<hash of `path'>. `path' is supposed
to be a symlink to a store path. The garbage collector will to be a symlink to a store path. The garbage collector will
@ -79,6 +81,4 @@ struct GcStore : public virtual Store
virtual void collectGarbage(const GCOptions & options, GCResults & results) = 0; virtual void collectGarbage(const GCOptions & options, GCResults & results) = 0;
}; };
GcStore & requireGcStore(Store & store);
} }

View file

@ -2,6 +2,7 @@
#include "store-api.hh" #include "store-api.hh"
#include "gc-store.hh" #include "gc-store.hh"
#include "log-store.hh"
namespace nix { namespace nix {
@ -24,7 +25,10 @@ struct LocalFSStoreConfig : virtual StoreConfig
"physical path to the Nix store"}; "physical path to the Nix store"};
}; };
class LocalFSStore : public virtual LocalFSStoreConfig, public virtual Store, virtual GcStore class LocalFSStore : public virtual LocalFSStoreConfig,
public virtual Store,
public virtual GcStore,
public virtual LogStore
{ {
public: public:

21
src/libstore/log-store.hh Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include "store-api.hh"
namespace nix {
struct LogStore : public virtual Store
{
inline static std::string operationName = "Build log storage and retrieval";
/* Return the build log of the specified store path, if available,
or null otherwise. */
virtual std::optional<std::string> getBuildLog(const StorePath & path) = 0;
virtual void addBuildLog(const StorePath & path, std::string_view log) = 0;
static LogStore & require(Store & store);
};
}

View file

@ -5,6 +5,7 @@
#include "store-api.hh" #include "store-api.hh"
#include "gc-store.hh" #include "gc-store.hh"
#include "log-store.hh"
namespace nix { namespace nix {
@ -30,7 +31,10 @@ struct RemoteStoreConfig : virtual StoreConfig
/* FIXME: RemoteStore is a misnomer - should be something like /* FIXME: RemoteStore is a misnomer - should be something like
DaemonStore. */ DaemonStore. */
class RemoteStore : public virtual RemoteStoreConfig, public virtual Store, public virtual GcStore class RemoteStore : public virtual RemoteStoreConfig,
public virtual Store,
public virtual GcStore,
public virtual LogStore
{ {
public: public:

View file

@ -52,6 +52,10 @@ public:
bool sameMachine() override bool sameMachine() override
{ return false; } { return false; }
// FIXME extend daemon protocol, move implementation to RemoteStore
std::optional<std::string> getBuildLog(const StorePath & path) override
{ unsupported("getBuildLog"); }
private: private:
struct Connection : RemoteStore::Connection struct Connection : RemoteStore::Connection

View file

@ -605,14 +605,6 @@ public:
*/ */
StorePathSet exportReferences(const StorePathSet & storePaths, const StorePathSet & inputPaths); StorePathSet exportReferences(const StorePathSet & storePaths, const StorePathSet & inputPaths);
/* Return the build log of the specified store path, if available,
or null otherwise. */
virtual std::optional<std::string> getBuildLog(const StorePath & path)
{ return std::nullopt; }
virtual void addBuildLog(const StorePath & path, std::string_view log)
{ unsupported("addBuildLog"); }
/* Hack to allow long-running processes like hydra-queue-runner to /* Hack to allow long-running processes like hydra-queue-runner to
occasionally flush their path info cache. */ occasionally flush their path info cache. */
void clearPathInfoCache() void clearPathInfoCache()

View file

@ -0,0 +1,16 @@
#pragma once
#include "store-api.hh"
namespace nix {
template<typename T>
T & require(Store & store)
{
auto * castedStore = dynamic_cast<T *>(&store);
if (!castedStore)
throw UsageError("%s not supported by store '%s'", T::operationName, store.getUri());
return *castedStore;
}
}

View file

@ -1,4 +1,5 @@
#include "store-api.hh" #include "store-api.hh"
#include "store-cast.hh"
#include "gc-store.hh" #include "gc-store.hh"
#include "profiles.hh" #include "profiles.hh"
#include "shared.hh" #include "shared.hh"
@ -81,7 +82,7 @@ static int main_nix_collect_garbage(int argc, char * * argv)
// Run the actual garbage collector. // Run the actual garbage collector.
if (!dryRun) { if (!dryRun) {
auto store = openStore(); auto store = openStore();
auto & gcStore = requireGcStore(*store); auto & gcStore = require<GcStore>(*store);
options.action = GCOptions::gcDeleteDead; options.action = GCOptions::gcDeleteDead;
GCResults results; GCResults results;
PrintFreed freed(true, results); PrintFreed freed(true, results);

View file

@ -3,7 +3,9 @@
#include "dotgraph.hh" #include "dotgraph.hh"
#include "globals.hh" #include "globals.hh"
#include "build-result.hh" #include "build-result.hh"
#include "store-cast.hh"
#include "gc-store.hh" #include "gc-store.hh"
#include "log-store.hh"
#include "local-store.hh" #include "local-store.hh"
#include "monitor-fd.hh" #include "monitor-fd.hh"
#include "serve-protocol.hh" #include "serve-protocol.hh"
@ -429,7 +431,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
store->computeFSClosure( store->computeFSClosure(
args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations); args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations);
auto & gcStore = requireGcStore(*store); auto & gcStore = require<GcStore>(*store);
Roots roots = gcStore.findRoots(false); Roots roots = gcStore.findRoots(false);
for (auto & [target, links] : roots) for (auto & [target, links] : roots)
if (referrers.find(target) != referrers.end()) if (referrers.find(target) != referrers.end())
@ -474,13 +476,15 @@ static void opReadLog(Strings opFlags, Strings opArgs)
{ {
if (!opFlags.empty()) throw UsageError("unknown flag"); if (!opFlags.empty()) throw UsageError("unknown flag");
auto & logStore = require<LogStore>(*store);
RunPager pager; RunPager pager;
for (auto & i : opArgs) { for (auto & i : opArgs) {
auto path = store->followLinksToStorePath(i); auto path = logStore.followLinksToStorePath(i);
auto log = store->getBuildLog(path); auto log = logStore.getBuildLog(path);
if (!log) if (!log)
throw Error("build log of derivation '%s' is not available", store->printStorePath(path)); throw Error("build log of derivation '%s' is not available", logStore.printStorePath(path));
std::cout << *log; std::cout << *log;
} }
} }
@ -590,7 +594,7 @@ static void opGC(Strings opFlags, Strings opArgs)
if (!opArgs.empty()) throw UsageError("no arguments expected"); if (!opArgs.empty()) throw UsageError("no arguments expected");
auto & gcStore = requireGcStore(*store); auto & gcStore = require<GcStore>(*store);
if (printRoots) { if (printRoots) {
Roots roots = gcStore.findRoots(false); Roots roots = gcStore.findRoots(false);
@ -629,7 +633,7 @@ static void opDelete(Strings opFlags, Strings opArgs)
for (auto & i : opArgs) for (auto & i : opArgs)
options.pathsToDelete.insert(store->followLinksToStorePath(i)); options.pathsToDelete.insert(store->followLinksToStorePath(i));
auto & gcStore = requireGcStore(*store); auto & gcStore = require<GcStore>(*store);
GCResults results; GCResults results;
PrintFreed freed(true, results); PrintFreed freed(true, results);

View file

@ -2,6 +2,7 @@
#include "common-args.hh" #include "common-args.hh"
#include "shared.hh" #include "shared.hh"
#include "store-api.hh" #include "store-api.hh"
#include "log-store.hh"
#include "progress-bar.hh" #include "progress-bar.hh"
using namespace nix; using namespace nix;
@ -34,17 +35,24 @@ struct CmdLog : InstallableCommand
RunPager pager; RunPager pager;
for (auto & sub : subs) { for (auto & sub : subs) {
auto * logSubP = dynamic_cast<LogStore *>(&*sub);
if (!logSubP) {
printInfo("Skipped '%s' which does not support retrieving build logs", sub->getUri());
continue;
}
auto & logSub = *logSubP;
auto log = std::visit(overloaded { auto log = std::visit(overloaded {
[&](const DerivedPath::Opaque & bo) { [&](const DerivedPath::Opaque & bo) {
return sub->getBuildLog(bo.path); return logSub.getBuildLog(bo.path);
}, },
[&](const DerivedPath::Built & bfd) { [&](const DerivedPath::Built & bfd) {
return sub->getBuildLog(bfd.drvPath); return logSub.getBuildLog(bfd.drvPath);
}, },
}, b.raw()); }, b.raw());
if (!log) continue; if (!log) continue;
stopProgressBar(); stopProgressBar();
printInfo("got build log for '%s' from '%s'", installable->what(), sub->getUri()); printInfo("got build log for '%s' from '%s'", installable->what(), logSub.getUri());
std::cout << *log; std::cout << *log;
return; return;
} }

View file

@ -25,6 +25,7 @@ extern "C" {
#include "eval-inline.hh" #include "eval-inline.hh"
#include "attr-path.hh" #include "attr-path.hh"
#include "store-api.hh" #include "store-api.hh"
#include "log-store.hh"
#include "common-eval-args.hh" #include "common-eval-args.hh"
#include "get-drvs.hh" #include "get-drvs.hh"
#include "derivations.hh" #include "derivations.hh"
@ -526,9 +527,16 @@ bool NixRepl::processLine(std::string line)
bool foundLog = false; bool foundLog = false;
RunPager pager; RunPager pager;
for (auto & sub : subs) { for (auto & sub : subs) {
auto log = sub->getBuildLog(drvPath); auto * logSubP = dynamic_cast<LogStore *>(&*sub);
if (!logSubP) {
printInfo("Skipped '%s' which does not support retrieving build logs", sub->getUri());
continue;
}
auto & logSub = *logSubP;
auto log = logSub.getBuildLog(drvPath);
if (log) { if (log) {
printInfo("got build log for '%s' from '%s'", drvPathRaw, sub->getUri()); printInfo("got build log for '%s' from '%s'", drvPathRaw, logSub.getUri());
logger->writeToStdout(*log); logger->writeToStdout(*log);
foundLog = true; foundLog = true;
break; break;

View file

@ -1,6 +1,8 @@
#include "command.hh" #include "command.hh"
#include "shared.hh" #include "shared.hh"
#include "store-api.hh" #include "store-api.hh"
#include "store-cast.hh"
#include "log-store.hh"
#include "sync.hh" #include "sync.hh"
#include "thread-pool.hh" #include "thread-pool.hh"
@ -26,7 +28,10 @@ struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand
void run(ref<Store> srcStore) override void run(ref<Store> srcStore) override
{ {
auto & srcLogStore = require<LogStore>(*srcStore);
auto dstStore = getDstStore(); auto dstStore = getDstStore();
auto & dstLogStore = require<LogStore>(*dstStore);
StorePathSet drvPaths; StorePathSet drvPaths;
@ -35,8 +40,8 @@ struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand
drvPaths.insert(drvPath); drvPaths.insert(drvPath);
for (auto & drvPath : drvPaths) { for (auto & drvPath : drvPaths) {
if (auto log = srcStore->getBuildLog(drvPath)) if (auto log = srcLogStore.getBuildLog(drvPath))
dstStore->addBuildLog(drvPath, *log); dstLogStore.addBuildLog(drvPath, *log);
else else
throw Error("build log for '%s' is not available", srcStore->printStorePath(drvPath)); throw Error("build log for '%s' is not available", srcStore->printStorePath(drvPath));
} }

View file

@ -2,6 +2,7 @@
#include "common-args.hh" #include "common-args.hh"
#include "shared.hh" #include "shared.hh"
#include "store-api.hh" #include "store-api.hh"
#include "store-cast.hh"
#include "gc-store.hh" #include "gc-store.hh"
using namespace nix; using namespace nix;
@ -33,7 +34,7 @@ struct CmdStoreDelete : StorePathsCommand
void run(ref<Store> store, std::vector<StorePath> && storePaths) override void run(ref<Store> store, std::vector<StorePath> && storePaths) override
{ {
auto & gcStore = requireGcStore(*store); auto & gcStore = require<GcStore>(*store);
for (auto & path : storePaths) for (auto & path : storePaths)
options.pathsToDelete.insert(path); options.pathsToDelete.insert(path);

View file

@ -2,6 +2,7 @@
#include "common-args.hh" #include "common-args.hh"
#include "shared.hh" #include "shared.hh"
#include "store-api.hh" #include "store-api.hh"
#include "store-cast.hh"
#include "gc-store.hh" #include "gc-store.hh"
using namespace nix; using namespace nix;
@ -34,7 +35,7 @@ struct CmdStoreGC : StoreCommand, MixDryRun
void run(ref<Store> store) override void run(ref<Store> store) override
{ {
auto & gcStore = requireGcStore(*store); auto & gcStore = require<GcStore>(*store);
options.action = dryRun ? GCOptions::gcReturnDead : GCOptions::gcDeleteDead; options.action = dryRun ? GCOptions::gcReturnDead : GCOptions::gcDeleteDead;
GCResults results; GCResults results;