Clean up store hierarchy with IndirectRootStore

See the API doc comments for details.
This commit is contained in:
John Ericson 2023-03-23 10:06:45 -04:00
parent 13269ba93b
commit 60d8dd7aea
12 changed files with 136 additions and 37 deletions

View file

@ -1,5 +1,5 @@
#include "local-derivation-goal.hh" #include "local-derivation-goal.hh"
#include "gc-store.hh" #include "indirect-root-store.hh"
#include "hook-instance.hh" #include "hook-instance.hh"
#include "worker.hh" #include "worker.hh"
#include "builtins.hh" #include "builtins.hh"
@ -1200,7 +1200,7 @@ struct RestrictedStoreConfig : virtual LocalFSStoreConfig
/* A wrapper around LocalStore that only allows building/querying of /* A wrapper around LocalStore that only allows building/querying of
paths that are in the input closures of the build or were added via paths that are in the input closures of the build or were added via
recursive Nix calls. */ recursive Nix calls. */
struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual LocalFSStore, public virtual GcStore struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual IndirectRootStore, public virtual GcStore
{ {
ref<LocalStore> next; ref<LocalStore> next;

View file

@ -7,6 +7,7 @@
#include "store-cast.hh" #include "store-cast.hh"
#include "gc-store.hh" #include "gc-store.hh"
#include "log-store.hh" #include "log-store.hh"
#include "indirect-root-store.hh"
#include "path-with-outputs.hh" #include "path-with-outputs.hh"
#include "finally.hh" #include "finally.hh"
#include "archive.hh" #include "archive.hh"
@ -675,8 +676,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
Path path = absPath(readString(from)); Path path = absPath(readString(from));
logger->startWork(); logger->startWork();
auto & gcStore = require<GcStore>(*store); auto & indirectRootStore = require<IndirectRootStore>(*store);
gcStore.addIndirectRoot(path); indirectRootStore.addIndirectRoot(path);
logger->stopWork(); logger->stopWork();
to << 1; to << 1;

View file

@ -71,19 +71,36 @@ struct GCResults
}; };
/**
* Mix-in class for \ref Store "stores" which expose a notion of garbage
* collection.
*
* Garbage collection will allow deleting paths which are not
* transitively "rooted".
*
* The notion of GC roots actually not part of this class.
*
* - The base `Store` class has `Store::addTempRoot()` because for a store
* that doesn't support garbage collection at all, a temporary GC root is
* safely implementable as no-op.
*
* @todo actually this is not so good because stores are *views*.
* Some views have only a no-op temp roots even though others to the
* same store allow triggering GC. For instance one can't add a root
* over ssh, but that doesn't prevent someone from gc-ing that store
* accesed via SSH locally).
*
* - The derived `LocalFSStore` class has `LocalFSStore::addPermRoot`,
* which is not part of this class because it relies on the notion of
* an ambient file system. There are stores (`ssh-ng://`, for one),
* that *do* support garbage collection but *don't* expose any file
* system, and `LocalFSStore::addPermRoot` thus does not make sense
* for them.
*/
struct GcStore : public virtual Store struct GcStore : public virtual Store
{ {
inline static std::string operationName = "Garbage collection"; inline static std::string operationName = "Garbage collection";
/**
* Add an indirect root, which is merely a symlink to `path` from
* `/nix/var/nix/gcroots/auto/<hash of path>`. `path` is supposed
* to be a symlink to a store path. The garbage collector will
* automatically remove the indirect root when it finds that
* `path` has disappeared.
*/
virtual void addIndirectRoot(const Path & path) = 0;
/** /**
* Find the roots of the garbage collector. Each root is a pair * Find the roots of the garbage collector. Each root is a pair
* `(link, storepath)` where `link` is the path of the symlink * `(link, storepath)` where `link` is the path of the symlink

View file

@ -1,7 +1,6 @@
#include "derivations.hh" #include "derivations.hh"
#include "globals.hh" #include "globals.hh"
#include "local-store.hh" #include "local-store.hh"
#include "local-fs-store.hh"
#include "finally.hh" #include "finally.hh"
#include <functional> #include <functional>
@ -50,7 +49,7 @@ void LocalStore::addIndirectRoot(const Path & path)
} }
Path LocalFSStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot) Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot)
{ {
Path gcRoot(canonPath(_gcRoot)); Path gcRoot(canonPath(_gcRoot));

View file

@ -0,0 +1,48 @@
#pragma once
///@file
#include "local-fs-store.hh"
namespace nix {
/**
* Mix-in class for implementing permanent roots as a pair of a direct
* (strong) reference and indirect weak reference to the first
* reference.
*
* See methods for details on the operations it represents.
*/
struct IndirectRootStore : public virtual LocalFSStore
{
inline static std::string operationName = "Indirect GC roots registration";
/**
* Implementation of `LocalFSStore::addPermRoot` where the permanent
* root is a pair of
*
* - The user-facing symlink which all implementations must create
*
* - An additional weak reference known as the "indirect root" that
* points to that symlink.
*
* The garbage collector will automatically remove the indirect root
* when it finds that the symlink has disappeared.
*
* The implementation of this method is concrete, but it delegates
* to `addIndirectRoot()` which is abstract.
*/
Path addPermRoot(const StorePath & storePath, const Path & gcRoot) override final;
/**
* Add an indirect root, which is a weak reference to the
* user-facing symlink created by `addPermRoot()`.
*
* @param path user-facing and user-controlled symlink to a store
* path.
*
* The form this weak-reference takes is implementation-specific.
*/
virtual void addIndirectRoot(const Path & path) = 0;
};
}

View file

@ -40,6 +40,7 @@ class LocalFSStore : public virtual LocalFSStoreConfig,
public virtual LogStore public virtual LogStore
{ {
public: public:
inline static std::string operationName = "Local Filesystem Store";
const static std::string drvsLogDir; const static std::string drvsLogDir;
@ -49,9 +50,20 @@ public:
ref<FSAccessor> getFSAccessor() override; ref<FSAccessor> getFSAccessor() override;
/** /**
* Register a permanent GC root. * Creates symlink from the `gcRoot` to the `storePath` and
* registers the `gcRoot` as a permanent GC root. The `gcRoot`
* symlink lives outside the store and is created and owned by the
* user.
*
* @param gcRoot The location of the symlink.
*
* @param storePath The store object being rooted. The symlink will
* point to `toRealPath(store.printStorePath(storePath))`.
*
* How the permanent GC root corresponding to this symlink is
* managed is implementation-specific.
*/ */
Path addPermRoot(const StorePath & storePath, const Path & gcRoot); virtual Path addPermRoot(const StorePath & storePath, const Path & gcRoot) = 0;
virtual Path getRealStoreDir() { return realStoreDir; } virtual Path getRealStoreDir() { return realStoreDir; }

View file

@ -5,8 +5,7 @@
#include "pathlocks.hh" #include "pathlocks.hh"
#include "store-api.hh" #include "store-api.hh"
#include "local-fs-store.hh" #include "indirect-root-store.hh"
#include "gc-store.hh"
#include "sync.hh" #include "sync.hh"
#include "util.hh" #include "util.hh"
@ -68,7 +67,9 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig
std::string doc() override; std::string doc() override;
}; };
class LocalStore : public virtual LocalStoreConfig, public virtual LocalFSStore, public virtual GcStore class LocalStore : public virtual LocalStoreConfig
, public virtual IndirectRootStore
, public virtual GcStore
{ {
private: private:
@ -209,6 +210,12 @@ private:
public: public:
/**
* Implementation of IndirectRootStore::addIndirectRoot().
*
* The weak reference merely is a symlink to `path' from
* /nix/var/nix/gcroots/auto/<hash of `path'>.
*/
void addIndirectRoot(const Path & path) override; void addIndirectRoot(const Path & path) override;
private: private:

View file

@ -822,15 +822,6 @@ void RemoteStore::addTempRoot(const StorePath & path)
} }
void RemoteStore::addIndirectRoot(const Path & path)
{
auto conn(getConnection());
conn->to << WorkerProto::Op::AddIndirectRoot << path;
conn.processStderr();
readInt(conn->from);
}
Roots RemoteStore::findRoots(bool censor) Roots RemoteStore::findRoots(bool censor)
{ {
auto conn(getConnection()); auto conn(getConnection());

View file

@ -126,8 +126,6 @@ public:
void addTempRoot(const StorePath & path) override; void addTempRoot(const StorePath & path) override;
void addIndirectRoot(const Path & path) override;
Roots findRoots(bool censor) override; Roots findRoots(bool censor) override;
void collectGarbage(const GCOptions & options, GCResults & results) override; void collectGarbage(const GCOptions & options, GCResults & results) override;

View file

@ -1,5 +1,6 @@
#include "ssh-store-config.hh" #include "ssh-store-config.hh"
#include "store-api.hh" #include "store-api.hh"
#include "local-fs-store.hh"
#include "remote-store.hh" #include "remote-store.hh"
#include "remote-store-connection.hh" #include "remote-store-connection.hh"
#include "remote-fs-accessor.hh" #include "remote-fs-accessor.hh"
@ -61,7 +62,7 @@ public:
std::optional<std::string> getBuildLogExact(const StorePath & path) override std::optional<std::string> getBuildLogExact(const StorePath & path) override
{ unsupported("getBuildLogExact"); } { unsupported("getBuildLogExact"); }
private: protected:
struct Connection : RemoteStore::Connection struct Connection : RemoteStore::Connection
{ {
@ -93,9 +94,12 @@ private:
ref<RemoteStore::Connection> SSHStore::openConnection() ref<RemoteStore::Connection> SSHStore::openConnection()
{ {
auto conn = make_ref<Connection>(); auto conn = make_ref<Connection>();
conn->sshConn = master.startCommand(
fmt("%s --stdio", remoteProgram) std::string command = remoteProgram + " --stdio";
+ (remoteStore.get() == "" ? "" : " --store " + shellEscape(remoteStore.get()))); if (remoteStore.get() != "")
command += " --store " + shellEscape(remoteStore.get());
conn->sshConn = master.startCommand(command);
conn->to = FdSink(conn->sshConn->in.get()); conn->to = FdSink(conn->sshConn->in.get());
conn->from = FdSource(conn->sshConn->out.get()); conn->from = FdSource(conn->sshConn->out.get());
return conn; return conn;

View file

@ -1,4 +1,5 @@
#include "uds-remote-store.hh" #include "uds-remote-store.hh"
#include "worker-protocol.hh"
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -77,6 +78,15 @@ ref<RemoteStore::Connection> UDSRemoteStore::openConnection()
} }
void UDSRemoteStore::addIndirectRoot(const Path & path)
{
auto conn(getConnection());
conn->to << WorkerProto::Op::AddIndirectRoot << path;
conn.processStderr();
readInt(conn->from);
}
static RegisterStoreImplementation<UDSRemoteStore, UDSRemoteStoreConfig> regUDSRemoteStore; static RegisterStoreImplementation<UDSRemoteStore, UDSRemoteStoreConfig> regUDSRemoteStore;
} }

View file

@ -3,7 +3,7 @@
#include "remote-store.hh" #include "remote-store.hh"
#include "remote-store-connection.hh" #include "remote-store-connection.hh"
#include "local-fs-store.hh" #include "indirect-root-store.hh"
namespace nix { namespace nix {
@ -21,7 +21,9 @@ struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreCon
std::string doc() override; std::string doc() override;
}; };
class UDSRemoteStore : public virtual UDSRemoteStoreConfig, public virtual LocalFSStore, public virtual RemoteStore class UDSRemoteStore : public virtual UDSRemoteStoreConfig
, public virtual IndirectRootStore
, public virtual RemoteStore
{ {
public: public:
@ -39,6 +41,16 @@ public:
void narFromPath(const StorePath & path, Sink & sink) override void narFromPath(const StorePath & path, Sink & sink) override
{ LocalFSStore::narFromPath(path, sink); } { LocalFSStore::narFromPath(path, sink); }
/**
* Implementation of `IndirectRootStore::addIndirectRoot()` which
* delegates to the remote store.
*
* The idea is that the client makes the direct symlink, so it is
* owned managed by the client's user account, and the server makes
* the indirect symlink.
*/
void addIndirectRoot(const Path & path) override;
private: private:
struct Connection : RemoteStore::Connection struct Connection : RemoteStore::Connection