Properly fail when trying to register an incoherent realisation

This commit is contained in:
regnat 2021-05-19 16:19:46 +02:00
parent a5df669bc6
commit b8f7177a7b
4 changed files with 107 additions and 39 deletions

View file

@ -53,6 +53,7 @@ struct LocalStore::State::Stmts {
SQLiteStmt InvalidatePath; SQLiteStmt InvalidatePath;
SQLiteStmt AddDerivationOutput; SQLiteStmt AddDerivationOutput;
SQLiteStmt RegisterRealisedOutput; SQLiteStmt RegisterRealisedOutput;
SQLiteStmt UpdateRealisedOutput;
SQLiteStmt QueryValidDerivers; SQLiteStmt QueryValidDerivers;
SQLiteStmt QueryDerivationOutputs; SQLiteStmt QueryDerivationOutputs;
SQLiteStmt QueryRealisedOutput; SQLiteStmt QueryRealisedOutput;
@ -345,6 +346,15 @@ LocalStore::LocalStore(const Params & params)
values (?, ?, (select id from ValidPaths where path = ?), ?) values (?, ?, (select id from ValidPaths where path = ?), ?)
; ;
)"); )");
state->stmts->UpdateRealisedOutput.create(state->db,
R"(
update Realisations
set signatures = ?
where
drvPath = ? and
outputName = ?
;
)");
state->stmts->QueryRealisedOutput.create(state->db, state->stmts->QueryRealisedOutput.create(state->db,
R"( R"(
select Realisations.id, Output.path, Realisations.signatures from Realisations select Realisations.id, Output.path, Realisations.signatures from Realisations
@ -710,14 +720,41 @@ void LocalStore::registerDrvOutput(const Realisation & info)
settings.requireExperimentalFeature("ca-derivations"); settings.requireExperimentalFeature("ca-derivations");
retrySQLite<void>([&]() { retrySQLite<void>([&]() {
auto state(_state.lock()); auto state(_state.lock());
state->stmts->RegisterRealisedOutput.use() if (auto oldR = queryRealisation_(*state, info.id)) {
(info.id.strHash()) if (info.isCompatibleWith(*oldR)) {
(info.id.outputName) auto combinedSignatures = oldR->signatures;
(printStorePath(info.outPath)) combinedSignatures.insert(info.signatures.begin(),
(concatStringsSep(" ", info.signatures)) info.signatures.end());
.exec(); state->stmts->UpdateRealisedOutput.use()
(concatStringsSep(" ", combinedSignatures))
(info.id.strHash())
(info.id.outputName)
.exec();
} else {
throw Error("Trying to register a realisation of '%s', but we already "
"have another one locally",
info.id.to_string());
}
} else {
state->stmts->RegisterRealisedOutput.use()
(info.id.strHash())
(info.id.outputName)
(printStorePath(info.outPath))
(concatStringsSep(" ", info.signatures))
.exec();
}
uint64_t myId = state->db.getLastInsertedRowId(); uint64_t myId = state->db.getLastInsertedRowId();
for (auto & [outputId, _] : info.dependentRealisations) { for (auto & [outputId, depPath] : info.dependentRealisations) {
auto localRealisation = queryRealisationCore_(*state, outputId);
if (!localRealisation)
throw Error("unable to register the derivation '%s' as it "
"depends on the non existent '%s'",
info.id.to_string(), outputId.to_string());
if (localRealisation->second.outPath != depPath)
throw Error("unable to register the derivation '%s' as it "
"depends on a realisation of '%s' that doesnt"
"match what we have locally",
info.id.to_string(), outputId.to_string());
state->stmts->AddRealisationReference.use() state->stmts->AddRealisationReference.use()
(myId) (myId)
(outputId.strHash()) (outputId.strHash())
@ -1734,46 +1771,67 @@ void LocalStore::createUser(const std::string & userName, uid_t userId)
} }
} }
std::optional<const Realisation> LocalStore::queryRealisation( std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_(
const DrvOutput& id) { LocalStore::State & state,
typedef std::optional<const Realisation> Ret; const DrvOutput & id)
return retrySQLite<Ret>([&]() -> Ret { {
auto state(_state.lock()); auto useQueryRealisedOutput(
auto useQueryRealisedOutput(state->stmts->QueryRealisedOutput.use() state.stmts->QueryRealisedOutput.use()
(id.strHash()) (id.strHash())
(id.outputName)); (id.outputName));
if (!useQueryRealisedOutput.next()) if (!useQueryRealisedOutput.next())
return std::nullopt; return std::nullopt;
auto realisationDbId = useQueryRealisedOutput.getInt(0); auto realisationDbId = useQueryRealisedOutput.getInt(0);
auto outputPath = parseStorePath(useQueryRealisedOutput.getStr(1)); auto outputPath = parseStorePath(useQueryRealisedOutput.getStr(1));
auto signatures = auto signatures =
tokenizeString<StringSet>(useQueryRealisedOutput.getStr(2)); tokenizeString<StringSet>(useQueryRealisedOutput.getStr(2));
std::map<DrvOutput, StorePath> dependentRealisations; return {{
auto useRealisationRefs( realisationDbId,
state->stmts->QueryRealisationReferences.use() Realisation{
(realisationDbId));
while (useRealisationRefs.next()) {
auto depHash = useRealisationRefs.getStr(0);
auto depOutputName = useRealisationRefs.getStr(1);
auto useQueryRealisedOutput(state->stmts->QueryRealisedOutput.use()
(depHash)
(depOutputName));
assert(useQueryRealisedOutput.next());
auto outputPath = parseStorePath(useQueryRealisedOutput.getStr(1));
auto depId = DrvOutput { Hash::parseAnyPrefixed(depHash), depOutputName };
dependentRealisations.insert({depId, outputPath});
}
return Ret{Realisation{
.id = id, .id = id,
.outPath = outputPath, .outPath = outputPath,
.signatures = signatures, .signatures = signatures,
.dependentRealisations = dependentRealisations, }
}}; }};
});
} }
std::optional<const Realisation> LocalStore::queryRealisation_(
LocalStore::State & state,
const DrvOutput & id)
{
auto maybeCore = queryRealisationCore_(state, id);
if (!maybeCore)
return std::nullopt;
auto [realisationDbId, res] = *maybeCore;
std::map<DrvOutput, StorePath> dependentRealisations;
auto useRealisationRefs(
state.stmts->QueryRealisationReferences.use()
(realisationDbId));
while (useRealisationRefs.next()) {
auto depId = DrvOutput {
Hash::parseAnyPrefixed(useRealisationRefs.getStr(0)),
useRealisationRefs.getStr(1),
};
auto dependentRealisation = queryRealisationCore_(state, depId);
assert(dependentRealisation); // Enforced by the db schema
auto outputPath = dependentRealisation->second.outPath;
dependentRealisations.insert({depId, outputPath});
}
res.dependentRealisations = dependentRealisations;
return { res };
}
std::optional<const Realisation>
LocalStore::queryRealisation(const DrvOutput &id) {
return retrySQLite<std::optional<const Realisation>>([&]() {
auto state(_state.lock());
return queryRealisation_(*state, id);
});
}
FixedOutputHash LocalStore::hashCAPath( FixedOutputHash LocalStore::hashCAPath(
const FileIngestionMethod & method, const HashType & hashType, const FileIngestionMethod & method, const HashType & hashType,

View file

@ -203,6 +203,8 @@ public:
void registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs) override; void registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs) override;
void cacheDrvOutputMapping(State & state, const uint64_t deriver, const string & outputName, const StorePath & output); void cacheDrvOutputMapping(State & state, const uint64_t deriver, const string & outputName, const StorePath & output);
std::optional<const Realisation> queryRealisation_(State & state, const DrvOutput & id);
std::optional<std::pair<int64_t, Realisation>> queryRealisationCore_(State & state, const DrvOutput & id);
std::optional<const Realisation> queryRealisation(const DrvOutput&) override; std::optional<const Realisation> queryRealisation(const DrvOutput&) override;
private: private:

View file

@ -140,6 +140,12 @@ StorePath RealisedPath::path() const {
return std::visit([](auto && arg) { return arg.getPath(); }, raw); return std::visit([](auto && arg) { return arg.getPath(); }, raw);
} }
bool Realisation::isCompatibleWith(const Realisation & other) const
{
assert (id == other.id);
return outPath == other.outPath;
}
void RealisedPath::closure( void RealisedPath::closure(
Store& store, Store& store,
const RealisedPath::Set& startPaths, const RealisedPath::Set& startPaths,

View file

@ -47,6 +47,8 @@ struct Realisation {
static std::set<Realisation> closure(Store &, const std::set<Realisation> &); static std::set<Realisation> closure(Store &, const std::set<Realisation> &);
static void closure(Store &, const std::set<Realisation> &, std::set<Realisation>& res); static void closure(Store &, const std::set<Realisation> &, std::set<Realisation>& res);
bool isCompatibleWith(const Realisation & other) const;
StorePath getPath() const { return outPath; } StorePath getPath() const { return outPath; }
GENERATE_CMP(Realisation, me->id, me->outPath); GENERATE_CMP(Realisation, me->id, me->outPath);