Skip to content

Commit

Permalink
Fix GC when there are cycles in the referrers graph
Browse files Browse the repository at this point in the history
(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.
  • Loading branch information
edolstra committed Oct 13, 2021
1 parent e31a483 commit 35c98a5
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 34 deletions.
74 changes: 43 additions & 31 deletions src/libstore/gc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -555,31 +550,21 @@ 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;
}
}

{
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;
}


Expand Down Expand Up @@ -628,7 +613,6 @@ void LocalStore::removeUnusedLinks(const GCState & state)
}



void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
{
GCState state(options, results);
Expand Down Expand Up @@ -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) {
Expand All @@ -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);

Expand Down
5 changes: 2 additions & 3 deletions src/libstore/local-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 35c98a5

Please sign in to comment.