Skip to content

Commit b662341

Browse files
committedMay 6, 2021
Add a realisations disk cache
Similar to the nar-info disk cache (and using the same db). This makes rebuilds muuch faster. - This works regardless of the ca-derivations experimental feature. I could modify the logic to not touch the db if the flag isn’t there, but given that this is a trash-able local cache, it doesn’t seem to be really worth it. - We could unify the `NARs` and `Realisation` tables to only have one generic kv table. This is left as an exercise to the reader. - I didn’t update the cache db version number as the new schema just adds a new table to the previous one, so the db will be transparently migrated and is backwards-compatible. Fix #4746
1 parent fe3a10a commit b662341

File tree

4 files changed

+149
-3
lines changed

4 files changed

+149
-3
lines changed
 

Diff for: ‎src/libstore/binary-cache-store.cc

+27-2
Original file line numberDiff line numberDiff line change
@@ -450,18 +450,43 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
450450

451451
std::optional<const Realisation> BinaryCacheStore::queryRealisation(const DrvOutput & id)
452452
{
453+
if (diskCache) {
454+
auto [cacheOutcome, maybeCachedRealisation] =
455+
diskCache->lookupRealisation(getUri(), id);
456+
switch (cacheOutcome) {
457+
case (NarInfoDiskCache::oValid):
458+
debug("Returning a cached realisation for %s", id.to_string());
459+
return *maybeCachedRealisation;
460+
case (NarInfoDiskCache::oInvalid):
461+
debug("Returning a cached missing realisation for %s", id.to_string());
462+
return {};
463+
case (NarInfoDiskCache::oUnknown):
464+
break;
465+
}
466+
}
467+
453468
auto outputInfoFilePath = realisationsPrefix + "/" + id.to_string() + ".doi";
454469
auto rawOutputInfo = getFile(outputInfoFilePath);
455470

456471
if (rawOutputInfo) {
457-
return {Realisation::fromJSON(
458-
nlohmann::json::parse(*rawOutputInfo), outputInfoFilePath)};
472+
auto realisation = Realisation::fromJSON(
473+
nlohmann::json::parse(*rawOutputInfo), outputInfoFilePath);
474+
475+
if (diskCache)
476+
diskCache->upsertRealisation(
477+
getUri(), realisation);
478+
479+
return {realisation};
459480
} else {
481+
if (diskCache)
482+
diskCache->upsertAbsentRealisation(getUri(), id);
460483
return std::nullopt;
461484
}
462485
}
463486

464487
void BinaryCacheStore::registerDrvOutput(const Realisation& info) {
488+
if (diskCache)
489+
diskCache->upsertRealisation(getUri(), info);
465490
auto filePath = realisationsPrefix + "/" + info.id.to_string() + ".doi";
466491
upsertFile(filePath, info.toJSON().dump(), "application/json");
467492
}

Diff for: ‎src/libstore/nar-info-disk-cache.cc

+99-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "globals.hh"
55

66
#include <sqlite3.h>
7+
#include <nlohmann/json.hpp>
78

89
namespace nix {
910

@@ -38,6 +39,16 @@ create table if not exists NARs (
3839
foreign key (cache) references BinaryCaches(id) on delete cascade
3940
);
4041
42+
create table if not exists Realisations (
43+
cache integer not null,
44+
outputId text not null,
45+
content blob, -- Json serialisation of the realisation, or empty if present is true
46+
timestamp integer not null,
47+
present integer not null,
48+
primary key (cache, outputId),
49+
foreign key (cache) references BinaryCaches(id) on delete cascade
50+
);
51+
4152
create table if not exists LastPurge (
4253
dummy text primary key,
4354
value integer
@@ -63,7 +74,9 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache
6374
struct State
6475
{
6576
SQLite db;
66-
SQLiteStmt insertCache, queryCache, insertNAR, insertMissingNAR, queryNAR, purgeCache;
77+
SQLiteStmt insertCache, queryCache, insertNAR, insertMissingNAR,
78+
queryNAR, insertRealisation, insertMissingRealisation,
79+
queryRealisation, purgeCache;
6780
std::map<std::string, Cache> caches;
6881
};
6982

@@ -98,6 +111,26 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache
98111
state->queryNAR.create(state->db,
99112
"select present, namePart, url, compression, fileHash, fileSize, narHash, narSize, refs, deriver, sigs, ca from NARs where cache = ? and hashPart = ? and ((present = 0 and timestamp > ?) or (present = 1 and timestamp > ?))");
100113

114+
state->insertRealisation.create(state->db,
115+
R"(
116+
insert or replace into Realisations(cache, outputId, content, timestamp, present)
117+
values (?, ?, ?, ?, 1)
118+
)");
119+
120+
state->insertMissingRealisation.create(state->db,
121+
R"(
122+
insert or replace into Realisations(cache, outputId, timestamp, present)
123+
values (?, ?, ?, 0)
124+
)");
125+
126+
state->queryRealisation.create(state->db,
127+
R"(
128+
select present, content from Realisations
129+
where cache = ? and outputId = ? and
130+
((present = 0 and timestamp > ?) or
131+
(present = 1 and timestamp > ?))
132+
)");
133+
101134
/* Periodically purge expired entries from the database. */
102135
retrySQLite<void>([&]() {
103136
auto now = time(0);
@@ -212,6 +245,38 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache
212245
});
213246
}
214247

248+
std::pair<Outcome, std::shared_ptr<Realisation>> lookupRealisation(
249+
const std::string & uri, const DrvOutput & id) override
250+
{
251+
return retrySQLite<std::pair<Outcome, std::shared_ptr<Realisation>>>(
252+
[&]() -> std::pair<Outcome, std::shared_ptr<Realisation>> {
253+
auto state(_state.lock());
254+
255+
auto & cache(getCache(*state, uri));
256+
257+
auto now = time(0);
258+
259+
auto queryRealisation(state->queryRealisation.use()
260+
(cache.id)
261+
(id.to_string())
262+
(now - settings.ttlNegativeNarInfoCache)
263+
(now - settings.ttlPositiveNarInfoCache));
264+
265+
if (!queryRealisation.next())
266+
return {oUnknown, 0};
267+
268+
if (queryRealisation.getInt(0) == 0)
269+
return {oInvalid, 0};
270+
271+
auto realisation =
272+
std::make_shared<Realisation>(Realisation::fromJSON(
273+
nlohmann::json::parse(queryRealisation.getStr(1)),
274+
"Local disk cache"));
275+
276+
return {oValid, realisation};
277+
});
278+
}
279+
215280
void upsertNarInfo(
216281
const std::string & uri, const std::string & hashPart,
217282
std::shared_ptr<const ValidPathInfo> info) override
@@ -251,6 +316,39 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache
251316
}
252317
});
253318
}
319+
320+
void upsertRealisation(
321+
const std::string & uri,
322+
const Realisation & realisation) override
323+
{
324+
retrySQLite<void>([&]() {
325+
auto state(_state.lock());
326+
327+
auto & cache(getCache(*state, uri));
328+
329+
state->insertRealisation.use()
330+
(cache.id)
331+
(realisation.id.to_string())
332+
(realisation.toJSON().dump())
333+
(time(0)).exec();
334+
});
335+
336+
}
337+
338+
virtual void upsertAbsentRealisation(
339+
const std::string & uri,
340+
const DrvOutput & id) override
341+
{
342+
retrySQLite<void>([&]() {
343+
auto state(_state.lock());
344+
345+
auto & cache(getCache(*state, uri));
346+
state->insertMissingRealisation.use()
347+
(cache.id)
348+
(id.to_string())
349+
(time(0)).exec();
350+
});
351+
}
254352
};
255353

256354
ref<NarInfoDiskCache> getNarInfoDiskCache()

Diff for: ‎src/libstore/nar-info-disk-cache.hh

+10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "ref.hh"
44
#include "nar-info.hh"
5+
#include "realisation.hh"
56

67
namespace nix {
78

@@ -29,6 +30,15 @@ public:
2930
virtual void upsertNarInfo(
3031
const std::string & uri, const std::string & hashPart,
3132
std::shared_ptr<const ValidPathInfo> info) = 0;
33+
34+
virtual void upsertRealisation(
35+
const std::string & uri,
36+
const Realisation & realisation) = 0;
37+
virtual void upsertAbsentRealisation(
38+
const std::string & uri,
39+
const DrvOutput & id) = 0;
40+
virtual std::pair<Outcome, std::shared_ptr<Realisation>> lookupRealisation(
41+
const std::string & uri, const DrvOutput & id) = 0;
3242
};
3343

3444
/* Return a singleton cache object that can be used concurrently by

Diff for: ‎tests/ca/substitute.sh

+13
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,16 @@ if [[ -z "$(ls "$REMOTE_STORE_DIR/realisations")" ]]; then
4545
echo "Realisations not rebuilt"
4646
exit 1
4747
fi
48+
49+
# Test the local realisation disk cache
50+
buildDrvs --post-build-hook ../push-to-store.sh
51+
clearStore
52+
# Add the realisations of rootCA to the cachecache
53+
clearCacheCache
54+
export _NIX_FORCE_HTTP=1
55+
buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0
56+
# Try rebuilding, but remove the realisations from the remote cache to force
57+
# using the cachecache
58+
clearStore
59+
rm $REMOTE_STORE_DIR/realisations/*
60+
buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0

0 commit comments

Comments
 (0)
Please sign in to comment.