aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2008-06-13 18:25:24 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2008-06-13 18:25:24 +0000
commit826b271d9aead1a0f8e1678e7c2814066fffb983 (patch)
tree1fbbdebd0a748d9991190c7afb6675857c174834 /src
parent30c9f909b24d64d8fabc2bb450e03744cc69c9a0 (diff)
* Garbage collector: don't do a complete topological sort of the Nix
store under the reference relation, since that means that the garbage collector will need a long time to start deleting paths. Instead just delete the referrers of a path first.
Diffstat (limited to 'src')
-rw-r--r--src/libstore/gc.cc157
-rw-r--r--src/libstore/local-store.hh4
2 files changed, 87 insertions, 74 deletions
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 9ed87d112..b30f78b33 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -435,6 +435,83 @@ Paths topoSortPaths(const PathSet & paths)
}
+void LocalStore::tryToDelete(GCAction action, const PathSet & livePaths,
+ const PathSet & tempRootsClosed, PathSet & done, PathSet & deleted,
+ const Path & path, unsigned long long & bytesFreed)
+{
+ if (done.find(path) != done.end()) return;
+ done.insert(path);
+
+ debug(format("considering deletion of `%1%'") % path);
+
+ if (livePaths.find(path) != livePaths.end()) {
+ if (action == gcDeleteSpecific)
+ throw Error(format("cannot delete path `%1%' since it is still alive") % path);
+ debug(format("live path `%1%'") % path);
+ return;
+ }
+
+ if (tempRootsClosed.find(path) != tempRootsClosed.end()) {
+ debug(format("temporary root `%1%'") % path);
+ return;
+ }
+
+ /* Delete all the referrers first. They must be garbage too,
+ since if they were in the closure of some live path, then this
+ path would also be in the closure. Note that
+ deleteFromStore() below still makes sure that the referrer set
+ has become empty, just in case. */
+ PathSet referrers;
+ if (store->isValidPath(path))
+ queryReferrers(path, referrers);
+ foreach (PathSet::iterator, i, referrers)
+ if (*i != path)
+ tryToDelete(action, livePaths, tempRootsClosed, done, deleted, *i, bytesFreed);
+
+ debug(format("dead path `%1%'") % path);
+ deleted.insert(path);
+
+ /* If just returning the set of dead paths, we also return the
+ space that would be freed if we deleted them. */
+ if (action == gcReturnDead) {
+ bytesFreed += computePathSize(path);
+ return;
+ }
+
+#ifndef __CYGWIN__
+ AutoCloseFD fdLock;
+
+ /* Only delete a lock file if we can acquire a write lock on it.
+ That means that it's either stale, or the process that created
+ it hasn't locked it yet. In the latter case the other process
+ will detect that we deleted the lock, and retry (see
+ pathlocks.cc). */
+ if (path.size() >= 5 && string(path, path.size() - 5) == ".lock") {
+ fdLock = openLockFile(path, false);
+ if (fdLock != -1 && !lockFile(fdLock, ltWrite, false)) {
+ debug(format("skipping active lock `%1%'") % path);
+ return;
+ }
+ }
+#endif
+
+ if (!pathExists(path)) return;
+
+ printMsg(lvlInfo, format("deleting `%1%'") % path);
+
+ /* Okay, it's safe to delete. */
+ unsigned long long freed;
+ deleteFromStore(path, freed);
+ bytesFreed += freed;
+
+#ifndef __CYGWIN__
+ if (fdLock != -1)
+ /* Write token to stale (deleted) lock file. */
+ writeFull(fdLock, (const unsigned char *) "d", 1);
+#endif
+}
+
+
void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed)
{
@@ -551,96 +628,28 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
/* Read the Nix store directory to find all currently existing
paths. */
printMsg(lvlError, format("reading the Nix store..."));
- PathSet storePathSet;
+ PathSet storePaths;
if (action != gcDeleteSpecific) {
Paths entries = readDirectory(nixStore);
for (Paths::iterator i = entries.begin(); i != entries.end(); ++i)
- storePathSet.insert(canonPath(nixStore + "/" + *i));
+ storePaths.insert(canonPath(nixStore + "/" + *i));
} else {
for (PathSet::iterator i = pathsToDelete.begin();
i != pathsToDelete.end(); ++i)
{
assertStorePath(*i);
- storePathSet.insert(*i);
+ storePaths.insert(*i);
}
}
- /* Topologically sort them under the `referrers' relation. That
- is, a < b iff a is in referrers(b). This gives us the order in
- which things can be deleted safely. */
- /* !!! when we have multiple output paths per derivation, this
- will not work anymore because we get cycles. */
- printMsg(lvlError, format("toposorting..."));
- Paths storePaths = topoSortPaths(storePathSet);
-
/* Try to delete store paths in the topologically sorted order. */
printMsg(lvlError, action == gcReturnDead
? format("looking for garbage...")
: format("deleting garbage..."));
-
- for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) {
-
- debug(format("considering deletion of `%1%'") % *i);
-
- if (livePaths.find(*i) != livePaths.end()) {
- if (action == gcDeleteSpecific)
- throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
- debug(format("live path `%1%'") % *i);
- continue;
- }
-
- if (tempRootsClosed.find(*i) != tempRootsClosed.end()) {
- debug(format("temporary root `%1%'") % *i);
- continue;
- }
-
- debug(format("dead path `%1%'") % *i);
- result.insert(*i);
-
- /* If just returning the set of dead paths, we also return the
- space that would be freed if we deleted them. */
- if (action == gcReturnDead)
- bytesFreed += computePathSize(*i);
-
- if (action == gcDeleteDead || action == gcDeleteSpecific) {
-#ifndef __CYGWIN__
- AutoCloseFD fdLock;
-
- /* Only delete a lock file if we can acquire a write lock
- on it. That means that it's either stale, or the
- process that created it hasn't locked it yet. In the
- latter case the other process will detect that we
- deleted the lock, and retry (see pathlocks.cc). */
- if (i->size() >= 5 && string(*i, i->size() - 5) == ".lock") {
- fdLock = openLockFile(*i, false);
- if (fdLock != -1 && !lockFile(fdLock, ltWrite, false)) {
- debug(format("skipping active lock `%1%'") % *i);
- continue;
- }
- }
-#endif
-
- if (!pathExists(*i)) continue;
-
- printMsg(lvlInfo, format("deleting `%1%'") % *i);
-
- /* Okay, it's safe to delete. */
- try {
- unsigned long long freed;
- deleteFromStore(*i, freed);
- bytesFreed += freed;
- } catch (PathInUse & e) {
- printMsg(lvlError, format("warning: %1%") % e.msg());
- }
-
-#ifndef __CYGWIN__
- if (fdLock != -1)
- /* Write token to stale (deleted) lock file. */
- writeFull(fdLock, (const unsigned char *) "d", 1);
-#endif
- }
- }
+ PathSet done;
+ foreach (PathSet::iterator, i, storePaths)
+ tryToDelete(action, livePaths, tempRootsClosed, done, result, *i, bytesFreed);
}
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 57d9d765e..444a5f340 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -143,6 +143,10 @@ private:
void upgradeStore12();
+ void tryToDelete(GCAction action, const PathSet & livePaths,
+ const PathSet & tempRootsClosed, PathSet & done, PathSet & deleted,
+ const Path & path, unsigned long long & bytesFreed);
+
};