diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2017-09-05T18·43+0200 |
---|---|---|
committer | Eelco Dolstra <edolstra@gmail.com> | 2017-09-05T18·43+0200 |
commit | 0b606aad46e1d96da36d4831df63ad90f11d21c3 (patch) | |
tree | 5787f4f043f7316a8ad4625772abe9a4da3c1d57 /src/libstore/gc.cc | |
parent | b932ea58ec610830ed3141bb14fbd812aa66b2c1 (diff) |
Add automatic garbage collection
Nix can now automatically run the garbage collector during builds or while adding paths to the store. The option "min-free = <bytes>" specifies that Nix should run the garbage collector whenever free space in the Nix store drops below <bytes>. It will then delete garbage until "max-free" bytes are available. Garbage collection during builds is asynchronous; running builds are not paused and new builds are not blocked. However, there also is a synchronous GC run prior to the first build/substitution. Currently, no old GC roots are deleted (as in "nix-collect-garbage -d").
Diffstat (limited to 'src/libstore/gc.cc')
-rw-r--r-- | src/libstore/gc.cc | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 534db8c6e4fe..cf95f7f450bd 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(); +} + + } |