* 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

View file

@ -23,14 +23,23 @@
namespace nix { namespace nix {
class SQLiteError : public Error MakeError(SQLiteError, Error);
{ MakeError(SQLiteBusy, SQLiteError);
public:
SQLiteError(sqlite3 * db, const format & f)
: Error(format("%1%: %2%") % f.str() % sqlite3_errmsg(db)) 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() SQLite::~SQLite()
@ -499,6 +508,8 @@ void LocalStore::registerValidPath(const ValidPathInfo & info)
ValidPathInfo info2(info); ValidPathInfo info2(info);
if (info2.registrationTime == 0) info2.registrationTime = time(0); if (info2.registrationTime == 0) info2.registrationTime = time(0);
while (1) {
try {
SQLiteTxn txn(db); SQLiteTxn txn(db);
unsigned long long id = addValidPath(info2); unsigned long long id = addValidPath(info2);
@ -507,6 +518,12 @@ void LocalStore::registerValidPath(const ValidPathInfo & info)
addReference(id, queryValidPathId(*i)); addReference(id, queryValidPathId(*i));
txn.commit(); 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); assertStorePath(path);
while (1) {
try {
SQLiteTxn txn(db); SQLiteTxn txn(db);
if (isValidPath(path)) { if (isValidPath(path)) {
@ -1259,9 +1278,12 @@ void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFr
invalidatePath(path); invalidatePath(path);
} }
deletePathWrapped(path, bytesFreed, blocksFreed);
txn.commit(); txn.commit();
break;
} catch (SQLiteBusy & e) { };
}
deletePathWrapped(path, bytesFreed, blocksFreed);
} }