libstore: turn periodic gc attempt into a promise

notably we will check whether we want to do GC at all only once during
startup, and we'll only attempt GC every ten seconds rather than every
time a goal has finished a partial work call. this shouldn't cause any
problems in practice since relying on auto-gc is not deterministic and
stores in which builds can fill all remaining free space in merely ten
seconds are severely troubled even when gargage collection runs a lot.

Change-Id: I1175a56bf7f4e531f8be90157ad88750ff2ddec4
This commit is contained in:
eldritch horrors 2024-09-30 01:31:30 +02:00
parent b0c7c1ec66
commit d5db0b1abc
2 changed files with 22 additions and 16 deletions

View file

@ -298,6 +298,13 @@ std::vector<GoalPtr> Worker::run(std::function<Targets (GoalFactory &)> req)
} }
auto promise = runImpl(); auto promise = runImpl();
// TODO GC interface?
if (auto localStore = dynamic_cast<LocalStore *>(&store); localStore && settings.minFree != 0) {
// Periodically wake up to see if we need to run the garbage collector.
promise = promise.exclusiveJoin(boopGC(*localStore));
}
promise.wait(aio.waitScope).value(); promise.wait(aio.waitScope).value();
std::vector<GoalPtr> results; std::vector<GoalPtr> results;
@ -315,10 +322,6 @@ try {
checkInterrupt(); checkInterrupt();
// TODO GC interface?
if (auto localStore = dynamic_cast<LocalStore *>(&store))
localStore->autoGC(false);
/* Call every wake goal (in the ordering established by /* Call every wake goal (in the ordering established by
CompareGoalPtrs). */ CompareGoalPtrs). */
while (!awake.empty() && !topGoals.empty()) { while (!awake.empty() && !topGoals.empty()) {
@ -356,22 +359,23 @@ try {
co_return result::failure(std::current_exception()); co_return result::failure(std::current_exception());
} }
kj::Promise<Result<void>> Worker::boopGC(LocalStore & localStore)
try {
while (true) {
co_await aio.provider->getTimer().afterDelay(10 * kj::SECONDS);
localStore.autoGC(false);
}
} catch (...) {
co_return result::failure(std::current_exception());
}
kj::Promise<Result<void>> Worker::waitForInput() kj::Promise<Result<void>> Worker::waitForInput()
try { try {
printMsg(lvlVomit, "waiting for children"); printMsg(lvlVomit, "waiting for children");
auto waitFor = [&]{
auto pair = kj::newPromiseAndFulfiller<void>(); auto pair = kj::newPromiseAndFulfiller<void>();
this->childFinished = kj::mv(pair.fulfiller); this->childFinished = kj::mv(pair.fulfiller);
return kj::mv(pair.promise); co_await pair.promise;
}();
if (settings.minFree.get() != 0) {
// Periodicallty wake up to see if we need to run the garbage collector.
waitFor = waitFor.exclusiveJoin(aio.provider->getTimer().afterDelay(10 * kj::SECONDS));
}
co_await waitFor;
co_return result::success(); co_return result::success();
} catch (...) { } catch (...) {
co_return result::failure(std::current_exception()); co_return result::failure(std::current_exception());

View file

@ -20,6 +20,7 @@ namespace nix {
struct DerivationGoal; struct DerivationGoal;
struct PathSubstitutionGoal; struct PathSubstitutionGoal;
class DrvOutputSubstitutionGoal; class DrvOutputSubstitutionGoal;
class LocalStore;
typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point; typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;
@ -189,6 +190,7 @@ private:
} }
kj::Promise<Result<void>> runImpl(); kj::Promise<Result<void>> runImpl();
kj::Promise<Result<void>> boopGC(LocalStore & localStore);
public: public: