* Retry a transaction if SQLite returns SQLITE_BUSY. This can happen

even with a very long busy timeout, because SQLITE_BUSY is also
  returned to resolve deadlocks.  This should get rid of random
  "database is locked" errors.  This is kind of hard to test though.
* Fix a horrible bug in deleteFromStore(): deletePathWrapped() should
  be called after committing the transaction, not before, because the
  commit might not succeed.
This commit is contained in:
Eelco Dolstra 2010-12-05 18:23:19 +00:00
parent 365f3028dd
commit de79d23f76
1 changed files with 83 additions and 61 deletions

View File

@ -23,14 +23,23 @@
namespace nix {
class SQLiteError : public Error
{
public:
SQLiteError(sqlite3 * db, const format & f)
: Error(format("%1%: %2%") % f.str() % sqlite3_errmsg(db))
MakeError(SQLiteError, Error);
MakeError(SQLiteBusy, SQLiteError);
static void throwSQLiteError(sqlite3 * db, const format & f)
__attribute__ ((noreturn));
static void throwSQLiteError(sqlite3 * db, const format & f)
{
int err = sqlite3_errcode(db);
if (err == SQLITE_BUSY) {
printMsg(lvlError, "warning: SQLite database is busy");
throw SQLiteBusy(format("%1%: %2%") % f.str() % sqlite3_errmsg(db));
}
else
throw SQLiteError(format("%1%: %2%") % f.str() % sqlite3_errmsg(db));
}
};
SQLite::~SQLite()
@ -499,6 +508,8 @@ void LocalStore::registerValidPath(const ValidPathInfo & info)
ValidPathInfo info2(info);
if (info2.registrationTime == 0) info2.registrationTime = time(0);
while (1) {
try {
SQLiteTxn txn(db);
unsigned long long id = addValidPath(info2);
@ -507,6 +518,12 @@ void LocalStore::registerValidPath(const ValidPathInfo & info)
addReference(id, queryValidPathId(*i));
txn.commit();
break;
} catch (SQLiteBusy & e) {
/* Retry; the `txn' destructor will roll back the current
transaction. */
}
}
}
@ -1248,6 +1265,8 @@ void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFr
assertStorePath(path);
while (1) {
try {
SQLiteTxn txn(db);
if (isValidPath(path)) {
@ -1259,9 +1278,12 @@ void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFr
invalidatePath(path);
}
deletePathWrapped(path, bytesFreed, blocksFreed);
txn.commit();
break;
} catch (SQLiteBusy & e) { };
}
deletePathWrapped(path, bytesFreed, blocksFreed);
}