aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libstore/build.cc5
-rw-r--r--src/libstore/gc.cc70
-rw-r--r--src/libstore/globals.hh10
-rw-r--r--src/libstore/local-store.cc18
-rw-r--r--src/libstore/local-store.hh21
5 files changed, 123 insertions, 1 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index ce41752e6..ddf4bf00d 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -3957,6 +3957,8 @@ void Worker::run(const Goals & _topGoals)
checkInterrupt();
+ store.autoGC(false);
+
/* Call every wake goal (in the ordering established by
CompareGoalPtrs). */
while (!awake.empty() && !topGoals.empty()) {
@@ -4014,6 +4016,9 @@ void Worker::waitForInput()
is a build timeout, then wait for input until the first
deadline for any child. */
auto nearest = steady_time_point::max(); // nearest deadline
+ if (settings.minFree.get() != 0)
+ // Periodicallty wake up to see if we need to run the garbage collector.
+ nearest = before + std::chrono::seconds(10);
for (auto & i : children) {
if (!i.respectTimeouts) continue;
if (0 != settings.maxSilentTime)
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 534db8c6e..cf95f7f45 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -1,6 +1,7 @@
#include "derivations.hh"
#include "globals.hh"
#include "local-store.hh"
+#include "finally.hh"
#include <functional>
#include <queue>
@@ -9,6 +10,7 @@
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
@@ -845,4 +847,72 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
}
+void LocalStore::autoGC(bool sync)
+{
+ auto getAvail = [this]() {
+ struct statvfs st;
+ if (statvfs(realStoreDir.c_str(), &st))
+ throw SysError("getting filesystem info about '%s'", realStoreDir);
+
+ return (uint64_t) st.f_bavail * st.f_bsize;
+ };
+
+ std::shared_future<void> future;
+
+ {
+ auto state(_state.lock());
+
+ if (state->gcRunning) {
+ future = state->gcFuture;
+ debug("waiting for auto-GC to finish");
+ goto sync;
+ }
+
+ auto now = std::chrono::steady_clock::now();
+
+ if (now < state->lastGCCheck + std::chrono::seconds(5)) return;
+
+ auto avail = getAvail();
+
+ state->lastGCCheck = now;
+
+ if (avail >= settings.minFree || avail >= settings.maxFree) return;
+
+ if (avail > state->availAfterGC * 0.97) return;
+
+ state->gcRunning = true;
+
+ std::promise<void> promise;
+ future = state->gcFuture = promise.get_future().share();
+
+ std::thread([promise{std::move(promise)}, this, avail, getAvail]() mutable {
+
+ /* Wake up any threads waiting for the auto-GC to finish. */
+ Finally wakeup([&]() {
+ auto state(_state.lock());
+ state->gcRunning = false;
+ state->lastGCCheck = std::chrono::steady_clock::now();
+ promise.set_value();
+ });
+
+ printInfo("running auto-GC to free %d bytes", settings.maxFree - avail);
+
+ GCOptions options;
+ options.maxFreed = settings.maxFree - avail;
+
+ GCResults results;
+
+ collectGarbage(options, results);
+
+ _state.lock()->availAfterGC = getAvail();
+
+ }).detach();
+ }
+
+ sync:
+ // Wait for the future outside of the state lock.
+ if (sync) future.get();
+}
+
+
}
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index c20d147f5..41d332311 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -4,8 +4,9 @@
#include "config.hh"
#include <map>
-#include <sys/types.h>
+#include <limits>
+#include <sys/types.h>
namespace nix {
@@ -342,6 +343,13 @@ public:
Setting<Strings> hashedMirrors{this, {"http://tarballs.nixos.org/"}, "hashed-mirrors",
"A list of servers used by builtins.fetchurl to fetch files by hash."};
+
+ Setting<uint64_t> minFree{this, 0, "min-free",
+ "Automatically run the garbage collector when free disk space drops below the specified amount."};
+
+ Setting<uint64_t> maxFree{this, std::numeric_limits<uint64_t>::max(), "max-free",
+ "Stop deleting garbage when free disk space is above the specified amount."};
+
};
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 5ca776099..7afecc1cf 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -244,6 +244,18 @@ LocalStore::LocalStore(const Params & params)
LocalStore::~LocalStore()
{
+ std::shared_future<void> future;
+
+ {
+ auto state(_state.lock());
+ if (state->gcRunning)
+ future = state->gcFuture;
+ }
+
+ if (future.valid()) {
+ printError("waiting for auto-GC to finish on exit...");
+ future.get();
+ }
try {
auto state(_state.lock());
@@ -991,6 +1003,8 @@ void LocalStore::addToStore(const ValidPathInfo & info, const ref<std::string> &
StringSource source(*nar);
restorePath(realPath, source);
+ autoGC();
+
canonicalisePathMetaData(realPath, -1);
optimisePath(realPath); // FIXME: combine with hashPath()
@@ -1025,6 +1039,8 @@ Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
deletePath(realPath);
+ autoGC();
+
if (recursive) {
StringSource source(dump);
restorePath(realPath, source);
@@ -1097,6 +1113,8 @@ Path LocalStore::addTextToStore(const string & name, const string & s,
deletePath(realPath);
+ autoGC();
+
writeFile(realPath, s);
canonicalisePathMetaData(realPath, -1);
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 04519bfca..4973bd9a9 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -7,6 +7,8 @@
#include "sync.hh"
#include "util.hh"
+#include <chrono>
+#include <future>
#include <string>
#include <unordered_set>
@@ -60,6 +62,21 @@ private:
/* The file to which we write our temporary roots. */
AutoCloseFD fdTempRoots;
+
+ /* The last time we checked whether to do an auto-GC, or an
+ auto-GC finished. */
+ std::chrono::time_point<std::chrono::steady_clock> lastGCCheck;
+
+ /* Whether auto-GC is running. If so, get gcFuture to wait for
+ the GC to finish. */
+ bool gcRunning = false;
+ std::shared_future<void> gcFuture;
+
+ /* How much disk space was available after the previous
+ auto-GC. If the current available disk space is below
+ minFree but not much below availAfterGC, then there is no
+ point in starting a new GC. */
+ uint64_t availAfterGC = std::numeric_limits<uint64_t>::max();
};
Sync<State, std::recursive_mutex> _state;
@@ -196,6 +213,10 @@ public:
void addSignatures(const Path & storePath, const StringSet & sigs) override;
+ /* If free disk space in /nix/store if below minFree, delete
+ garbage until it exceeds maxFree. */
+ void autoGC(bool sync = true);
+
private:
int getSchema();