Browse Source

Deal with LevelDB errors

miguelfreitas
Pieter Wuille 12 years ago committed by Pieter Wuille
parent
commit
421218d304
  1. 4
      src/init.cpp
  2. 16
      src/leveldb.cpp
  3. 23
      src/leveldb.h
  4. 70
      src/main.cpp

4
src/init.cpp

@ -187,9 +187,9 @@ bool AppInit(int argc, char* argv[])
fRet = AppInit2(); fRet = AppInit2();
} }
catch (std::exception& e) { catch (std::exception& e) {
PrintException(&e, "AppInit()"); PrintExceptionContinue(&e, "AppInit()");
} catch (...) { } catch (...) {
PrintException(NULL, "AppInit()"); PrintExceptionContinue(NULL, "AppInit()");
} }
if (!fRet) if (!fRet)
Shutdown(NULL); Shutdown(NULL);

16
src/leveldb.cpp

@ -12,6 +12,18 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
void HandleError(const leveldb::Status &status) throw(leveldb_error) {
if (status.ok())
return;
if (status.IsCorruption())
throw leveldb_error("Database corrupted");
if (status.IsIOError())
throw leveldb_error("Database I/O error");
if (status.IsNotFound())
throw leveldb_error("Database entry missing");
throw leveldb_error("Unknown database error");
}
static leveldb::Options GetOptions(size_t nCacheSize) { static leveldb::Options GetOptions(size_t nCacheSize) {
leveldb::Options options; leveldb::Options options;
options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); options.block_cache = leveldb::NewLRUCache(nCacheSize / 2);
@ -57,12 +69,12 @@ CLevelDB::~CLevelDB() {
options.env = NULL; options.env = NULL;
} }
bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) { bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) {
leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
if (!status.ok()) { if (!status.ok()) {
printf("LevelDB write failure: %s\n", status.ToString().c_str()); printf("LevelDB write failure: %s\n", status.ToString().c_str());
HandleError(status);
return false; return false;
} }
return true; return true;
} }

23
src/leveldb.h

@ -11,6 +11,14 @@
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
class leveldb_error : public std::runtime_error
{
public:
leveldb_error(const std::string &msg) : std::runtime_error(msg) {}
};
void HandleError(const leveldb::Status &status) throw(leveldb_error);
// Batch of changes queued to be written to a CLevelDB // Batch of changes queued to be written to a CLevelDB
class CLevelDBBatch class CLevelDBBatch
{ {
@ -72,7 +80,7 @@ public:
CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false);
~CLevelDB(); ~CLevelDB();
template<typename K, typename V> bool Read(const K& key, V& value) { template<typename K, typename V> bool Read(const K& key, V& value) throw(leveldb_error) {
CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(ssKey.GetSerializeSize(key)); ssKey.reserve(ssKey.GetSerializeSize(key));
ssKey << key; ssKey << key;
@ -84,6 +92,7 @@ public:
if (status.IsNotFound()) if (status.IsNotFound())
return false; return false;
printf("LevelDB read failure: %s\n", status.ToString().c_str()); printf("LevelDB read failure: %s\n", status.ToString().c_str());
HandleError(status);
} }
try { try {
CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
@ -94,13 +103,13 @@ public:
return true; return true;
} }
template<typename K, typename V> bool Write(const K& key, const V& value, bool fSync = false) { template<typename K, typename V> bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error) {
CLevelDBBatch batch; CLevelDBBatch batch;
batch.Write(key, value); batch.Write(key, value);
return WriteBatch(batch, fSync); return WriteBatch(batch, fSync);
} }
template<typename K> bool Exists(const K& key) { template<typename K> bool Exists(const K& key) throw(leveldb_error) {
CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(ssKey.GetSerializeSize(key)); ssKey.reserve(ssKey.GetSerializeSize(key));
ssKey << key; ssKey << key;
@ -112,24 +121,25 @@ public:
if (status.IsNotFound()) if (status.IsNotFound())
return false; return false;
printf("LevelDB read failure: %s\n", status.ToString().c_str()); printf("LevelDB read failure: %s\n", status.ToString().c_str());
HandleError(status);
} }
return true; return true;
} }
template<typename K> bool Erase(const K& key, bool fSync = false) { template<typename K> bool Erase(const K& key, bool fSync = false) throw(leveldb_error) {
CLevelDBBatch batch; CLevelDBBatch batch;
batch.Erase(key); batch.Erase(key);
return WriteBatch(batch, fSync); return WriteBatch(batch, fSync);
} }
bool WriteBatch(CLevelDBBatch &batch, bool fSync = false); bool WriteBatch(CLevelDBBatch &batch, bool fSync = false) throw(leveldb_error);
// not available for LevelDB; provide for compatibility with BDB // not available for LevelDB; provide for compatibility with BDB
bool Flush() { bool Flush() {
return true; return true;
} }
bool Sync() { bool Sync() throw(leveldb_error) {
CLevelDBBatch batch; CLevelDBBatch batch;
return WriteBatch(batch, true); return WriteBatch(batch, true);
} }
@ -141,4 +151,3 @@ public:
}; };
#endif // BITCOIN_LEVELDB_H #endif // BITCOIN_LEVELDB_H

