* Wrap deleteFromStore() in a transaction. Otherwise there might be a
race with other processes that add new referrers to a path, resulting in the garbage collector crashing with "foreign key constraint failed". (Nix/4) * Make --gc --print-dead etc. interruptible.
This commit is contained in:
parent
bfa6ee7d91
commit
64fd29855a
2 changed files with 13 additions and 8 deletions
|
@ -421,7 +421,7 @@ struct LocalStore::GCState
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static bool doDelete(GCOptions::GCAction action)
|
static bool shouldDelete(GCOptions::GCAction action)
|
||||||
{
|
{
|
||||||
return action == GCOptions::gcDeleteDead
|
return action == GCOptions::gcDeleteDead
|
||||||
|| action == GCOptions::gcDeleteSpecific;
|
|| action == GCOptions::gcDeleteSpecific;
|
||||||
|
@ -438,6 +438,8 @@ bool LocalStore::isActiveTempFile(const GCState & state,
|
||||||
|
|
||||||
bool LocalStore::tryToDelete(GCState & state, const Path & path)
|
bool LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
if (!pathExists(path)) return true;
|
if (!pathExists(path)) return true;
|
||||||
if (state.deleted.find(path) != state.deleted.end()) return true;
|
if (state.deleted.find(path) != state.deleted.end()) return true;
|
||||||
if (state.live.find(path) != state.live.end()) return false;
|
if (state.live.find(path) != state.live.end()) return false;
|
||||||
|
@ -516,7 +518,7 @@ bool LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The path is garbage, so delete it. */
|
/* The path is garbage, so delete it. */
|
||||||
if (doDelete(state.options.action)) {
|
if (shouldDelete(state.options.action)) {
|
||||||
printMsg(lvlInfo, format("deleting `%1%'") % path);
|
printMsg(lvlInfo, format("deleting `%1%'") % path);
|
||||||
|
|
||||||
unsigned long long bytesFreed, blocksFreed;
|
unsigned long long bytesFreed, blocksFreed;
|
||||||
|
@ -625,7 +627,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
vector<Path> entries_(entries.begin(), entries.end());
|
vector<Path> entries_(entries.begin(), entries.end());
|
||||||
random_shuffle(entries_.begin(), entries_.end());
|
random_shuffle(entries_.begin(), entries_.end());
|
||||||
|
|
||||||
if (doDelete(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..."));
|
||||||
|
|
|
@ -298,11 +298,10 @@ void LocalStore::openDB(bool create)
|
||||||
if (sqlite3_exec(db, ("pragma synchronous = " + syncMode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, ("pragma synchronous = " + syncMode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
||||||
throw SQLiteError(db, "setting synchronous mode");
|
throw SQLiteError(db, "setting synchronous mode");
|
||||||
|
|
||||||
/* Set the SQLite journal mode. The default is write-ahead
|
/* Set the SQLite journal mode. WAL mode is fastest, but doesn't
|
||||||
logging since it's the fastest and supports more concurrency.
|
seem entirely stable at the moment (Oct. 2010). Thus, use
|
||||||
The downside is that it doesn't work over NFS, so allow
|
truncate mode by default. */
|
||||||
truncate mode alternatively. */
|
string mode = queryBoolSetting("use-sqlite-wal", false) ? "wal" : "truncate";
|
||||||
string mode = queryBoolSetting("use-sqlite-wal", true) ? "wal" : "truncate";
|
|
||||||
string prevMode;
|
string prevMode;
|
||||||
{
|
{
|
||||||
SQLiteStmt stmt;
|
SQLiteStmt stmt;
|
||||||
|
@ -1220,6 +1219,8 @@ void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFr
|
||||||
|
|
||||||
assertStorePath(path);
|
assertStorePath(path);
|
||||||
|
|
||||||
|
SQLiteTxn txn(db);
|
||||||
|
|
||||||
if (isValidPath(path)) {
|
if (isValidPath(path)) {
|
||||||
PathSet referrers; queryReferrers(path, referrers);
|
PathSet referrers; queryReferrers(path, referrers);
|
||||||
referrers.erase(path); /* ignore self-references */
|
referrers.erase(path); /* ignore self-references */
|
||||||
|
@ -1230,6 +1231,8 @@ void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFr
|
||||||
}
|
}
|
||||||
|
|
||||||
deletePathWrapped(path, bytesFreed, blocksFreed);
|
deletePathWrapped(path, bytesFreed, blocksFreed);
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue