Skip to content

Commit 58cdab6

Browse files
committed
Store metadata about drv outputs realisations
For each known realisation, store: - its output - its output path This comes with a set of needed changes: - New `realisations` module declaring the types needed for describing these mappings - New `Store::registerDrvOutput` method registering all the needed informations about a derivation output (also replaces `LocalStore::linkDeriverToPath`) - new `Store::queryRealisation` method to retrieve the informations for a derivations This introcudes some redundancy on the remote-store side between `wopQueryDerivationOutputMap` and `wopQueryRealisation`. However we might need to keep both (regardless of backwards compat) because we sometimes need to get some infos for all the outputs of a derivation (where `wopQueryDerivationOutputMap` is handy), but all the stores can't implement it − because listing all the outputs of a derivation isn't really possible for binary caches where the server doesn't allow to list a directory.
1 parent 9c143c4 commit 58cdab6

16 files changed

+248
-12
lines changed

src/libstore/binary-cache-store.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,23 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
443443
})->path;
444444
}
445445

446+
std::optional<const Realisation> BinaryCacheStore::queryRealisation(const DrvOutput & id)
447+
{
448+
auto outputInfoFilePath = realisationsPrefix + "/" + id.to_string() + ".doi";
449+
auto rawOutputInfo = getFile(outputInfoFilePath);
450+
451+
if (rawOutputInfo) {
452+
return { Realisation::parse(*rawOutputInfo, outputInfoFilePath) };
453+
} else {
454+
return std::nullopt;
455+
}
456+
}
457+
458+
void BinaryCacheStore::registerDrvOutput(const Realisation& info) {
459+
auto filePath = realisationsPrefix + "/" + info.id.to_string() + ".doi";
460+
upsertFile(filePath, info.to_string(), "text/x-nix-derivertopath");
461+
}
462+
446463
ref<FSAccessor> BinaryCacheStore::getFSAccessor()
447464
{
448465
return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()), localNarCache);

src/libstore/binary-cache-store.hh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ private:
3333

3434
protected:
3535

36+
// The prefix under which realisation infos will be stored
37+
const std::string realisationsPrefix = "/realisations";
38+
3639
BinaryCacheStore(const Params & params);
3740

3841
public:
@@ -99,6 +102,10 @@ public:
99102
StorePath addTextToStore(const string & name, const string & s,
100103
const StorePathSet & references, RepairFlag repair) override;
101104

105+
void registerDrvOutput(const Realisation & info) override;
106+
107+
std::optional<const Realisation> queryRealisation(const DrvOutput &) override;
108+
102109
void narFromPath(const StorePath & path, Sink & sink) override;
103110

104111
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,

src/libstore/build/derivation-goal.cc

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2094,6 +2094,20 @@ struct RestrictedStore : public LocalFSStore, public virtual RestrictedStoreConf
20942094
/* Nothing to be done; 'path' must already be valid. */
20952095
}
20962096

2097+
void registerDrvOutput(const Realisation & info) override
2098+
{
2099+
if (!goal.isAllowed(info.id.drvPath))
2100+
throw InvalidPath("cannot register unknown drv output '%s' in recursive Nix", printStorePath(info.id.drvPath));
2101+
next->registerDrvOutput(info);
2102+
}
2103+
2104+
std::optional<const Realisation> queryRealisation(const DrvOutput & id) override
2105+
{
2106+
if (!goal.isAllowed(id.drvPath))
2107+
throw InvalidPath("cannot query the output info for unknown derivation '%s' in recursive Nix", printStorePath(id.drvPath));
2108+
return next->queryRealisation(id);
2109+
}
2110+
20972111
void buildPaths(const std::vector<StorePathWithOutputs> & paths, BuildMode buildMode) override
20982112
{
20992113
if (buildMode != bmNormal) throw Error("unsupported build mode");
@@ -3393,7 +3407,10 @@ void DerivationGoal::registerOutputs()
33933407

33943408
if (useDerivation || isCaFloating)
33953409
for (auto & [outputName, newInfo] : infos)
3396-
worker.store.linkDeriverToPath(drvPathResolved, outputName, newInfo.path);
3410+
worker.store.registerDrvOutput(
3411+
DrvOutputId{drvPathResolved, outputName},
3412+
DrvOutputInfo{.outPath = newInfo.path,
3413+
.resolvedDrv = drvPathResolved});
33973414
}
33983415

