* In the garbage collector, delete invalid paths before deleting

unreachable paths.  This matters when using --max-freed etc.:
  unreachable paths could become reachable again, so it's nicer to
  keep them if there is "real" garbage to be deleted.  Also, don't use
  readDirectory() but read the Nix store and delete invalid paths in
  parallel.  This reduces GC latency on very large Nix stores.
This commit is contained in:
Eelco Dolstra 2011-12-22 15:55:53 +00:00
parent 58d974336c
commit b33da599c5
3 changed files with 45 additions and 12 deletions

View file

@ -617,27 +617,51 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
} else { } else {
printMsg(lvlError, format("reading the Nix store..."));
Paths entries = readDirectory(nixStore);
/* Randomise the order in which we delete entries to make the
collector less biased towards deleting paths that come
alphabetically first (e.g. /nix/store/000...). This
matters when using --max-freed etc. */
vector<Path> entries_(entries.begin(), entries.end());
random_shuffle(entries_.begin(), entries_.end());
if (shouldDelete(state.options.action)) if (shouldDelete(state.options.action))
printMsg(lvlError, format("deleting garbage...")); printMsg(lvlError, format("deleting garbage..."));
else else
printMsg(lvlError, format("determining live/dead paths...")); printMsg(lvlError, format("determining live/dead paths..."));
try { try {
AutoCloseDir dir = opendir(nixStore.c_str());
if (!dir) throw SysError(format("opening directory `%1%'") % nixStore);
/* Read the store and immediately delete all paths that
aren't valid. When using --max-freed etc., deleting
invalid paths is preferred over deleting unreachable
paths, since unreachable paths could become reachable
again. We don't use readDirectory() here so that GCing
can start faster. */
Paths entries;
struct dirent * dirent;
while (errno = 0, dirent = readdir(dir)) {
checkInterrupt();
string name = dirent->d_name;
if (name == "." || name == "..") continue;
Path path = nixStore + "/" + name;
if (isValidPath(path))
entries.push_back(path);
else
tryToDelete(state, path);
}
dir.close();
/* Now delete the unreachable valid paths. Randomise the
order in which we delete entries to make the collector
less biased towards deleting paths that come
alphabetically first (e.g. /nix/store/000...). This
matters when using --max-freed etc. */
vector<Path> entries_(entries.begin(), entries.end());
random_shuffle(entries_.begin(), entries_.end());
foreach (vector<Path>::iterator, i, entries_) foreach (vector<Path>::iterator, i, entries_)
tryToDelete(state, nixStore + "/" + *i); tryToDelete(state, nixStore + "/" + *i);
} catch (GCLimitReached & e) { } catch (GCLimitReached & e) {
} }
} }
} }

View file

@ -701,7 +701,7 @@ AutoCloseDir::AutoCloseDir(DIR * dir)
AutoCloseDir::~AutoCloseDir() AutoCloseDir::~AutoCloseDir()
{ {
if (dir) closedir(dir); close();
} }
@ -717,6 +717,14 @@ AutoCloseDir::operator DIR *()
} }
void AutoCloseDir::close()
{
if (dir) {
closedir(dir);
dir = 0;
}
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View file

@ -223,6 +223,7 @@ public:
~AutoCloseDir(); ~AutoCloseDir();
void operator =(DIR * dir); void operator =(DIR * dir);
operator DIR *(); operator DIR *();
void close();
}; };