Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/bdb.cpp
Show First 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | |||||
void BerkeleyEnvironment::Close() { | void BerkeleyEnvironment::Close() { | ||||
if (!fDbEnvInit) { | if (!fDbEnvInit) { | ||||
return; | return; | ||||
} | } | ||||
fDbEnvInit = false; | fDbEnvInit = false; | ||||
for (auto &db : m_databases) { | for (auto &db : m_databases) { | ||||
auto count = mapFileUseCount.find(db.first); | |||||
assert(count == mapFileUseCount.end() || count->second == 0); | |||||
BerkeleyDatabase &database = db.second.get(); | BerkeleyDatabase &database = db.second.get(); | ||||
assert(database.m_refcount <= 0); | |||||
if (database.m_db) { | if (database.m_db) { | ||||
database.m_db->close(0); | database.m_db->close(0); | ||||
database.m_db.reset(); | database.m_db.reset(); | ||||
} | } | ||||
} | } | ||||
FILE *error_file = nullptr; | FILE *error_file = nullptr; | ||||
dbenv->get_errfile(&error_file); | dbenv->get_errfile(&error_file); | ||||
▲ Show 20 Lines • Show All 172 Lines • ▼ Show 20 Lines | LogPrintf("Using BerkeleyDB version %s\n", | ||||
DbEnv::version(nullptr, nullptr, nullptr)); | DbEnv::version(nullptr, nullptr, nullptr)); | ||||
LogPrintf("Using wallet %s\n", file_path.string()); | LogPrintf("Using wallet %s\n", file_path.string()); | ||||
if (!env->Open(errorStr)) { | if (!env->Open(errorStr)) { | ||||
return false; | return false; | ||||
} | } | ||||
if (fs::exists(file_path)) { | if (fs::exists(file_path)) { | ||||
LOCK(cs_db); | assert(m_refcount == 0); | ||||
assert(env->mapFileUseCount.count(strFile) == 0); | |||||
Db db(env->dbenv.get(), 0); | Db db(env->dbenv.get(), 0); | ||||
int result = db.verify(strFile.c_str(), nullptr, nullptr, 0); | int result = db.verify(strFile.c_str(), nullptr, nullptr, 0); | ||||
if (result != 0) { | if (result != 0) { | ||||
errorStr = | errorStr = | ||||
strprintf(_("%s corrupt. Try using the wallet tool " | strprintf(_("%s corrupt. Try using the wallet tool " | ||||
"bitcoin-wallet to salvage or restoring a backup."), | "bitcoin-wallet to salvage or restoring a backup."), | ||||
file_path); | file_path); | ||||
▲ Show 20 Lines • Show All 175 Lines • ▼ Show 20 Lines | void BerkeleyEnvironment::CloseDb(const std::string &strFile) { | ||||
} | } | ||||
} | } | ||||
void BerkeleyEnvironment::ReloadDbEnv() { | void BerkeleyEnvironment::ReloadDbEnv() { | ||||
// Make sure that no Db's are in use | // Make sure that no Db's are in use | ||||
AssertLockNotHeld(cs_db); | AssertLockNotHeld(cs_db); | ||||
std::unique_lock<RecursiveMutex> lock(cs_db); | std::unique_lock<RecursiveMutex> lock(cs_db); | ||||
m_db_in_use.wait(lock, [this]() { | m_db_in_use.wait(lock, [this]() { | ||||
for (auto &count : mapFileUseCount) { | for (auto &db : m_databases) { | ||||
if (count.second > 0) { | if (db.second.get().m_refcount > 0) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
}); | }); | ||||
std::vector<std::string> filenames; | std::vector<std::string> filenames; | ||||
for (auto it : m_databases) { | for (auto it : m_databases) { | ||||
Show All 13 Lines | |||||
bool BerkeleyDatabase::Rewrite(const char *pszSkip) { | bool BerkeleyDatabase::Rewrite(const char *pszSkip) { | ||||
if (IsDummy()) { | if (IsDummy()) { | ||||
return true; | return true; | ||||
} | } | ||||
while (true) { | while (true) { | ||||
{ | { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
if (!env->mapFileUseCount.count(strFile) || | if (m_refcount <= 0) { | ||||
env->mapFileUseCount[strFile] == 0) { | |||||
// Flush log data to the dat file | // Flush log data to the dat file | ||||
env->CloseDb(strFile); | env->CloseDb(strFile); | ||||
env->CheckpointLSN(strFile); | env->CheckpointLSN(strFile); | ||||
env->mapFileUseCount.erase(strFile); | m_refcount = -1; | ||||
bool fSuccess = true; | bool fSuccess = true; | ||||
LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile); | LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile); | ||||
std::string strFileRes = strFile + ".rewrite"; | std::string strFileRes = strFile + ".rewrite"; | ||||
{ // surround usage of db with extra {} | { // surround usage of db with extra {} | ||||
BerkeleyBatch db(*this, "r"); | BerkeleyBatch db(*this, "r"); | ||||
std::unique_ptr<Db> pdbCopy = | std::unique_ptr<Db> pdbCopy = | ||||
std::make_unique<Db>(env->dbenv.get(), 0); | std::make_unique<Db>(env->dbenv.get(), 0); | ||||
▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | void BerkeleyEnvironment::Flush(bool fShutdown) { | ||||
LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", | LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", | ||||
strPath, fShutdown ? "true" : "false", | strPath, fShutdown ? "true" : "false", | ||||
fDbEnvInit ? "" : " database not started"); | fDbEnvInit ? "" : " database not started"); | ||||
if (!fDbEnvInit) { | if (!fDbEnvInit) { | ||||
return; | return; | ||||
} | } | ||||
{ | { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
std::map<std::string, int>::iterator mi = mapFileUseCount.begin(); | bool no_dbs_accessed = true; | ||||
while (mi != mapFileUseCount.end()) { | for (auto &db_it : m_databases) { | ||||
std::string strFile = (*mi).first; | std::string strFile = db_it.first; | ||||
int nRefCount = (*mi).second; | int nRefCount = db_it.second.get().m_refcount; | ||||
if (nRefCount < 0) { | |||||
continue; | |||||
} | |||||
LogPrint( | LogPrint( | ||||
BCLog::WALLETDB, | BCLog::WALLETDB, | ||||
"BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", | "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", | ||||
strFile, nRefCount); | strFile, nRefCount); | ||||
if (nRefCount == 0) { | if (nRefCount == 0) { | ||||
// Move log data to the dat file | // Move log data to the dat file | ||||
CloseDb(strFile); | CloseDb(strFile); | ||||
LogPrint(BCLog::WALLETDB, | LogPrint(BCLog::WALLETDB, | ||||
"BerkeleyEnvironment::Flush: %s checkpoint\n", | "BerkeleyEnvironment::Flush: %s checkpoint\n", | ||||
strFile); | strFile); | ||||
dbenv->txn_checkpoint(0, 0, 0); | dbenv->txn_checkpoint(0, 0, 0); | ||||
LogPrint(BCLog::WALLETDB, | LogPrint(BCLog::WALLETDB, | ||||
"BerkeleyEnvironment::Flush: %s detach\n", strFile); | "BerkeleyEnvironment::Flush: %s detach\n", strFile); | ||||
if (!fMockDb) { | if (!fMockDb) { | ||||
dbenv->lsn_reset(strFile.c_str(), 0); | dbenv->lsn_reset(strFile.c_str(), 0); | ||||
} | } | ||||
LogPrint(BCLog::WALLETDB, | LogPrint(BCLog::WALLETDB, | ||||
"BerkeleyEnvironment::Flush: %s closed\n", strFile); | "BerkeleyEnvironment::Flush: %s closed\n", strFile); | ||||
mapFileUseCount.erase(mi++); | nRefCount = -1; | ||||
} else { | } else { | ||||
mi++; | no_dbs_accessed = false; | ||||
} | } | ||||
} | } | ||||
LogPrint(BCLog::WALLETDB, | LogPrint(BCLog::WALLETDB, | ||||
"BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", | "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", | ||||
fShutdown ? "true" : "false", | fShutdown ? "true" : "false", | ||||
fDbEnvInit ? "" : " database not started", | fDbEnvInit ? "" : " database not started", | ||||
GetTimeMillis() - nStart); | GetTimeMillis() - nStart); | ||||
if (fShutdown) { | if (fShutdown) { | ||||
char **listp; | char **listp; | ||||
if (mapFileUseCount.empty()) { | if (no_dbs_accessed) { | ||||
dbenv->log_archive(&listp, DB_ARCH_REMOVE); | dbenv->log_archive(&listp, DB_ARCH_REMOVE); | ||||
Close(); | Close(); | ||||
if (!fMockDb) { | if (!fMockDb) { | ||||
fs::remove_all(fs::path(strPath) / "database"); | fs::remove_all(fs::path(strPath) / "database"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool BerkeleyDatabase::PeriodicFlush() { | bool BerkeleyDatabase::PeriodicFlush() { | ||||
// There's nothing to do for dummy databases. Return true. | // There's nothing to do for dummy databases. Return true. | ||||
if (IsDummy()) { | if (IsDummy()) { | ||||
return true; | return true; | ||||
} | } | ||||
// Don't flush if we can't acquire the lock. | // Don't flush if we can't acquire the lock. | ||||
TRY_LOCK(cs_db, lockDb); | TRY_LOCK(cs_db, lockDb); | ||||
if (!lockDb) { | if (!lockDb) { | ||||
return false; | return false; | ||||
} | } | ||||
// Don't flush if any databases are in use | // Don't flush if any databases are in use | ||||
for (const auto &use_count : env->mapFileUseCount) { | for (auto &it : env->m_databases) { | ||||
if (use_count.second > 0) { | if (it.second.get().m_refcount > 0) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
// Don't flush if there haven't been any batch writes for this database. | // Don't flush if there haven't been any batch writes for this database. | ||||
auto it = env->mapFileUseCount.find(strFile); | if (m_refcount < 0) { | ||||
if (it == env->mapFileUseCount.end()) { | |||||
return false; | return false; | ||||
} | } | ||||
LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile); | LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile); | ||||
int64_t nStart = GetTimeMillis(); | int64_t nStart = GetTimeMillis(); | ||||
// Flush wallet file so it's self contained | // Flush wallet file so it's self contained | ||||
env->CloseDb(strFile); | env->CloseDb(strFile); | ||||
env->CheckpointLSN(strFile); | env->CheckpointLSN(strFile); | ||||
env->mapFileUseCount.erase(it); | m_refcount = -1; | ||||
LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, | LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, | ||||
GetTimeMillis() - nStart); | GetTimeMillis() - nStart); | ||||
return true; | return true; | ||||
} | } | ||||
bool BerkeleyDatabase::Backup(const std::string &strDest) const { | bool BerkeleyDatabase::Backup(const std::string &strDest) const { | ||||
if (IsDummy()) { | if (IsDummy()) { | ||||
return false; | return false; | ||||
} | } | ||||
while (true) { | while (true) { | ||||
{ | { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
if (!env->mapFileUseCount.count(strFile) || | if (m_refcount <= 0) { | ||||
env->mapFileUseCount[strFile] == 0) { | |||||
// Flush log data to the dat file | // Flush log data to the dat file | ||||
env->CloseDb(strFile); | env->CloseDb(strFile); | ||||
env->CheckpointLSN(strFile); | env->CheckpointLSN(strFile); | ||||
env->mapFileUseCount.erase(strFile); | |||||
// Copy wallet file. | // Copy wallet file. | ||||
fs::path pathSrc = env->Directory() / strFile; | fs::path pathSrc = env->Directory() / strFile; | ||||
fs::path pathDest(strDest); | fs::path pathDest(strDest); | ||||
if (fs::is_directory(pathDest)) { | if (fs::is_directory(pathDest)) { | ||||
pathDest /= strFile; | pathDest /= strFile; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 171 Lines • ▼ Show 20 Lines | bool BerkeleyBatch::HasKey(CDataStream &&key) { | ||||
SafeDbt datKey(key.data(), key.size()); | SafeDbt datKey(key.data(), key.size()); | ||||
int ret = pdb->exists(activeTxn, datKey, 0); | int ret = pdb->exists(activeTxn, datKey, 0); | ||||
return ret == 0; | return ret == 0; | ||||
} | } | ||||
void BerkeleyDatabase::AddRef() { | void BerkeleyDatabase::AddRef() { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
++env->mapFileUseCount[strFile]; | if (m_refcount < 0) { | ||||
m_refcount = 1; | |||||
} else { | |||||
m_refcount++; | |||||
} | |||||
} | } | ||||
void BerkeleyDatabase::RemoveRef() { | void BerkeleyDatabase::RemoveRef() { | ||||
{ | |||||
LOCK(cs_db); | LOCK(cs_db); | ||||
--env->mapFileUseCount[strFile]; | m_refcount--; | ||||
} | |||||
env->m_db_in_use.notify_all(); | env->m_db_in_use.notify_all(); | ||||
} | } | ||||
std::unique_ptr<DatabaseBatch> | std::unique_ptr<DatabaseBatch> | ||||
BerkeleyDatabase::MakeBatch(const char *mode, bool flush_on_close) { | BerkeleyDatabase::MakeBatch(const char *mode, bool flush_on_close) { | ||||
return std::make_unique<BerkeleyBatch>(*this, mode, flush_on_close); | return std::make_unique<BerkeleyBatch>(*this, mode, flush_on_close); | ||||
} | } |