Retry all SQLite operations

To deal with SQLITE_PROTOCOL, we also need to retry read-only
operations.
This commit is contained in:
Eelco Dolstra 2013-10-16 15:58:20 +02:00
parent ff02f5336c
commit a737f51fd9
2 changed files with 168 additions and 127 deletions

View File

@ -692,56 +692,64 @@ void LocalStore::addReference(unsigned long long referrer, unsigned long long re
void LocalStore::registerFailedPath(const Path & path) void LocalStore::registerFailedPath(const Path & path)
{ {
SQLiteStmtUse use(stmtRegisterFailedPath); retry_sqlite {
stmtRegisterFailedPath.bind(path); SQLiteStmtUse use(stmtRegisterFailedPath);
stmtRegisterFailedPath.bind(time(0)); stmtRegisterFailedPath.bind(path);
if (sqlite3_step(stmtRegisterFailedPath) != SQLITE_DONE) stmtRegisterFailedPath.bind(time(0));
throwSQLiteError(db, format("registering failed path `%1%'") % path); if (sqlite3_step(stmtRegisterFailedPath) != SQLITE_DONE)
throwSQLiteError(db, format("registering failed path `%1%'") % path);
} end_retry_sqlite;
} }
bool LocalStore::hasPathFailed(const Path & path) bool LocalStore::hasPathFailed(const Path & path)
{ {
SQLiteStmtUse use(stmtHasPathFailed); retry_sqlite {
stmtHasPathFailed.bind(path); SQLiteStmtUse use(stmtHasPathFailed);
int res = sqlite3_step(stmtHasPathFailed); stmtHasPathFailed.bind(path);
if (res != SQLITE_DONE && res != SQLITE_ROW) int res = sqlite3_step(stmtHasPathFailed);
throwSQLiteError(db, "querying whether path failed"); if (res != SQLITE_DONE && res != SQLITE_ROW)
return res == SQLITE_ROW; throwSQLiteError(db, "querying whether path failed");
return res == SQLITE_ROW;
} end_retry_sqlite;
} }
PathSet LocalStore::queryFailedPaths() PathSet LocalStore::queryFailedPaths()
{ {
SQLiteStmtUse use(stmtQueryFailedPaths); retry_sqlite {
SQLiteStmtUse use(stmtQueryFailedPaths);
PathSet res; PathSet res;
int r; int r;
while ((r = sqlite3_step(stmtQueryFailedPaths)) == SQLITE_ROW) { while ((r = sqlite3_step(stmtQueryFailedPaths)) == SQLITE_ROW) {
const char * s = (const char *) sqlite3_column_text(stmtQueryFailedPaths, 0); const char * s = (const char *) sqlite3_column_text(stmtQueryFailedPaths, 0);
assert(s); assert(s);
res.insert(s); res.insert(s);
} }
if (r != SQLITE_DONE) if (r != SQLITE_DONE)
throwSQLiteError(db, "error querying failed paths"); throwSQLiteError(db, "error querying failed paths");
return res; return res;
} end_retry_sqlite;
} }
void LocalStore::clearFailedPaths(const PathSet & paths) void LocalStore::clearFailedPaths(const PathSet & paths)
{ {
SQLiteTxn txn(db); retry_sqlite {
SQLiteTxn txn(db);
foreach (PathSet::const_iterator, i, paths) { foreach (PathSet::const_iterator, i, paths) {
SQLiteStmtUse use(stmtClearFailedPath); SQLiteStmtUse use(stmtClearFailedPath);
stmtClearFailedPath.bind(*i); stmtClearFailedPath.bind(*i);
if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE) if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE)
throwSQLiteError(db, format("clearing failed path `%1%' in database") % *i); throwSQLiteError(db, format("clearing failed path `%1%' in database") % *i);
} }
txn.commit(); txn.commit();
} end_retry_sqlite;
} }
@ -766,44 +774,47 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
assertStorePath(path); assertStorePath(path);
/* Get the path info. */ retry_sqlite {
SQLiteStmtUse use1(stmtQueryPathInfo);
stmtQueryPathInfo.bind(path); /* Get the path info. */
SQLiteStmtUse use1(stmtQueryPathInfo);
int r = sqlite3_step(stmtQueryPathInfo); stmtQueryPathInfo.bind(path);
if (r == SQLITE_DONE) throw Error(format("path `%1%' is not valid") % path);
if (r != SQLITE_ROW) throwSQLiteError(db, "querying path in database");
info.id = sqlite3_column_int(stmtQueryPathInfo, 0); int r = sqlite3_step(stmtQueryPathInfo);
if (r == SQLITE_DONE) throw Error(format("path `%1%' is not valid") % path);
if (r != SQLITE_ROW) throwSQLiteError(db, "querying path in database");
const char * s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 1); info.id = sqlite3_column_int(stmtQueryPathInfo, 0);
assert(s);
info.hash = parseHashField(path, s);
info.registrationTime = sqlite3_column_int(stmtQueryPathInfo, 2); const char * s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 1);
s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
if (s) info.deriver = s;
/* Note that narSize = NULL yields 0. */
info.narSize = sqlite3_column_int64(stmtQueryPathInfo, 4);
/* Get the references. */
SQLiteStmtUse use2(stmtQueryReferences);
stmtQueryReferences.bind(info.id);
while ((r = sqlite3_step(stmtQueryReferences)) == SQLITE_ROW) {
s = (const char *) sqlite3_column_text(stmtQueryReferences, 0);
assert(s); assert(s);
info.references.insert(s); info.hash = parseHashField(path, s);
}
if (r != SQLITE_DONE) info.registrationTime = sqlite3_column_int(stmtQueryPathInfo, 2);
throwSQLiteError(db, format("error getting references of `%1%'") % path);
return info; s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
if (s) info.deriver = s;
/* Note that narSize = NULL yields 0. */
info.narSize = sqlite3_column_int64(stmtQueryPathInfo, 4);
/* Get the references. */
SQLiteStmtUse use2(stmtQueryReferences);
stmtQueryReferences.bind(info.id);
while ((r = sqlite3_step(stmtQueryReferences)) == SQLITE_ROW) {
s = (const char *) sqlite3_column_text(stmtQueryReferences, 0);
assert(s);
info.references.insert(s);
}
if (r != SQLITE_DONE)
throwSQLiteError(db, format("error getting references of `%1%'") % path);
return info;
} end_retry_sqlite;
} }
@ -834,7 +845,7 @@ unsigned long long LocalStore::queryValidPathId(const Path & path)
} }
bool LocalStore::isValidPath(const Path & path) bool LocalStore::isValidPath_(const Path & path)
{ {
SQLiteStmtUse use(stmtQueryPathInfo); SQLiteStmtUse use(stmtQueryPathInfo);
stmtQueryPathInfo.bind(path); stmtQueryPathInfo.bind(path);
@ -845,33 +856,44 @@ bool LocalStore::isValidPath(const Path & path)
} }
bool LocalStore::isValidPath(const Path & path)
{
retry_sqlite {
return isValidPath_(path);
} end_retry_sqlite;
}
PathSet LocalStore::queryValidPaths(const PathSet & paths) PathSet LocalStore::queryValidPaths(const PathSet & paths)
{ {
PathSet res; retry_sqlite {
foreach (PathSet::const_iterator, i, paths) PathSet res;
if (isValidPath(*i)) res.insert(*i); foreach (PathSet::const_iterator, i, paths)
return res; if (isValidPath_(*i)) res.insert(*i);
return res;
} end_retry_sqlite;
} }
PathSet LocalStore::queryAllValidPaths() PathSet LocalStore::queryAllValidPaths()
{ {
SQLiteStmt stmt; retry_sqlite {
stmt.create(db, "select path from ValidPaths"); SQLiteStmt stmt;
stmt.create(db, "select path from ValidPaths");
PathSet res; PathSet res;
int r;
while ((r = sqlite3_step(stmt)) == SQLITE_ROW) {
const char * s = (const char *) sqlite3_column_text(stmt, 0);
assert(s);
res.insert(s);
}
int r; if (r != SQLITE_DONE)
while ((r = sqlite3_step(stmt)) == SQLITE_ROW) { throwSQLiteError(db, "error getting valid paths");
const char * s = (const char *) sqlite3_column_text(stmt, 0);
assert(s);
res.insert(s);
}
if (r != SQLITE_DONE) return res;
throwSQLiteError(db, "error getting valid paths"); } end_retry_sqlite;
return res;
} }
@ -883,10 +905,8 @@ void LocalStore::queryReferences(const Path & path,
} }
void LocalStore::queryReferrers(const Path & path, PathSet & referrers) void LocalStore::queryReferrers_(const Path & path, PathSet & referrers)
{ {
assertStorePath(path);
SQLiteStmtUse use(stmtQueryReferrers); SQLiteStmtUse use(stmtQueryReferrers);
stmtQueryReferrers.bind(path); stmtQueryReferrers.bind(path);
@ -903,6 +923,15 @@ void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
} }
void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
{
assertStorePath(path);
retry_sqlite {
queryReferrers_(path, referrers);
} end_retry_sqlite;
}
Path LocalStore::queryDeriver(const Path & path) Path LocalStore::queryDeriver(const Path & path)
{ {
return queryPathInfo(path).deriver; return queryPathInfo(path).deriver;
@ -913,61 +942,67 @@ PathSet LocalStore::queryValidDerivers(const Path & path)
{ {
assertStorePath(path); assertStorePath(path);
SQLiteStmtUse use(stmtQueryValidDerivers); retry_sqlite {
stmtQueryValidDerivers.bind(path); SQLiteStmtUse use(stmtQueryValidDerivers);
stmtQueryValidDerivers.bind(path);
PathSet derivers; PathSet derivers;
int r; int r;
while ((r = sqlite3_step(stmtQueryValidDerivers)) == SQLITE_ROW) { while ((r = sqlite3_step(stmtQueryValidDerivers)) == SQLITE_ROW) {
const char * s = (const char *) sqlite3_column_text(stmtQueryValidDerivers, 1); const char * s = (const char *) sqlite3_column_text(stmtQueryValidDerivers, 1);
assert(s); assert(s);
derivers.insert(s); derivers.insert(s);
} }
if (r != SQLITE_DONE) if (r != SQLITE_DONE)
throwSQLiteError(db, format("error getting valid derivers of `%1%'") % path); throwSQLiteError(db, format("error getting valid derivers of `%1%'") % path);
return derivers; return derivers;
} end_retry_sqlite;
} }
PathSet LocalStore::queryDerivationOutputs(const Path & path) PathSet LocalStore::queryDerivationOutputs(const Path & path)
{ {
SQLiteStmtUse use(stmtQueryDerivationOutputs); retry_sqlite {
stmtQueryDerivationOutputs.bind(queryValidPathId(path)); SQLiteStmtUse use(stmtQueryDerivationOutputs);
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
PathSet outputs; PathSet outputs;
int r; int r;
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) { while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 1); const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 1);
assert(s); assert(s);
outputs.insert(s); outputs.insert(s);
} }
if (r != SQLITE_DONE) if (r != SQLITE_DONE)
throwSQLiteError(db, format("error getting outputs of `%1%'") % path); throwSQLiteError(db, format("error getting outputs of `%1%'") % path);
return outputs; return outputs;
} end_retry_sqlite;
} }
StringSet LocalStore::queryDerivationOutputNames(const Path & path) StringSet LocalStore::queryDerivationOutputNames(const Path & path)
{ {
SQLiteStmtUse use(stmtQueryDerivationOutputs); retry_sqlite {
stmtQueryDerivationOutputs.bind(queryValidPathId(path)); SQLiteStmtUse use(stmtQueryDerivationOutputs);
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
StringSet outputNames; StringSet outputNames;
int r; int r;
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) { while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 0); const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 0);
assert(s); assert(s);
outputNames.insert(s); outputNames.insert(s);
} }
if (r != SQLITE_DONE) if (r != SQLITE_DONE)
throwSQLiteError(db, format("error getting output names of `%1%'") % path); throwSQLiteError(db, format("error getting output names of `%1%'") % path);
return outputNames; return outputNames;
} end_retry_sqlite;
} }
@ -977,15 +1012,17 @@ Path LocalStore::queryPathFromHashPart(const string & hashPart)
Path prefix = settings.nixStore + "/" + hashPart; Path prefix = settings.nixStore + "/" + hashPart;
SQLiteStmtUse use(stmtQueryPathFromHashPart); retry_sqlite {
stmtQueryPathFromHashPart.bind(prefix); SQLiteStmtUse use(stmtQueryPathFromHashPart);
stmtQueryPathFromHashPart.bind(prefix);
int res = sqlite3_step(stmtQueryPathFromHashPart); int res = sqlite3_step(stmtQueryPathFromHashPart);
if (res == SQLITE_DONE) return ""; if (res == SQLITE_DONE) return "";
if (res != SQLITE_ROW) throwSQLiteError(db, "finding path in database"); if (res != SQLITE_ROW) throwSQLiteError(db, "finding path in database");
const char * s = (const char *) sqlite3_column_text(stmtQueryPathFromHashPart, 0); const char * s = (const char *) sqlite3_column_text(stmtQueryPathFromHashPart, 0);
return s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0 ? s : ""; return s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0 ? s : "";
} end_retry_sqlite;
} }
@ -1229,7 +1266,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
foreach (ValidPathInfos::const_iterator, i, infos) { foreach (ValidPathInfos::const_iterator, i, infos) {
assert(i->hash.type == htSHA256); assert(i->hash.type == htSHA256);
if (isValidPath(i->path)) if (isValidPath_(i->path))
updatePathInfo(*i); updatePathInfo(*i);
else else
addValidPath(*i); addValidPath(*i);
@ -1643,8 +1680,8 @@ void LocalStore::invalidatePathChecked(const Path & path)
retry_sqlite { retry_sqlite {
SQLiteTxn txn(db); 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 */
if (!referrers.empty()) if (!referrers.empty())
throw PathInUse(format("cannot delete path `%1%' because it is in use by %2%") throw PathInUse(format("cannot delete path `%1%' because it is in use by %2%")

View File

@ -304,6 +304,10 @@ private:
void checkDerivationOutputs(const Path & drvPath, const Derivation & drv); void checkDerivationOutputs(const Path & drvPath, const Derivation & drv);
void optimisePath_(OptimiseStats & stats, const Path & path); void optimisePath_(OptimiseStats & stats, const Path & path);
// Internal versions that are not wrapped in retry_sqlite.
bool isValidPath_(const Path & path);
void queryReferrers_(const Path & path, PathSet & referrers);
}; };