Skip to content

Commit 22c928f

Browse files
authored
Merge pull request #12992 from DeterminateSystems/input-cache
Rename FlakeCache to InputCache and move it to libfetchers
2 parents 13a0853 + 4966217 commit 22c928f

File tree

8 files changed

+140
-79
lines changed

8 files changed

+140
-79
lines changed

src/libexpr/eval.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "nix/util/url.hh"
2020
#include "nix/fetchers/fetch-to-store.hh"
2121
#include "nix/fetchers/tarball.hh"
22+
#include "nix/fetchers/input-cache.hh"
2223

2324
#include "parser-tab.hh"
2425

@@ -310,6 +311,7 @@ EvalState::EvalState(
310311
)}
311312
, store(store)
312313
, buildStore(buildStore ? buildStore : store)
314+
, inputCache(fetchers::InputCache::create())
313315
, debugRepl(nullptr)
314316
, debugStop(false)
315317
, trylevel(0)
@@ -1152,6 +1154,7 @@ void EvalState::resetFileCache()
11521154
{
11531155
fileEvalCache.clear();
11541156
fileParseCache.clear();
1157+
inputCache->clear();
11551158
}
11561159

11571160

src/libexpr/include/nix/expr/eval.hh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ namespace nix {
3333
constexpr size_t maxPrimOpArity = 8;
3434

3535
class Store;
36-
namespace fetchers { struct Settings; }
36+
namespace fetchers {
37+
struct Settings;
38+
struct InputCache;
39+
}
3740
struct EvalSettings;
3841
class EvalState;
3942
class StorePath;
@@ -300,6 +303,8 @@ public:
300303

301304
RootValue vImportedDrvToDerivation = nullptr;
302305

306+
ref<fetchers::InputCache> inputCache;
307+
303308
/**
304309
* Debugger
305310
*/
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include "fetchers.hh"
2+
3+
namespace nix::fetchers {
4+
5+
struct InputCache
6+
{
7+
struct CachedResult
8+
{
9+
ref<SourceAccessor> accessor;
10+
Input resolvedInput;
11+
Input lockedInput;
12+
};
13+
14+
CachedResult getAccessor(ref<Store> store, const Input & originalInput, bool useRegistries);
15+
16+
struct CachedInput
17+
{
18+
Input lockedInput;
19+
ref<SourceAccessor> accessor;
20+
};
21+
22+
virtual std::optional<CachedInput> lookup(const Input & originalInput) const = 0;
23+
24+
virtual void upsert(Input key, CachedInput cachedInput) = 0;
25+
26+
virtual void clear() = 0;
27+
28+
static ref<InputCache> create();
29+
};
30+
31+
}

src/libfetchers/include/nix/fetchers/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ headers = files(
99
'filtering-source-accessor.hh',
1010
'git-lfs-fetch.hh',
1111
'git-utils.hh',
12+
'input-cache.hh',
1213
'registry.hh',
1314
'store-path-accessor.hh',
1415
'tarball.hh',

src/libfetchers/input-cache.cc

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include "nix/fetchers/input-cache.hh"
2+
#include "nix/fetchers/registry.hh"
3+
#include "nix/util/sync.hh"
4+
#include "nix/util/source-path.hh"
5+
6+
namespace nix::fetchers {
7+
8+
InputCache::CachedResult InputCache::getAccessor(ref<Store> store, const Input & originalInput, bool useRegistries)
9+
{
10+
auto fetched = lookup(originalInput);
11+
Input resolvedInput = originalInput;
12+
13+
if (!fetched) {
14+
if (originalInput.isDirect()) {
15+
auto [accessor, lockedInput] = originalInput.getAccessor(store);
16+
fetched.emplace(CachedInput{.lockedInput = lockedInput, .accessor = accessor});
17+
} else {
18+
if (useRegistries) {
19+
auto [res, extraAttrs] =
20+
lookupInRegistries(store, originalInput, [](fetchers::Registry::RegistryType type) {
21+
/* Only use the global registry and CLI flags
22+
to resolve indirect flakerefs. */
23+
return type == fetchers::Registry::Flag || type == fetchers::Registry::Global;
24+
});
25+
resolvedInput = std::move(res);
26+
fetched = lookup(resolvedInput);
27+
if (!fetched) {
28+
auto [accessor, lockedInput] = resolvedInput.getAccessor(store);
29+
fetched.emplace(CachedInput{.lockedInput = lockedInput, .accessor = accessor});
30+
}
31+
upsert(resolvedInput, *fetched);
32+
} else {
33+
throw Error(
34+
"'%s' is an indirect flake reference, but registry lookups are not allowed",
35+
originalInput.to_string());
36+
}
37+
}
38+
upsert(originalInput, *fetched);
39+
}
40+
41+
debug("got tree '%s' from '%s'", fetched->accessor, fetched->lockedInput.to_string());
42+
43+
return {fetched->accessor, resolvedInput, fetched->lockedInput};
44+
}
45+
46+
struct InputCacheImpl : InputCache
47+
{
48+
Sync<std::map<Input, CachedInput>> cache_;
49+
50+
std::optional<CachedInput> lookup(const Input & originalInput) const override
51+
{
52+
auto cache(cache_.readLock());
53+
auto i = cache->find(originalInput);
54+
if (i == cache->end())
55+
return std::nullopt;
56+
debug(
57+
"mapping '%s' to previously seen input '%s' -> '%s",
58+
originalInput.to_string(),
59+
i->first.to_string(),
60+
i->second.lockedInput.to_string());
61+
return i->second;
62+
}
63+
64+
void upsert(Input key, CachedInput cachedInput) override
65+
{
66+
cache_.lock()->insert_or_assign(std::move(key), std::move(cachedInput));
67+
}
68+
69+
void clear() override
70+
{
71+
cache_.lock()->clear();
72+
}
73+
};
74+
75+
ref<InputCache> InputCache::create()
76+
{
77+
return make_ref<InputCacheImpl>();
78+
}
79+
80+
}

src/libfetchers/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ sources = files(
4444
'git.cc',
4545
'github.cc',
4646
'indirect.cc',
47+
'input-cache.cc',
4748
'mercurial.cc',
4849
'path.cc',
4950
'registry.cc',

src/libflake-c/nix_api_flake.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ nix_locked_flake * nix_flake_lock(
177177
{
178178
nix_clear_err(context);
179179
try {
180+
eval_state->state.resetFileCache();
180181
auto lockedFlake = nix::make_ref<nix::flake::LockedFlake>(nix::flake::lockFlake(
181182
*flakeSettings->settings, eval_state->state, *flakeReference->flakeRef, *flags->lockFlags));
182183
return new nix_locked_flake{lockedFlake};

src/libflake/flake/flake.cc

Lines changed: 17 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "nix/store/local-fs-store.hh"
1515
#include "nix/fetchers/fetch-to-store.hh"
1616
#include "nix/util/memory-source-accessor.hh"
17+
#include "nix/fetchers/input-cache.hh"
1718

1819
#include <nlohmann/json.hpp>
1920

@@ -23,66 +24,6 @@ using namespace flake;
2324

2425
namespace flake {
2526

26-
struct FetchedFlake
27-
{
28-
FlakeRef lockedRef;
29-
ref<SourceAccessor> accessor;
30-
};
31-
32-
typedef std::map<FlakeRef, FetchedFlake> FlakeCache;
33-
34-
static std::optional<FetchedFlake> lookupInFlakeCache(
35-
const FlakeCache & flakeCache,
36-
const FlakeRef & flakeRef)
37-
{
38-
auto i = flakeCache.find(flakeRef);
39-
if (i == flakeCache.end()) return std::nullopt;
40-
debug("mapping '%s' to previously seen input '%s' -> '%s",
41-
flakeRef, i->first, i->second.lockedRef);
42-
return i->second;
43-
}
44-
45-
static std::tuple<ref<SourceAccessor>, FlakeRef, FlakeRef> fetchOrSubstituteTree(
46-
EvalState & state,
47-
const FlakeRef & originalRef,
48-
bool useRegistries,
49-
FlakeCache & flakeCache)
50-
{
51-
auto fetched = lookupInFlakeCache(flakeCache, originalRef);
52-
FlakeRef resolvedRef = originalRef;
53-
54-
if (!fetched) {
55-
if (originalRef.input.isDirect()) {
56-
auto [accessor, lockedRef] = originalRef.lazyFetch(state.store);
57-
fetched.emplace(FetchedFlake{.lockedRef = lockedRef, .accessor = accessor});
58-
} else {
59-
if (useRegistries) {
60-
resolvedRef = originalRef.resolve(
61-
state.store,
62-
[](fetchers::Registry::RegistryType type) {
63-
/* Only use the global registry and CLI flags
64-
to resolve indirect flakerefs. */
65-
return type == fetchers::Registry::Flag || type == fetchers::Registry::Global;
66-
});
67-
fetched = lookupInFlakeCache(flakeCache, originalRef);
68-
if (!fetched) {
69-
auto [accessor, lockedRef] = resolvedRef.lazyFetch(state.store);
70-
fetched.emplace(FetchedFlake{.lockedRef = lockedRef, .accessor = accessor});
71-
}
72-
flakeCache.insert_or_assign(resolvedRef, *fetched);
73-
}
74-
else {
75-
throw Error("'%s' is an indirect flake reference, but registry lookups are not allowed", originalRef);
76-
}
77-
}
78-
flakeCache.insert_or_assign(originalRef, *fetched);
79-
}
80-
81-
debug("got tree '%s' from '%s'", fetched->accessor, fetched->lockedRef);
82-
83-
return {fetched->accessor, resolvedRef, fetched->lockedRef};
84-
}
85-
8627
static StorePath copyInputToStore(
8728
EvalState & state,
8829
fetchers::Input & input,
@@ -397,15 +338,16 @@ static Flake getFlake(
397338
EvalState & state,
398339
const FlakeRef & originalRef,
399340
bool useRegistries,
400-
FlakeCache & flakeCache,
401341
const InputAttrPath & lockRootAttrPath)
402342
{
403343
// Fetch a lazy tree first.
404-
auto [accessor, resolvedRef, lockedRef] = fetchOrSubstituteTree(
405-
state, originalRef, useRegistries, flakeCache);
344+
auto cachedInput = state.inputCache->getAccessor(state.store, originalRef.input, useRegistries);
345+
346+
auto resolvedRef = FlakeRef(std::move(cachedInput.resolvedInput), originalRef.subdir);
347+
auto lockedRef = FlakeRef(std::move(cachedInput.lockedInput), originalRef.subdir);
406348

407349
// Parse/eval flake.nix to get at the input.self attributes.
408-
auto flake = readFlake(state, originalRef, resolvedRef, lockedRef, {accessor}, lockRootAttrPath);
350+
auto flake = readFlake(state, originalRef, resolvedRef, lockedRef, {cachedInput.accessor}, lockRootAttrPath);
409351

410352
// Re-fetch the tree if necessary.
411353
auto newLockedRef = applySelfAttrs(lockedRef, flake);
@@ -414,23 +356,21 @@ static Flake getFlake(
414356
debug("refetching input '%s' due to self attribute", newLockedRef);
415357
// FIXME: need to remove attrs that are invalidated by the changed input attrs, such as 'narHash'.
416358
newLockedRef.input.attrs.erase("narHash");
417-
auto [accessor2, resolvedRef2, lockedRef2] = fetchOrSubstituteTree(
418-
state, newLockedRef, false, flakeCache);
419-
accessor = accessor2;
420-
lockedRef = lockedRef2;
359+
auto cachedInput2 = state.inputCache->getAccessor(state.store, newLockedRef.input, useRegistries);
360+
cachedInput.accessor = cachedInput2.accessor;
361+
lockedRef = FlakeRef(std::move(cachedInput2.lockedInput), newLockedRef.subdir);
421362
}
422363

423364
// Copy the tree to the store.
424-
auto storePath = copyInputToStore(state, lockedRef.input, originalRef.input, accessor);
365+
auto storePath = copyInputToStore(state, lockedRef.input, originalRef.input, cachedInput.accessor);
425366

426367
// Re-parse flake.nix from the store.
427368
return readFlake(state, originalRef, resolvedRef, lockedRef, state.storePath(storePath), lockRootAttrPath);
428369
}
429370

430371
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool useRegistries)
431372
{
432-
FlakeCache flakeCache;
433-
return getFlake(state, originalRef, useRegistries, flakeCache, {});
373+
return getFlake(state, originalRef, useRegistries, {});
434374
}
435375

436376
static LockFile readLockFile(
@@ -452,11 +392,9 @@ LockedFlake lockFlake(
452392
{
453393
experimentalFeatureSettings.require(Xp::Flakes);
454394

455-
FlakeCache flakeCache;
456-
457395
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
458396

459-
auto flake = getFlake(state, topRef, useRegistries, flakeCache, {});
397+
auto flake = getFlake(state, topRef, useRegistries, {});
460398

461399
if (lockFlags.applyNixConfig) {
462400
flake.config.apply(settings);
@@ -631,7 +569,7 @@ LockedFlake lockFlake(
631569
if (auto resolvedPath = resolveRelativePath()) {
632570
return readFlake(state, ref, ref, ref, *resolvedPath, inputAttrPath);
633571
} else {
634-
return getFlake(state, ref, useRegistries, flakeCache, inputAttrPath);
572+
return getFlake(state, ref, useRegistries, inputAttrPath);
635573
}
636574
};
637575

@@ -779,11 +717,12 @@ LockedFlake lockFlake(
779717
if (auto resolvedPath = resolveRelativePath()) {
780718
return {*resolvedPath, *input.ref};
781719
} else {
782-
auto [accessor, resolvedRef, lockedRef] = fetchOrSubstituteTree(
783-
state, *input.ref, useRegistries, flakeCache);
720+
auto cachedInput = state.inputCache->getAccessor(state.store, input.ref->input, useRegistries);
721+
722+
auto lockedRef = FlakeRef(std::move(cachedInput.lockedInput), input.ref->subdir);
784723

785724
// FIXME: allow input to be lazy.
786-
auto storePath = copyInputToStore(state, lockedRef.input, input.ref->input, accessor);
725+
auto storePath = copyInputToStore(state, lockedRef.input, input.ref->input, cachedInput.accessor);
787726

788727
return {state.storePath(storePath), lockedRef};
789728
}

0 commit comments

Comments
 (0)