mirror of
https://github.com/NixOS/nix.git
synced 2025-11-26 04:00:59 +01:00
* Combine referrer update to prevent quadratic complexity in the
garbage collector.
This commit is contained in:
parent
af7fd0b21f
commit
10c6e2a629
3 changed files with 54 additions and 19 deletions
|
|
@ -82,6 +82,11 @@ LocalStore::LocalStore()
|
||||||
|
|
||||||
LocalStore::~LocalStore()
|
LocalStore::~LocalStore()
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
|
flushDelayedUpdates();
|
||||||
|
} catch (...) {
|
||||||
|
ignoreException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -211,21 +216,28 @@ static void appendReferrer(const Path & from, const Path & to)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Atomically update the referrers file. */
|
/* Atomically update the referrers file. If `purge' is true, the set
|
||||||
static void rewriteReferrers(const Path & path, const PathSet & referrers)
|
of referrers is set to `referrers'. Otherwise, the current set of
|
||||||
|
referrers is purged of invalid paths. */
|
||||||
|
void LocalStore::rewriteReferrers(const Path & path, bool purge, PathSet referrers)
|
||||||
{
|
{
|
||||||
Path referrersFile = referrersFileFor(path);
|
Path referrersFile = referrersFileFor(path);
|
||||||
|
|
||||||
PathLocks referrersLock(singleton<PathSet, Path>(referrersFile));
|
PathLocks referrersLock(singleton<PathSet, Path>(referrersFile));
|
||||||
referrersLock.setDeletion(true);
|
referrersLock.setDeletion(true);
|
||||||
|
|
||||||
|
if (purge)
|
||||||
|
/* queryReferrers() purges invalid paths, so that's all we
|
||||||
|
need. */
|
||||||
|
queryReferrers(path, referrers);
|
||||||
|
|
||||||
Path tmpFile = tmpFileForAtomicUpdate(referrersFile);
|
Path tmpFile = tmpFileForAtomicUpdate(referrersFile);
|
||||||
|
|
||||||
AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
|
AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
|
||||||
if (fd == -1) throw SysError(format("opening file `%1%'") % referrersFile);
|
if (fd == -1) throw SysError(format("opening file `%1%'") % referrersFile);
|
||||||
|
|
||||||
string s;
|
string s;
|
||||||
for (PathSet::const_iterator i = referrers.begin(); i != referrers.end(); ++i) {
|
foreach (PathSet::const_iterator, i, referrers) {
|
||||||
s += " "; s += *i;
|
s += " "; s += *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,6 +250,15 @@ static void rewriteReferrers(const Path & path, const PathSet & referrers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LocalStore::flushDelayedUpdates()
|
||||||
|
{
|
||||||
|
foreach (PathSet::iterator, i, delayedUpdates) {
|
||||||
|
rewriteReferrers(*i, true, PathSet());
|
||||||
|
}
|
||||||
|
delayedUpdates.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::registerValidPath(const Path & path,
|
void LocalStore::registerValidPath(const Path & path,
|
||||||
const Hash & hash, const PathSet & references,
|
const Hash & hash, const PathSet & references,
|
||||||
const Path & deriver)
|
const Path & deriver)
|
||||||
|
|
@ -501,10 +522,6 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define foreach(it_type, it, collection) \
|
|
||||||
for (it_type it = collection.begin(); it != collection.end(); ++it)
|
|
||||||
|
|
||||||
|
|
||||||
/* Invalidate a path. The caller is responsible for checking that
|
/* Invalidate a path. The caller is responsible for checking that
|
||||||
there are no referrers. */
|
there are no referrers. */
|
||||||
void LocalStore::invalidatePath(const Path & path)
|
void LocalStore::invalidatePath(const Path & path)
|
||||||
|
|
@ -529,18 +546,25 @@ void LocalStore::invalidatePath(const Path & path)
|
||||||
|
|
||||||
/* Clear `path' from the info cache. */
|
/* Clear `path' from the info cache. */
|
||||||
pathInfoCache.erase(path);
|
pathInfoCache.erase(path);
|
||||||
|
delayedUpdates.erase(path);
|
||||||
|
|
||||||
/* Remove the corresponding referrer entries for each path
|
/* Cause the referrer files for each path referenced by this one
|
||||||
referenced by this one. This has to happen after removing the
|
to be updated. This has to happen after removing the info file
|
||||||
info file to preserve the invariant (see
|
to preserve the invariant (see registerValidPath()).
|
||||||
registerValidPath()). */
|
|
||||||
foreach (PathSet::iterator, i, info.references) {
|
The referrer files are updated lazily in flushDelayedUpdates()
|
||||||
/* !!! O(n) */
|
to prevent quadratic performance in the garbage collector
|
||||||
if (*i == path) continue; /* self-reference */
|
(i.e., when N referrers to some path X are deleted, we have to
|
||||||
PathSet referrers; queryReferrers(*i, referrers);
|
rewrite the referrers file for X N times, causing O(N^2) I/O).
|
||||||
referrers.erase(path);
|
|
||||||
rewriteReferrers(*i, referrers);
|
What happens if we die before the referrer file can be updated?
|
||||||
}
|
That's not a problem, because stale (invalid) entries in the
|
||||||
|
referrer file are ignored by queryReferrers(). Thus a referrer
|
||||||
|
file is allowed to have stale entries; removing them is just an
|
||||||
|
optimisation. verifyStore() gets rid of them eventually.
|
||||||
|
*/
|
||||||
|
foreach (PathSet::iterator, i, info.references)
|
||||||
|
if (*i != path) delayedUpdates.insert(*i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -943,7 +967,7 @@ void LocalStore::verifyStore(bool checkContents)
|
||||||
} else referrersNew.insert(*j);
|
} else referrersNew.insert(*j);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update) rewriteReferrers(from, referrersNew);
|
if (update) rewriteReferrers(from, false, referrersNew);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,10 +118,17 @@ private:
|
||||||
every once in a while. */
|
every once in a while. */
|
||||||
std::map<Path, ValidPathInfo> pathInfoCache;
|
std::map<Path, ValidPathInfo> pathInfoCache;
|
||||||
|
|
||||||
|
/* Store paths for which the referrers file must be purged. */
|
||||||
|
PathSet delayedUpdates;
|
||||||
|
|
||||||
void registerValidPath(const ValidPathInfo & info);
|
void registerValidPath(const ValidPathInfo & info);
|
||||||
|
|
||||||
ValidPathInfo queryPathInfo(const Path & path);
|
ValidPathInfo queryPathInfo(const Path & path);
|
||||||
|
|
||||||
|
void rewriteReferrers(const Path & path, bool purge, PathSet referrers);
|
||||||
|
|
||||||
|
void flushDelayedUpdates();
|
||||||
|
|
||||||
bool queryReferrersInternal(const Path & path, PathSet & referrers);
|
bool queryReferrersInternal(const Path & path, PathSet & referrers);
|
||||||
|
|
||||||
void invalidatePath(const Path & path);
|
void invalidatePath(const Path & path);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,10 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
#define foreach(it_type, it, collection) \
|
||||||
|
for (it_type it = collection.begin(); it != collection.end(); ++it)
|
||||||
|
|
||||||
|
|
||||||
/* Return an environment variable. */
|
/* Return an environment variable. */
|
||||||
string getEnv(const string & key, const string & def = "");
|
string getEnv(const string & key, const string & def = "");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue