forked from lix-project/lix
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.
This commit is contained in:
parent
e31a48366f
commit
35c98a59c5
|
@ -499,34 +499,29 @@ void LocalStore::deleteFromStore(GCState & state, std::string_view baseName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LocalStore::tryToDelete(
|
bool LocalStore::canReachRoot(
|
||||||
GCState & state,
|
GCState & state,
|
||||||
StorePathSet & visited,
|
StorePathSet & visited,
|
||||||
const StorePath & path,
|
const StorePath & path)
|
||||||
bool recursive)
|
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
//Activity act(*logger, lvlDebug, format("considering whether to delete '%1%'") % path);
|
//Activity act(*logger, lvlDebug, format("considering whether to delete '%1%'") % path);
|
||||||
|
|
||||||
/* Wake up any client waiting for deletion of this path to
|
if (state.options.action == GCOptions::gcDeleteSpecific
|
||||||
finish. */
|
&& !state.options.pathsToDelete.count(path))
|
||||||
Finally releasePending([&]() {
|
return true;
|
||||||
auto shared(state.shared.lock());
|
|
||||||
shared->pending.reset();
|
|
||||||
state.wakeup.notify_all();
|
|
||||||
});
|
|
||||||
|
|
||||||
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)) {
|
if (state.roots.count(path)) {
|
||||||
debug("cannot delete '%s' because it's a root", printStorePath(path));
|
debug("cannot delete '%s' because it's a root", printStorePath(path));
|
||||||
state.alive.insert(path);
|
state.alive.insert(path);
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isValidPath(path)) {
|
if (isValidPath(path)) {
|
||||||
|
@ -555,12 +550,9 @@ bool LocalStore::tryToDelete(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & i : incoming)
|
for (auto & i : incoming)
|
||||||
if (i != path
|
if (i != path && canReachRoot(state, visited, i)) {
|
||||||
&& (recursive || state.options.pathsToDelete.count(i))
|
|
||||||
&& !tryToDelete(state, visited, i, recursive))
|
|
||||||
{
|
|
||||||
state.alive.insert(path);
|
state.alive.insert(path);
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,18 +560,11 @@ bool LocalStore::tryToDelete(
|
||||||
auto hashPart = std::string(path.hashPart());
|
auto hashPart = std::string(path.hashPart());
|
||||||
auto shared(state.shared.lock());
|
auto shared(state.shared.lock());
|
||||||
if (shared->tempRoots.count(hashPart))
|
if (shared->tempRoots.count(hashPart))
|
||||||
return false;
|
return true;
|
||||||
shared->pending = hashPart;
|
shared->pending = hashPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.dead.insert(path);
|
return false;
|
||||||
|
|
||||||
if (state.shouldDelete) {
|
|
||||||
invalidatePathChecked(path);
|
|
||||||
deleteFromStore(state, path.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -628,7 +613,6 @@ void LocalStore::removeUnusedLinks(const GCState & state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
{
|
{
|
||||||
GCState state(options, results);
|
GCState state(options, results);
|
||||||
|
@ -769,12 +753,21 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
|
|
||||||
for (auto & i : options.pathsToDelete) {
|
for (auto & i : options.pathsToDelete) {
|
||||||
StorePathSet visited;
|
StorePathSet visited;
|
||||||
if (!tryToDelete(state, visited, i, false))
|
|
||||||
|
if (canReachRoot(state, visited, i))
|
||||||
throw Error(
|
throw Error(
|
||||||
"cannot delete path '%1%' since it is still alive. "
|
"cannot delete path '%1%' since it is still alive. "
|
||||||
"To find out why, use: "
|
"To find out why, use: "
|
||||||
"nix-store --query --roots",
|
"nix-store --query --roots",
|
||||||
printStorePath(i));
|
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) {
|
} else if (options.maxFreed > 0) {
|
||||||
|
@ -801,7 +794,26 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
|
|
||||||
if (auto storePath = maybeParseStorePath(storeDir + "/" + name)) {
|
if (auto storePath = maybeParseStorePath(storeDir + "/" + name)) {
|
||||||
StorePathSet visited;
|
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
|
} else
|
||||||
deleteFromStore(state, name);
|
deleteFromStore(state, name);
|
||||||
|
|
||||||
|
|
|
@ -240,11 +240,10 @@ private:
|
||||||
|
|
||||||
struct GCState;
|
struct GCState;
|
||||||
|
|
||||||
bool tryToDelete(
|
bool canReachRoot(
|
||||||
GCState & state,
|
GCState & state,
|
||||||
StorePathSet & visited,
|
StorePathSet & visited,
|
||||||
const StorePath & path,
|
const StorePath & path);
|
||||||
bool recursive);
|
|
||||||
|
|
||||||
void deleteFromStore(GCState & state, std::string_view baseName);
|
void deleteFromStore(GCState & state, std::string_view baseName);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue