upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-14 12:59:03 -0300
committerNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-14 13:06:57 -0300
commit82c3e10da01260650ddd2944e3f895ccf2c07ffc (patch)
tree760244d73f0a4e41449cb7c9da249d2bd4d45dfa /src
parent5b635e570ec38a8b621f19f93a4125f875e3f152 (diff)
db: recreate Berkeley DB environment after shutdown
Diffstat (limited to 'src')
-rw-r--r--src/db.cpp93
-rw-r--r--src/db.h4
-rw-r--r--src/main_gui.cpp3
-rw-r--r--src/selftest.cpp66
4 files changed, 130 insertions, 36 deletions
diff --git a/src/db.cpp b/src/db.cpp
index 0ff58e7..8b82fe5 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -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.
54string strWalletLoadError; 54string strWalletLoadError;
55DbEnv dbenv(0u); 55static DbEnv* pdbenv = NULL;
56static FILE* pdbErrFile = NULL;
56static map<string, int> mapFileUseCount; 57static map<string, int> mapFileUseCount;
57 58
59DbEnv& GetDbEnv()
60{
61 if (!pdbenv)
62 pdbenv = new DbEnv(0u);
63 return *pdbenv;
64}
65
66static 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
58class CDBInit 83class CDBInit
59{ 84{
60public: 85public:
@@ -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}
74instance_of_cdbinit; 95instance_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);
diff --git a/src/db.h b/src/db.h
index 2816957..53bda59 100644
--- a/src/db.h
+++ b/src/db.h
@@ -48,7 +48,7 @@ public:
48}; 48};
49 49
50 50
51extern DbEnv dbenv; 51extern DbEnv& GetDbEnv();
52extern void DBFlush(bool fShutdown); 52extern 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
971static 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
971static void ClearWalletRuntimeForTest() 1033static 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}