70
src/main.cpp

@ -800,7 +800,11 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn
bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs)
{ {
return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs); try {
return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs);
} catch(std::runtime_error &e) {
return state.Abort(_("System error: ") + e.what());
}
} }
bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
@ -1256,8 +1260,12 @@ bool ConnectBestBlock(CValidationState &state) {
if (fRequestShutdown) if (fRequestShutdown)
break; break;
CValidationState state; CValidationState state;
if (!SetBestChain(state, pindexSwitch)) try {
return false; if (!SetBestChain(state, pindexSwitch))
return false;
} catch(std::runtime_error &e) {
return state.Abort(_("System error: ") + e.what());
}
} }
return true; return true;
} }
@ -1687,7 +1695,7 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi
if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40))
return error("ConnectBlock() : FindUndoPos failed"); return error("ConnectBlock() : FindUndoPos failed");
if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash())) if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash()))
return state.Abort(_("Error: failed to write undo data")); return state.Abort(_("Failed to write undo data"));
// update nUndoPos in block index // update nUndoPos in block index
pindex->nUndoPos = pos.nPos; pindex->nUndoPos = pos.nPos;
@ -1698,12 +1706,12 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi
CDiskBlockIndex blockindex(pindex); CDiskBlockIndex blockindex(pindex);
if (!pblocktree->WriteBlockIndex(blockindex)) if (!pblocktree->WriteBlockIndex(blockindex))
return state.Abort(_("Error: failed to write block index")); return state.Abort(_("Failed to write block index"));
} }
if (fTxIndex) if (fTxIndex)
if (!pblocktree->WriteTxIndex(vPos)) if (!pblocktree->WriteTxIndex(vPos))
return state.Abort(_("Error: failed to write transaction index")); return state.Abort(_("Failed to write transaction index"));
// add this block to the view's block chain // add this block to the view's block chain
assert(view.SetBestBlock(pindex)); assert(view.SetBestBlock(pindex));
@ -1757,7 +1765,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
CBlock block; CBlock block;
if (!block.ReadFromDisk(pindex)) if (!block.ReadFromDisk(pindex))
return state.Abort(_("Error: failed to read block")); return state.Abort(_("Failed to read block"));
int64 nStart = GetTimeMicros(); int64 nStart = GetTimeMicros();
if (!block.DisconnectBlock(state, pindex, view)) if (!block.DisconnectBlock(state, pindex, view))
return error("SetBestBlock() : DisconnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str()); return error("SetBestBlock() : DisconnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str());
@ -1777,7 +1785,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
BOOST_FOREACH(CBlockIndex *pindex, vConnect) { BOOST_FOREACH(CBlockIndex *pindex, vConnect) {
CBlock block; CBlock block;
if (!block.ReadFromDisk(pindex)) if (!block.ReadFromDisk(pindex))
return state.Abort(_("Error: failed to read block")); return state.Abort(_("Failed to read block"));
int64 nStart = GetTimeMicros(); int64 nStart = GetTimeMicros();
if (!block.ConnectBlock(state, pindex, view)) { if (!block.ConnectBlock(state, pindex, view)) {
if (state.IsInvalid()) { if (state.IsInvalid()) {
@ -1815,7 +1823,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
FlushBlockFile(); FlushBlockFile();
pblocktree->Sync(); pblocktree->Sync();
if (!pcoinsTip->Flush()) if (!pcoinsTip->Flush())
return state.Abort(_("Error: failed to write to coin database")); return state.Abort(_("Failed to write to coin database"));
} }
// At this point, all changes have been done to the database. // At this point, all changes have been done to the database.
@ -1921,7 +1929,7 @@ bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos)
setBlockIndexValid.insert(pindexNew); setBlockIndexValid.insert(pindexNew);
if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)))
return state.Abort(_("Error: failed to write block index")); return state.Abort(_("Failed to write block index"));
// New best? // New best?
if (!ConnectBestBlock(state)) if (!ConnectBestBlock(state))
@ -1936,7 +1944,7 @@ bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos)
} }
if (!pblocktree->Flush()) if (!pblocktree->Flush())
return state.Abort(_("Error: failed to sync block index")); return state.Abort(_("Failed to sync block index"));
uiInterface.NotifyBlocksChanged(); uiInterface.NotifyBlocksChanged();
return true; return true;
@ -1990,7 +1998,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
} }
if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return state.Abort(_("Error: failed to write file info")); return state.Abort(_("Failed to write file info"));
if (fUpdatedLast) if (fUpdatedLast)
pblocktree->WriteLastBlockFile(nLastBlockFile); pblocktree->WriteLastBlockFile(nLastBlockFile);
@ -2008,15 +2016,15 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
pos.nPos = infoLastBlockFile.nUndoSize; pos.nPos = infoLastBlockFile.nUndoSize;
nNewSize = (infoLastBlockFile.nUndoSize += nAddSize); nNewSize = (infoLastBlockFile.nUndoSize += nAddSize);
if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return state.Abort(_("Error: failed to write block info")); return state.Abort(_("Failed to write block info"));
} else { } else {
CBlockFileInfo info; CBlockFileInfo info;
if (!pblocktree->ReadBlockFileInfo(nFile, info)) if (!pblocktree->ReadBlockFileInfo(nFile, info))
return state.Abort(_("Error: failed to read block info")); return state.Abort(_("Failed to read block info"));
pos.nPos = info.nUndoSize; pos.nPos = info.nUndoSize;
nNewSize = (info.nUndoSize += nAddSize); nNewSize = (info.nUndoSize += nAddSize);
if (!pblocktree->WriteBlockFileInfo(nFile, info)) if (!pblocktree->WriteBlockFileInfo(nFile, info))
return state.Abort(_("Error: failed to write block info")); return state.Abort(_("Failed to write block info"));
} }
unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
@ -2154,17 +2162,21 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp)
} }
// Write block to history file // Write block to history file
unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); try {
CDiskBlockPos blockPos; unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION);
if (dbp != NULL) CDiskBlockPos blockPos;
blockPos = *dbp; if (dbp != NULL)
if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) blockPos = *dbp;
return error("AcceptBlock() : FindBlockPos failed"); if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL))
if (dbp == NULL) return error("AcceptBlock() : FindBlockPos failed");
if (!WriteToDisk(blockPos)) if (dbp == NULL)
return state.Abort(_("Error: failed to write block")); if (!WriteToDisk(blockPos))
if (!AddToBlockIndex(state, blockPos)) return state.Abort(_("Failed to write block"));
return error("AcceptBlock() : AddToBlockIndex failed"); if (!AddToBlockIndex(state, blockPos))
return error("AcceptBlock() : AddToBlockIndex failed");
} catch(std::runtime_error &e) {
return state.Abort(_("System error: ") + e.what());
}
// Relay inventory, but don't relay old inventory during initial block download // Relay inventory, but don't relay old inventory during initial block download
int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate();
@ -2432,7 +2444,7 @@ bool AbortNode(const std::string &strMessage) {
fRequestShutdown = true; fRequestShutdown = true;
strMiscWarning = strMessage; strMiscWarning = strMessage;
printf("*** %s\n", strMessage.c_str()); printf("*** %s\n", strMessage.c_str());
uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MODAL);
StartShutdown(); StartShutdown();
return false; return false;
} }
@ -2801,7 +2813,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
int64 nStart = GetTimeMillis(); int64 nStart = GetTimeMillis();
int nLoaded = 0; int nLoaded = 0;
{ try {
CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION);
uint64 nStartByte = 0; uint64 nStartByte = 0;
if (dbp) { if (dbp) {
@ -2858,6 +2870,8 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
} }
} }
fclose(fileIn); fclose(fileIn);
} catch(std::runtime_error &e) {
AbortNode(_("Error: system error: ") + e.what());
} }
if (nLoaded > 0) if (nLoaded > 0)
printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart);

Loading…
Cancel
Save