aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Chow <github@achow101.com>2023-10-09 14:36:12 -0400
committerAndrew Chow <github@achow101.com>2023-10-09 14:52:00 -0400
commit04265ba9378efbd4c35b33390b1e5cf246d420a9 (patch)
treec3833e67ff38b276d7fdc46591c8600160ed683b
parent4e7442e743afef4d365fc3ce7c6e9f0a5d4d1ec4 (diff)
parented52e71176fc97c6ed01e3eebd85acdec54b4448 (diff)
downloadbitcoin-04265ba9378efbd4c35b33390b1e5cf246d420a9.tar.xz
Merge bitcoin/bitcoin#26331: Implement `CCoinsViewErrorCatcher::HaveCoin` and check disk space periodically
ed52e71176fc97c6ed01e3eebd85acdec54b4448 Periodically check disk space to avoid corruption (Aurèle Oulès) 7fe537f7a48675b1d25542bee6f390d665547580 Implement CCoinsViewErrorCatcher::HaveCoin (Aurèle Oulès) Pull request description: Attempt to fix #26112. As suggested by sipa in https://github.com/bitcoin/bitcoin/issues/26112#issuecomment-1249683401: > CCoinsViewErrorCatcher, the wrapper class used around CCoinsViewDB that's supposed to detect these problems and forcefully exit the application, has an override for GetCoins. But in CheckTxInputs, HaveInputs is first invoked, which on its turn calls HaveCoin. HaveCoin is implemented in CCoinsViewDB, but not in CCoinsViewErrorCatcher, and thus the disk read exception escapes. > A solution may be to just add an override for HaveCoin in CCoinsViewErrorCatcher. I implemented `CCoinsViewErrorCatcher::HaveCoin` and also added a periodic disk space check that shutdowns the node if there is not enough space left on disk, the minimum here is 50MB. For reviewers, it's possible to saturate disk space to test the PR by creating large files with `fallocate -l 50G test.bin` ACKs for top commit: achow101: ACK ed52e71176fc97c6ed01e3eebd85acdec54b4448 w0xlt: Code Review ACK https://github.com/bitcoin/bitcoin/pull/26331/commits/ed52e71176fc97c6ed01e3eebd85acdec54b4448 sipa: utACK ed52e71176fc97c6ed01e3eebd85acdec54b4448 Tree-SHA512: 456aa7b996023df42b4fbb5158ee429d9abf7374b7b1ec129b21aea1188ad19be8da4ae8e0edd90b85b7a3042b8e44e17d3742e33808a4234d5ddbe9bcef1b78
-rw-r--r--src/coins.cpp16
-rw-r--r--src/coins.h1
-rw-r--r--src/init.cpp9
3 files changed, 23 insertions, 3 deletions
diff --git a/src/coins.cpp b/src/coins.cpp
index 0fe642e46b..b44d920ee1 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -353,11 +353,13 @@ const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
return coinEmpty;
}
-bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) const {
+template <typename Func>
+static bool ExecuteBackedWrapper(Func func, const std::vector<std::function<void()>>& err_callbacks)
+{
try {
- return CCoinsViewBacked::GetCoin(outpoint, coin);
+ return func();
} catch(const std::runtime_error& e) {
- for (const auto& f : m_err_callbacks) {
+ for (const auto& f : err_callbacks) {
f();
}
LogPrintf("Error reading from database: %s\n", e.what());
@@ -368,3 +370,11 @@ bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) cons
std::abort();
}
}
+
+bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) const {
+ return ExecuteBackedWrapper([&]() { return CCoinsViewBacked::GetCoin(outpoint, coin); }, m_err_callbacks);
+}
+
+bool CCoinsViewErrorCatcher::HaveCoin(const COutPoint &outpoint) const {
+ return ExecuteBackedWrapper([&]() { return CCoinsViewBacked::HaveCoin(outpoint); }, m_err_callbacks);
+}
diff --git a/src/coins.h b/src/coins.h
index 039a07054d..a6cbb03133 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -383,6 +383,7 @@ public:
}
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
+ bool HaveCoin(const COutPoint &outpoint) const override;
private:
/** A list of callbacks to execute upon leveldb read error. */
diff --git a/src/init.cpp b/src/init.cpp
index 98f233d9df..22100dfbf9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1138,6 +1138,15 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
RandAddPeriodic();
}, std::chrono::minutes{1});
+ // Check disk space every 5 minutes to avoid db corruption.
+ node.scheduler->scheduleEvery([&args]{
+ constexpr uint64_t min_disk_space = 50 << 20; // 50 MB
+ if (!CheckDiskSpace(args.GetBlocksDirPath(), min_disk_space)) {
+ LogPrintf("Shutting down due to lack of disk space!\n");
+ StartShutdown();
+ }
+ }, std::chrono::minutes{5});
+
GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
// Create client interfaces for wallets that are supposed to be loaded