aboutsummaryrefslogtreecommitdiff
path: root/src/libstore
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2021-10-08 16:58:19 +0200
committerEelco Dolstra <edolstra@gmail.com>2021-10-13 12:12:44 +0200
commit35c98a59c5f09d8ca376ac809e87d14ff2fcbde1 (patch)
tree402335e765afa28d07d10144a945156c22ded048 /src/libstore
parente31a48366f19d5fa452df6273e817a4ea5516e50 (diff)
Fix GC when there are cycles in the referrers graph
(where "referrers" includes the reverse of derivation outputs and derivers). Now we do a full traversal to look if we can reach any root. If not, all paths reached can be deleted.
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/gc.cc74
-rw-r--r--src/libstore/local-store.hh5
2 files changed, 45 insertions, 34 deletions
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 969066092..4123a90be 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -499,34 +499,29 @@ void LocalStore::deleteFromStore(GCState & state, std::string_view baseName)
}
-bool LocalStore::tryToDelete(
+bool LocalStore::canReachRoot(
GCState & state,
StorePathSet & visited,
- const StorePath & path,
- bool recursive)
+ const StorePath & path)
{
checkInterrupt();
//Activity act(*logger, lvlDebug, format("considering whether to delete '%1%'") % path);
- /* Wake up any client waiting for deletion of this path to
- finish. */
- Finally releasePending([&]() {
- auto shared(state.shared.lock());
- shared->pending.reset();
- state.wakeup.notify_all();
- });
+ if (state.options.action == GCOptions::gcDeleteSpecific
+ && !state.options.pathsToDelete.count(path))
+ return true;
- if (!visited.insert(path).second) return true;
+ if (!visited.insert(path).second) return false;
- if (state.alive.count(path)) return false;
+ if (state.alive.count(path)) return true;
- if (state.dead.count(path)) return true;
+ if (state.dead.count(path)) return false;
if (state.roots.count(path)) {
debug("cannot delete '%s' because it's a root", printStorePath(path));
state.alive.insert(path);
- return false;
+ return true;
}
if (isValidPath(path)) {
@@ -555,12 +550,9 @@ bool LocalStore::tryToDelete(
}
for (auto & i : incoming)
- if (i != path
- && (recursive || state.options.pathsToDelete.count(i))
- && !tryToDelete(state, visited, i, recursive))
- {
+ if (i != path && canReachRoot(state, visited, i)) {
state.alive.insert(path);
- return false;
+ return true;
}
}
@@ -568,18 +560,11 @@ bool LocalStore::tryToDelete(
auto hashPart = std::string(path.hashPart());
auto shared(state.shared.lock());
if (shared->tempRoots.count(hashPart))
- return false;
+ return true;
shared->pending = hashPart;
}
- state.dead.insert(path);
-
- if (state.shouldDelete) {
- invalidatePathChecked(path);
- deleteFromStore(state, path.to_string());
- }
-
- return true;
+ return false;
}
@@ -628,7 +613,6 @@ void LocalStore::removeUnusedLinks(const GCState & state)
}
-
void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
{
GCState state(options, results);
@@ -769,12 +753,21 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
for (auto & i : options.pathsToDelete) {
StorePathSet visited;
- if (!tryToDelete(state, visited, i, false))
+
+ if (canReachRoot(state, visited, i))
throw Error(
"cannot delete path '%1%' since it is still alive. "
"To find out why, use: "
"nix-store --query --roots",
printStorePath(i));
+
+ auto sorted = topoSortPaths(visited);
+ for (auto & path : sorted) {
+ if (state.dead.count(path)) continue;
+ invalidatePathChecked(path);
+ deleteFromStore(state, path.to_string());
+ state.dead.insert(path);
+ }
}
} else if (options.maxFreed > 0) {
@@ -801,7 +794,26 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
if (auto storePath = maybeParseStorePath(storeDir + "/" + name)) {
StorePathSet visited;
- tryToDelete(state, visited, *storePath, true);
+
+ /* Wake up any GC client waiting for deletion of
+ the paths in 'visited' to finish. */
+ Finally releasePending([&]() {
+ auto shared(state.shared.lock());
+ shared->pending.reset();
+ state.wakeup.notify_all();
+ });
+
+ if (!canReachRoot(state, visited, *storePath)) {
+ auto sorted = topoSortPaths(visited);
+ for (auto & path : sorted) {
+ if (state.dead.count(path)) continue;
+ if (state.shouldDelete) {
+ invalidatePathChecked(path);
+ deleteFromStore(state, path.to_string());
+ }
+ state.dead.insert(path);
+ }
+ }
} else
deleteFromStore(state, name);
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 08bbc1a7d..8629e1528 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -240,11 +240,10 @@ private:
struct GCState;
- bool tryToDelete(
+ bool canReachRoot(
GCState & state,
StorePathSet & visited,
- const StorePath & path,
- bool recursive);
+ const StorePath & path);
void deleteFromStore(GCState & state, std::string_view baseName);