33993416

src/libstore/daemon.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,28 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
868868
break;
869869
}
870870

871+
case wopRegisterDrvOutput: {
872+
logger->startWork();
873+
auto outputId = DrvOutput::parse(readString(from));
874+
auto outputPath = StorePath(readString(from));
875+
auto resolvedDrv = StorePath(readString(from));
876+
store->registerDrvOutput(Realisation{
877+
.id = outputId, .outPath = outputPath});
878+
logger->stopWork();
879+
break;
880+
}
881+
882+
case wopQueryRealisation: {
883+
logger->startWork();
884+
auto outputId = DrvOutput::parse(readString(from));
885+
auto info = store->queryRealisation(outputId);
886+
logger->stopWork();
887+
std::set<StorePath> outPaths;
888+
if (info) outPaths.insert(info->outPath);
889+
worker_proto::write(*store, to, outPaths);
890+
break;
891+
}
892+
871893
default:
872894
throw Error("invalid operation %1%", op);
873895
}

src/libstore/dummy-store.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ struct DummyStore : public Store, public virtual DummyStoreConfig
6060
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
6161
BuildMode buildMode) override
6262
{ unsupported("buildDerivation"); }
63+
64+
std::optional<const Realisation> queryRealisation(const DrvOutput&) override
65+
{ unsupported("queryRealisation"); }
6366
};
6467

6568
static RegisterStoreImplementation<DummyStore, DummyStoreConfig> regDummyStore;

src/libstore/legacy-ssh-store.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,10 @@ struct LegacySSHStore : public Store, public virtual LegacySSHStoreConfig
333333
auto conn(connections->get());
334334
return conn->remoteVersion;
335335
}
336+
337+
std::optional<const Realisation> queryRealisation(const DrvOutput&) override
338+
// TODO: Implement
339+
{ unsupported("queryRealisation"); }
336340
};
337341

338342
static RegisterStoreImplementation<LegacySSHStore, LegacySSHStoreConfig> regLegacySSHStore;

src/libstore/local-binary-cache-store.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class LocalBinaryCacheStore : public BinaryCacheStore, public virtual LocalBinar
8787
void LocalBinaryCacheStore::init()
8888
{
8989
createDirs(binaryCacheDir + "/nar");
90+
createDirs(binaryCacheDir + realisationsPrefix);
9091
if (writeDebugInfo)
9192
createDirs(binaryCacheDir + "/debuginfo");
9293
BinaryCacheStore::init();

src/libstore/local-store.cc

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -597,13 +597,16 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat
597597
}
598598

599599

600-
void LocalStore::linkDeriverToPath(const StorePath & deriver, const string & outputName, const StorePath & output)
600+
void LocalStore::registerDrvOutput(const Realisation & info)
601601
{
602602
auto state(_state.lock());
603-
return linkDeriverToPath(*state, queryValidPathId(*state, deriver), outputName, output);
603+
// XXX: This ignores the references of the output because we can
604+
// recompute them later from the drv and the references of the associated
605+
// store path, but doing so is both inefficient and fragile.
606+
return registerDrvOutput_(*state, queryValidPathId(*state, id.drvPath), id.outputName, info.outPath);
604607
}
605608

606-
void LocalStore::linkDeriverToPath(State & state, uint64_t deriver, const string & outputName, const StorePath & output)
609+
void LocalStore::registerDrvOutput_(State & state, uint64_t deriver, const string & outputName, const StorePath & output)
607610
{
608611
retrySQLite<void>([&]() {
609612
state.stmts->AddDerivationOutput.use()
@@ -653,7 +656,7 @@ uint64_t LocalStore::addValidPath(State & state,
653656
/* Floating CA derivations have indeterminate output paths until
654657
they are built, so don't register anything in that case */
655658
if (i.second.second)
656-
linkDeriverToPath(state, id, i.first, *i.second.second);
659+
registerDrvOutput_(state, id, i.first, *i.second.second);
657660
}
658661
}
659662

@@ -1612,5 +1615,13 @@ void LocalStore::createUser(const std::string & userName, uid_t userId)
16121615
}
16131616
}
16141617

