Retry all SQLite operations
To deal with SQLITE_PROTOCOL, we also need to retry read-only operations.
This commit is contained in:
		
							parent
							
								
									ff02f5336c
								
							
						
					
					
						commit
						a737f51fd9
					
				
					 2 changed files with 168 additions and 127 deletions
				
			
		|  | @ -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%") | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue