diff options
| -rw-r--r-- | src/db.cpp | 93 | ||||
| -rw-r--r-- | src/db.h | 4 | ||||
| -rw-r--r-- | src/main_gui.cpp | 3 | ||||
| -rw-r--r-- | src/selftest.cpp | 66 |
4 files changed, 130 insertions, 36 deletions
| @@ -52,9 +52,34 @@ static bool fDbEnvInit = false; | |||
| 52 | // Why LoadWallet() refused, in words meant for the person who has to act on it. | 52 | // Why LoadWallet() refused, in words meant for the person who has to act on it. |
| 53 | // Empty unless the reason is one we can name. | 53 | // Empty unless the reason is one we can name. |
| 54 | string strWalletLoadError; | 54 | string strWalletLoadError; |
| 55 | DbEnv dbenv(0u); | 55 | static DbEnv* pdbenv = NULL; |
| 56 | static FILE* pdbErrFile = NULL; | ||
| 56 | static map<string, int> mapFileUseCount; | 57 | static map<string, int> mapFileUseCount; |
| 57 | 58 | ||
| 59 | DbEnv& GetDbEnv() | ||
| 60 | { | ||
| 61 | if (!pdbenv) | ||
| 62 | pdbenv = new DbEnv(0u); | ||
| 63 | return *pdbenv; | ||
| 64 | } | ||
| 65 | |||
| 66 | static void CloseDbEnv() | ||
| 67 | { | ||
| 68 | if (pdbenv) | ||
| 69 | { | ||
| 70 | try { pdbenv->close(0); } | ||
| 71 | catch (...) { } | ||
| 72 | delete pdbenv; | ||
| 73 | pdbenv = NULL; | ||
| 74 | } | ||
| 75 | if (pdbErrFile) | ||
| 76 | { | ||
| 77 | fclose(pdbErrFile); | ||
| 78 | pdbErrFile = NULL; | ||
| 79 | } | ||
| 80 | fDbEnvInit = false; | ||
| 81 | } | ||
| 82 | |||
| 58 | class CDBInit | 83 | class CDBInit |
| 59 | { | 84 | { |
| 60 | public: | 85 | public: |
| @@ -64,11 +89,7 @@ public: | |||
| 64 | ~CDBInit() | 89 | ~CDBInit() |
| 65 | { | 90 | { |
| 66 | if (fDbEnvInit) | 91 | if (fDbEnvInit) |
| 67 | { | 92 | CloseDbEnv(); |
| 68 | try { dbenv.close(0); } | ||
| 69 | catch (...) { } | ||
| 70 | fDbEnvInit = false; | ||
| 71 | } | ||
| 72 | } | 93 | } |
| 73 | } | 94 | } |
| 74 | instance_of_cdbinit; | 95 | instance_of_cdbinit; |
| @@ -104,24 +125,30 @@ CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL) | |||
| 104 | 125 | ||
| 105 | printf("dbenv.open strAppDir=%s\n", strAppDir.c_str()); | 126 | printf("dbenv.open strAppDir=%s\n", strAppDir.c_str()); |
| 106 | 127 | ||
| 107 | dbenv.set_lg_dir(strLogDir.c_str()); | 128 | DbEnv& env = GetDbEnv(); |
| 108 | dbenv.set_lg_max(10000000); | 129 | env.set_lg_dir(strLogDir.c_str()); |
| 109 | dbenv.set_lk_max_locks(10000); | 130 | env.set_lg_max(10000000); |
| 110 | dbenv.set_lk_max_objects(10000); | 131 | env.set_lk_max_locks(10000); |
| 111 | dbenv.set_errfile(fopen("db.log", "a")); /// debug | 132 | env.set_lk_max_objects(10000); |
| 112 | ///dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); /// causes corruption | 133 | pdbErrFile = fopen("db.log", "a"); /// debug |
| 113 | ret = dbenv.open(strAppDir.c_str(), | 134 | if (pdbErrFile) |
| 114 | DB_CREATE | | 135 | env.set_errfile(pdbErrFile); |
| 115 | DB_INIT_LOCK | | 136 | ///env.log_set_config(DB_LOG_AUTO_REMOVE, 1); /// causes corruption |
| 116 | DB_INIT_LOG | | 137 | ret = env.open(strAppDir.c_str(), |
| 117 | DB_INIT_MPOOL | | 138 | DB_CREATE | |
| 118 | DB_INIT_TXN | | 139 | DB_INIT_LOCK | |
| 119 | DB_THREAD | | 140 | DB_INIT_LOG | |
| 120 | DB_PRIVATE | | 141 | DB_INIT_MPOOL | |
| 121 | DB_RECOVER, | 142 | DB_INIT_TXN | |
| 122 | 0); | 143 | DB_THREAD | |
| 144 | DB_PRIVATE | | ||
| 145 | DB_RECOVER, | ||
| 146 | 0); | ||
| 123 | if (ret > 0) | 147 | if (ret > 0) |
| 148 | { | ||
| 149 | CloseDbEnv(); | ||
| 124 | throw runtime_error(strprintf("CDB() : error %d opening database environment\n", ret)); | 150 | throw runtime_error(strprintf("CDB() : error %d opening database environment\n", ret)); |
| 151 | } | ||
| 125 | fDbEnvInit = true; | 152 | fDbEnvInit = true; |
| 126 | 153 | ||
| 127 | // A Berkeley DB file records, in every page header, a log sequence | 154 | // A Berkeley DB file records, in every page header, a log sequence |
| @@ -151,7 +178,7 @@ CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL) | |||
| 151 | continue; | 178 | continue; |
| 152 | try | 179 | try |
| 153 | { | 180 | { |
| 154 | if (dbenv.lsn_reset(pszName, 0) == 0) | 181 | if (env.lsn_reset(pszName, 0) == 0) |
| 155 | printf("CDB() : adopted %s from another environment " | 182 | printf("CDB() : adopted %s from another environment " |
| 156 | "(log sequence numbers reset)\n", pszName); | 183 | "(log sequence numbers reset)\n", pszName); |
| 157 | } | 184 | } |
| @@ -164,7 +191,7 @@ CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL) | |||
| 164 | ++mapFileUseCount[strFile]; | 191 | ++mapFileUseCount[strFile]; |
| 165 | } | 192 | } |
| 166 | 193 | ||
| 167 | pdb = new Db(&dbenv, 0); | 194 | pdb = new Db(&GetDbEnv(), 0); |
| 168 | 195 | ||
| 169 | ret = pdb->open(NULL, // Txn pointer | 196 | ret = pdb->open(NULL, // Txn pointer |
| 170 | pszFile, // Filename | 197 | pszFile, // Filename |
| @@ -199,7 +226,8 @@ void CDB::Close() | |||
| 199 | try { pdb->close(0); } catch (...) { } | 226 | try { pdb->close(0); } catch (...) { } |
| 200 | delete pdb; | 227 | delete pdb; |
| 201 | pdb = NULL; | 228 | pdb = NULL; |
| 202 | try { dbenv.txn_checkpoint(0, 0, 0); } catch (...) { } | 229 | if (fDbEnvInit) |
| 230 | try { GetDbEnv().txn_checkpoint(0, 0, 0); } catch (...) { } | ||
| 203 | 231 | ||
| 204 | CRITICAL_BLOCK(cs_db) | 232 | CRITICAL_BLOCK(cs_db) |
| 205 | --mapFileUseCount[strFile]; | 233 | --mapFileUseCount[strFile]; |
| @@ -279,7 +307,8 @@ void DBFlush(bool fShutdown) | |||
| 279 | // takes the process down before the wallet has been put down cleanly, | 307 | // takes the process down before the wallet has been put down cleanly, |
| 280 | // which is the difference between a portable wallet.dat and one welded | 308 | // which is the difference between a portable wallet.dat and one welded |
| 281 | // to this directory -- the lsn_reset just below is what frees it. | 309 | // to this directory -- the lsn_reset just below is what frees it. |
| 282 | try { dbenv.txn_checkpoint(0, 0, 0); } | 310 | DbEnv& env = GetDbEnv(); |
| 311 | try { env.txn_checkpoint(0, 0, 0); } | ||
| 283 | catch (const std::exception& e) | 312 | catch (const std::exception& e) |
| 284 | { printf("DBFlush() : checkpoint failed: %s\n", e.what()); } | 313 | { printf("DBFlush() : checkpoint failed: %s\n", e.what()); } |
| 285 | 314 | ||
| @@ -290,7 +319,7 @@ void DBFlush(bool fShutdown) | |||
| 290 | int nRefCount = (*mi).second; | 319 | int nRefCount = (*mi).second; |
| 291 | if (nRefCount == 0) | 320 | if (nRefCount == 0) |
| 292 | { | 321 | { |
| 293 | try { dbenv.lsn_reset(strFile.c_str(), 0); } | 322 | try { env.lsn_reset(strFile.c_str(), 0); } |
| 294 | catch (const std::exception& e) | 323 | catch (const std::exception& e) |
| 295 | { printf("DBFlush() : lsn_reset(%s) failed: %s\n", strFile.c_str(), e.what()); } | 324 | { printf("DBFlush() : lsn_reset(%s) failed: %s\n", strFile.c_str(), e.what()); } |
| 296 | mapFileUseCount.erase(mi++); | 325 | mapFileUseCount.erase(mi++); |
| @@ -310,9 +339,8 @@ void DBFlush(bool fShutdown) | |||
| 310 | // Found while making wallet encryption clean up after itself: | 339 | // Found while making wallet encryption clean up after itself: |
| 311 | // calling DBFlush(true) a second time crashed here every time. | 340 | // calling DBFlush(true) a second time crashed here every time. |
| 312 | if (mapFileUseCount.empty()) | 341 | if (mapFileUseCount.empty()) |
| 313 | try { dbenv.log_archive(NULL, DB_ARCH_REMOVE); } catch (...) { } | 342 | try { env.log_archive(NULL, DB_ARCH_REMOVE); } catch (...) { } |
| 314 | try { dbenv.close(0); } catch (...) { } | 343 | CloseDbEnv(); |
| 315 | fDbEnvInit = false; | ||
| 316 | } | 344 | } |
| 317 | } | 345 | } |
| 318 | } | 346 | } |
| @@ -1042,7 +1070,8 @@ bool BackupWallet(const string& strDest) | |||
| 1042 | // Committing first, so the copy is not missing the newest records. | 1070 | // Committing first, so the copy is not missing the newest records. |
| 1043 | // Both calls can throw, and a failed backup must not take the node | 1071 | // Both calls can throw, and a failed backup must not take the node |
| 1044 | // down with it. | 1072 | // down with it. |
| 1045 | try { dbenv.txn_checkpoint(0, 0, 0); } | 1073 | DbEnv& env = GetDbEnv(); |
| 1074 | try { env.txn_checkpoint(0, 0, 0); } | ||
| 1046 | catch (const std::exception& e) | 1075 | catch (const std::exception& e) |
| 1047 | { return error("BackupWallet() : checkpoint failed: %s\n", e.what()); } | 1076 | { return error("BackupWallet() : checkpoint failed: %s\n", e.what()); } |
| 1048 | 1077 | ||
| @@ -1077,7 +1106,7 @@ bool BackupWallet(const string& strDest) | |||
| 1077 | } | 1106 | } |
| 1078 | 1107 | ||
| 1079 | // Without this the copy is welded to this node's database/ dir. | 1108 | // Without this the copy is welded to this node's database/ dir. |
| 1080 | int ret = dbenv.lsn_reset(strTemp.c_str(), 0); | 1109 | int ret = env.lsn_reset(strTemp.c_str(), 0); |
| 1081 | if (ret != 0) | 1110 | if (ret != 0) |
| 1082 | printf("BackupWallet() : warning -- lsn_reset returned %d; the copy " | 1111 | printf("BackupWallet() : warning -- lsn_reset returned %d; the copy " |
| 1083 | "may only open beside this node's database/ directory\n", ret); | 1112 | "may only open beside this node's database/ directory\n", ret); |
| @@ -48,7 +48,7 @@ public: | |||
| 48 | }; | 48 | }; |
| 49 | 49 | ||
| 50 | 50 | ||
| 51 | extern DbEnv dbenv; | 51 | extern DbEnv& GetDbEnv(); |
| 52 | extern void DBFlush(bool fShutdown); | 52 | extern void DBFlush(bool fShutdown); |
| 53 | // Close the environment and delete its logs. Only the encryption command calls | 53 | // Close the environment and delete its logs. Only the encryption command calls |
| 54 | // this: the logs hold the plaintext records the old wallet wrote. | 54 | // this: the logs hold the plaintext records the old wallet wrote. |
| @@ -237,7 +237,7 @@ public: | |||
| 237 | if (!pdb) | 237 | if (!pdb) |
| 238 | return false; | 238 | return false; |
| 239 | DbTxn* ptxn = NULL; | 239 | DbTxn* ptxn = NULL; |
| 240 | int ret = dbenv.txn_begin(GetTxn(), &ptxn, 0); | 240 | int ret = GetDbEnv().txn_begin(GetTxn(), &ptxn, 0); |
| 241 | if (!ptxn || ret != 0) | 241 | if (!ptxn || ret != 0) |
| 242 | return false; | 242 | return false; |
| 243 | vTxn.push_back(ptxn); | 243 | vTxn.push_back(ptxn); |
diff --git a/src/main_gui.cpp b/src/main_gui.cpp index feda428..705b4ef 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp | |||
| @@ -114,7 +114,8 @@ static void PrintUsage() | |||
| 114 | printf(" /gen\n"); | 114 | printf(" /gen\n"); |
| 115 | printf(" /nogui or /daemon\n"); | 115 | printf(" /nogui or /daemon\n"); |
| 116 | printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity,\n"); | 116 | printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity,\n"); |
| 117 | printf(" wallet-crypto, wallet-encrypt, wallet-portability, net-message,\n"); | 117 | printf(" db-env-reopen, wallet-crypto, wallet-encrypt, wallet-portability,\n"); |
| 118 | printf(" net-message,\n"); | ||
| 118 | printf(" consensus-limits, pool-stratum,\n"); | 119 | printf(" consensus-limits, pool-stratum,\n"); |
| 119 | printf(" parse-money, socks5-proxy, or managed-tor\n"); | 120 | printf(" parse-money, socks5-proxy, or managed-tor\n"); |
| 120 | printf("\n"); | 121 | printf("\n"); |
diff --git a/src/selftest.cpp b/src/selftest.cpp index 54249b1..31c168f 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -968,6 +968,68 @@ static int RunWalletStorageSanitySelfTest() | |||
| 968 | return nFail == 0 ? 0 : 1; | 968 | return nFail == 0 ? 0 : 1; |
| 969 | } | 969 | } |
| 970 | 970 | ||
| 971 | static int RunDbEnvReopenSelfTest() | ||
| 972 | { | ||
| 973 | fflush(stdout); | ||
| 974 | printf("db-env-reopen self-test\n"); | ||
| 975 | |||
| 976 | std::string tmp; | ||
| 977 | if (!MakeTempDir(tmp)) | ||
| 978 | { | ||
| 979 | printf(" FAIL could not create a temporary data directory\n"); | ||
| 980 | return 1; | ||
| 981 | } | ||
| 982 | |||
| 983 | int nFail = 0; | ||
| 984 | std::string strOldDataDir = strSetDataDir; | ||
| 985 | strSetDataDir = tmp; | ||
| 986 | printf(" temp datadir: %s\n", tmp.c_str()); | ||
| 987 | |||
| 988 | try | ||
| 989 | { | ||
| 990 | for (int i = 0; i < 5; i++) | ||
| 991 | { | ||
| 992 | int64 nValue = 1000 + i; | ||
| 993 | { | ||
| 994 | CWalletDB walletdb("cr+"); | ||
| 995 | nFail += Check(walletdb.WriteSetting("db-env-reopen-cycle", nValue), | ||
| 996 | "wallet setting can be written before shutdown") ? 0 : 1; | ||
| 997 | } | ||
| 998 | |||
| 999 | DBFlush(true); | ||
| 1000 | |||
| 1001 | int64 nRead = 0; | ||
| 1002 | { | ||
| 1003 | CWalletDB walletdb("r"); | ||
| 1004 | nFail += Check(walletdb.ReadSetting("db-env-reopen-cycle", nRead), | ||
| 1005 | "wallet setting can be read after env reopen") ? 0 : 1; | ||
| 1006 | } | ||
| 1007 | nFail += Check(nRead == nValue, | ||
| 1008 | "reopened environment reads the last committed value") ? 0 : 1; | ||
| 1009 | |||
| 1010 | DBFlush(true); | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | catch (const std::exception& e) | ||
| 1014 | { | ||
| 1015 | printf(" FAIL exception: %s\n", e.what()); | ||
| 1016 | nFail++; | ||
| 1017 | } | ||
| 1018 | catch (...) | ||
| 1019 | { | ||
| 1020 | printf(" FAIL unknown exception\n"); | ||
| 1021 | nFail++; | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | DBFlush(true); | ||
| 1025 | strSetDataDir = strOldDataDir; | ||
| 1026 | RemoveTree(tmp); | ||
| 1027 | printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", | ||
| 1028 | nFail, nFail == 1 ? "" : "s"); | ||
| 1029 | fflush(stdout); | ||
| 1030 | return nFail == 0 ? 0 : 1; | ||
| 1031 | } | ||
| 1032 | |||
| 971 | static void ClearWalletRuntimeForTest() | 1033 | static void ClearWalletRuntimeForTest() |
| 972 | { | 1034 | { |
| 973 | CRITICAL_BLOCK(cs_mapKeys) | 1035 | CRITICAL_BLOCK(cs_mapKeys) |
| @@ -2225,6 +2287,8 @@ int RunSelfTest(const std::string& name) | |||
| 2225 | return RunWalletFormatSelfTest(); | 2287 | return RunWalletFormatSelfTest(); |
| 2226 | if (name == "wallet-storage-sanity") | 2288 | if (name == "wallet-storage-sanity") |
| 2227 | return RunWalletStorageSanitySelfTest(); | 2289 | return RunWalletStorageSanitySelfTest(); |
| 2290 | if (name == "db-env-reopen") | ||
| 2291 | return RunDbEnvReopenSelfTest(); | ||
| 2228 | if (name == "wallet-crypto") | 2292 | if (name == "wallet-crypto") |
| 2229 | return RunWalletCryptoSelfTest(); | 2293 | return RunWalletCryptoSelfTest(); |
| 2230 | if (name == "wallet-encrypt") | 2294 | if (name == "wallet-encrypt") |
| @@ -2245,6 +2309,6 @@ int RunSelfTest(const std::string& name) | |||
| 2245 | return RunManagedTorSelfTest(); | 2309 | return RunManagedTorSelfTest(); |
| 2246 | 2310 | ||
| 2247 | printf("Unknown self-test '%s'\n", name.c_str()); | 2311 | printf("Unknown self-test '%s'\n", name.c_str()); |
| 2248 | printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, wallet-crypto, wallet-encrypt, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); | 2312 | printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, db-env-reopen, wallet-crypto, wallet-encrypt, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); |
| 2249 | return 1; | 2313 | return 1; |
| 2250 | } | 2314 | } |