1615-
1618+
std::optional<const DrvOutputInfo> LocalStore::queryDrvOutputInfo(const DrvOutputId& id) {
1619+
auto outputPath = queryOutputPathOf(id.drvPath, id.outputName);
1620+
if (!(outputPath && isValidPath(*outputPath)))
1621+
return std::nullopt;
1622+
else
1623+
return {DrvOutputInfo{
1624+
.outPath = *outputPath,
1625+
}};
16161626
}
1627+
} // namespace nix

src/libstore/local-store.hh

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,13 @@ public:
208208
garbage until it exceeds maxFree. */
209209
void autoGC(bool sync = true);
210210

211+
/* Register the store path 'output' as the output named 'outputName' of
212+
derivation 'deriver'. */
213+
void registerDrvOutput(const DrvOutputId & outputId, const DrvOutputInfo & info) override;
214+
void registerDrvOutput_(State & state, uint64_t deriver, const string & outputName, const StorePath & output);
215+
216+
std::optional<const Realisation> queryRealisation(const DrvOutput&) override;
217+
211218
private:
212219

213220
int getSchema();
@@ -276,11 +283,6 @@ private:
276283
specified by the ‘secret-key-files’ option. */
277284
void signPathInfo(ValidPathInfo & info);
278285

279-
/* Register the store path 'output' as the output named 'outputName' of
280-
derivation 'deriver'. */
281-
void linkDeriverToPath(const StorePath & deriver, const string & outputName, const StorePath & output);
282-
void linkDeriverToPath(State & state, uint64_t deriver, const string & outputName, const StorePath & output);
283-
284286
Path getRealStoreDir() override { return realStoreDir; }
285287

286288
void createUser(const std::string & userName, uid_t userId) override;

src/libstore/realisation.cc

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include "realisation.hh"
2+
#include "store-api.hh"
3+
4+
namespace nix {
5+
6+
MakeError(InvalidDerivationOutputId, Error);
7+
8+
DrvOutput DrvOutput::parse(const std::string &strRep) {
9+
const auto &[rawPath, outputs] = parsePathWithOutputs(strRep);
10+
if (outputs.size() != 1)
11+
throw InvalidDerivationOutputId("Invalid derivation output id %s", strRep);
12+
13+
return DrvOutput{
14+
.drvPath = StorePath(rawPath),
15+
.outputName = *outputs.begin(),
16+
};
17+
}
18+
19+
std::string DrvOutput::to_string() const {
20+
return std::string(drvPath.to_string()) + "!" + outputName;
21+
}
22+
23+
std::string Realisation::to_string() const {
24+
std::string res;
25+
26+
res += "Id: " + id.to_string() + '\n';
27+
res += "OutPath: " + std::string(outPath.to_string()) + '\n';
28+
29+
return res;
30+
}
31+
32+
Realisation Realisation::parse(const std::string & s, const std::string & whence)
33+
{
34+
// XXX: Copy-pasted from NarInfo::NarInfo. Should be factored out
35+
auto corrupt = [&]() {
36+
return Error("Drv output info file '%1%' is corrupt", whence);
37+
};
38+
39+
std::optional<DrvOutput> id;
40+
std::optional<StorePath> outPath;
41+
42+
size_t pos = 0;
43+
while (pos < s.size()) {
44+
45+
size_t colon = s.find(':', pos);
46+
if (colon == std::string::npos) throw corrupt();
47+
48+
std::string name(s, pos, colon - pos);
49+
50+
size_t eol = s.find('\n', colon + 2);
51+
if (eol == std::string::npos) throw corrupt();
52+
53+
std::string value(s, colon + 2, eol - colon - 2);
54+
55+
if (name == "Id")
56+
id = DrvOutput::parse(value);
57+
58+
if (name == "OutPath")
59+
outPath = StorePath(value);
60+
61+
pos = eol + 1;
62+
}
63+
64+
if (!outPath) corrupt();
65+
if (!id) corrupt();
66+
return Realisation {
67+
.id = *id,
68+
.outPath = *outPath,
69+
};
70+
}
71+
72+
} // namespace nix

0 commit comments

Comments
 (0)