Skip to content

Commit 279c37f

Browse files
authored
Perf/ibd speedups (#277)
* Silence per-block LogPrintf calls on sync hot path Four unconditional LogPrintf calls were firing on every PON header and every block connect/disconnect during IBD, dominating debug.log volume and measurably slowing sync. Convert them to LogPrint with a category so they only fire under -debug=pon or -debug=fluxnode. - src/pon/pon.cpp: PON difficulty adjustment traces (2x per PON header) - src/fluxnode/fluxnode.cpp: FluxnodeCache::LogDebugData, plus an early-return so the per-block string concatenation over mapStartTxTracker / mapStartTxDOSTracker is skipped when the fluxnode category isn't active. * Adopt Bitcoin Core's dbcache split to favor the in-memory UTXO set Fluxd used to allocate 3/4 of -dbcache to the block tree DB whenever -insightexplorer was enabled, which left only ~76 MiB of in-memory UTXO cache at the default -dbcache=450. During IBD this caused frequent full-cache flushes (multi-second stalls every few thousand blocks). Switch to Bitcoin Core's allocation: block tree DB gets 1/8 of the total, capped at 2 MiB without txindex or 1024 MiB with txindex / insightexplorer. Coin DB cache is capped at 8 MiB. The remainder goes to the in-memory UTXO set. At -dbcache=450 with all indexes on, the UTXO cache grows from ~76 MiB to ~386 MiB; at -dbcache=2000 it reaches ~1.7 GiB, which eliminates most flush stalls during IBD. * Implement BIP 152 low-bandwidth mode (peer-initiated compact blocks) PR #266 shipped high-bandwidth compact block relay (unsolicited cmpctblock messages to up to three peers). Low-bandwidth mode lets a peer request a compact block on demand via getdata, saving bandwidth for edge peers (light clients, metered connections) that haven't opted into high-bandwidth announcements. Adds MSG_CMPCT_BLOCK as a new inv type, handled by ProcessGetData. When a block is within MAX_CMPCTBLOCK_DEPTH (5 blocks) of the tip, the server responds with a cmpctblock message; older blocks fall back to sending a full block because the requester is unlikely to have the mempool state needed to reconstruct it. Type number 11 is used instead of the BIP 152 canonical value of 4 because type 4 is already allocated to "spork" on the Flux network. IsFluxnodeType() is tightened to an explicit [4, 10] range so the new type isn't misrouted. * Early-return CheckForExpired/UndoExpiredStartTx when tracker is empty CheckForExpiredStartTx and CheckForUndoExpiredStartTx are called on every ConnectBlock/DisconnectBlock. For most of chain history the relevant trackers (mapStartTxTracker, mapStartTxDOSTracker) are empty, so the iterate-and-conditionally-copy loops do no work but still pay for the cs_main-adjacent lock acquisition and the subsequent log lines. Skip the body entirely when there is nothing to do. The emptiness check runs under the lock, so it races correctly with concurrent modifications. * Cap debug.log size and enforce it at runtime Previously ShrinkDebugFile() only ran at startup and only when -debug was not set (the -shrinkdebugfile default was !fDebug). With debug=1 the log could grow without bound. Changes: - -maxdebugfilesize upper cap is now 10 GiB when -debug is enabled (so a long debug session has room to breathe) and 2 GiB otherwise. The 500 MB default is unchanged. - Default -shrinkdebugfile to true so the cap is enforced even when debug logging is on. - Make ShrinkDebugFile() safe to call at runtime by writing the kept tail to a temp file and atomically renaming; concurrent LogPrintStr writes land on the unlinked inode and fReopenDebugLog is set so the next log write reopens on the new file. - Schedule ShrinkDebugFile() every 5 minutes so a long-running node with heavy debug output doesn't fill the disk between restarts. * Harden BIP 152 compact block handling Two fixes identified by an audit of fluxd's BIP 152 implementation against Bitcoin Core: 1. Clean up per-peer compact-block in-flight state on disconnect. FinalizeNode previously iterated mapPartialBlocks but did nothing (a TODO comment acknowledged the gap). Peer-keyed cleanup is actually available via listCompactBlocksInFlight, which stores {NodeId, block hash} markers. Walk it, drop entries for the disconnecting peer, and drop the corresponding mapPartialBlocks / mapPartialBlocksTime entries only when no other peer is still working on the same block. Prevents a slow memory leak when peers churn mid-reconstruction. 2. Unify the compact block depth constant as MAX_CMPCTBLOCK_DEPTH, shared by the cmpctblock reception check and the new low-bandwidth MSG_CMPCT_BLOCK getdata response. Value is 100, sized to roughly Bitcoin Core's ~50-minute wall-clock window at fluxd's 30-second PON block spacing (Bitcoin uses 5 at 10-minute blocks). The prior reception value of 10 covered only ~5 minutes on PON, which is too narrow for typical mempool turnover.
1 parent a1ed4cd commit 279c37f

9 files changed

Lines changed: 149 additions & 58 deletions

File tree

src/fluxnode/fluxnode.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,9 @@ bool FluxnodeCache::CheckNewStartTx(const COutPoint& out, int nHeight, bool fFro
479479
void FluxnodeCache::CheckForExpiredStartTx(const int& p_nHeight)
480480
{
481481
LOCK2(cs, g_fluxnodeCache.cs);
482+
if (g_fluxnodeCache.mapStartTxTracker.empty()) {
483+
return;
484+
}
482485
int removalHeight = p_nHeight - FLUXNODE_START_TX_EXPIRATION_HEIGHT;
483486

484487
if (IsPONActive(p_nHeight)) {
@@ -507,6 +510,9 @@ void FluxnodeCache::CheckForExpiredStartTx(const int& p_nHeight)
507510
void FluxnodeCache::CheckForUndoExpiredStartTx(const int& p_nHeight)
508511
{
509512
LOCK2(cs, g_fluxnodeCache.cs);
513+
if (g_fluxnodeCache.mapStartTxDOSTracker.empty()) {
514+
return;
515+
}
510516
int removalHeight = p_nHeight - FLUXNODE_START_TX_EXPIRATION_HEIGHT;
511517

512518
if (IsPONActive(p_nHeight)) {
@@ -1809,6 +1815,8 @@ int GetNumberOfTiers()
18091815

18101816
void FluxnodeCache::LogDebugData(const int& nHeight, const uint256& blockhash, bool fFromDisconnect)
18111817
{
1818+
if (!LogAcceptCategory("fluxnode")) return;
1819+
18121820
LOCK(cs);
18131821
std::string printme = "{ \n";
18141822
for (const auto &printitem: mapStartTxTracker) {
@@ -1823,10 +1831,10 @@ void FluxnodeCache::LogDebugData(const int& nHeight, const uint256& blockhash, b
18231831
printme3 = printme3 + "}";
18241832

18251833
if (fFromDisconnect) {
1826-
LogPrintf("Disconnecting - printing after block=%d, hash=%s\n, mapStart=%s\n\n, mapStartTxDOSTracker=%s\n\n",
1834+
LogPrint("fluxnode", "Disconnecting - printing after block=%d, hash=%s\n, mapStart=%s\n\n, mapStartTxDOSTracker=%s\n\n",
18271835
nHeight, blockhash.GetHex(), printme, printme3);
18281836
} else {
1829-
LogPrintf("printing after block=%d, hash=%s\n, mapStart=%s\n\n, mapStartTxDOSTracker=%s\n\n",
1837+
LogPrint("fluxnode", "printing after block=%d, hash=%s\n, mapStart=%s\n\n, mapStartTxDOSTracker=%s\n\n",
18301838
nHeight, blockhash.GetHex(), printme, printme3);
18311839
}
18321840

src/init.cpp

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,8 +1224,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
12241224
#ifndef WIN32
12251225
CreatePidFile(GetPidFile(), getpid());
12261226
#endif
1227-
// Shrink debug.log on startup if it's too large (configurable, default 500MB) - keeps last 50MB
1228-
if (GetBoolArg("-shrinkdebugfile", !fDebug))
1227+
// Shrink debug.log on startup if it's too large (configurable, default
1228+
// 500 MB, capped at 10 GB). Runs regardless of -debug so the file can't
1229+
// grow without bound when debug logging is enabled.
1230+
if (GetBoolArg("-shrinkdebugfile", true))
12291231
ShrinkDebugFile();
12301232

12311233
if (fPrintToDebugLog)
@@ -1474,24 +1476,24 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
14741476
}
14751477
}
14761478

1477-
// cache size calculations
1478-
int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20);
1479-
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
1480-
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greated than nMaxDbcache
1481-
int64_t nBlockTreeDBCache = nTotalCache / 8;
1482-
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false))
1483-
nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB
1484-
14851479
// https://github.com/bitpay/bitcoin/commit/c91d78b578a8700a45be936cb5bb0931df8f4b87#diff-c865a8939105e6350a50af02766291b7R1233
1486-
if (GetBoolArg("-insightexplorer", false)) {
1487-
if (!GetBoolArg("-txindex", false)) {
1488-
return InitError(_("-insightexplorer requires -txindex."));
1489-
}
1490-
// increase cache if additional indices are needed
1491-
nBlockTreeDBCache = nTotalCache * 3 / 4;
1480+
if (GetBoolArg("-insightexplorer", false) && !GetBoolArg("-txindex", false)) {
1481+
return InitError(_("-insightexplorer requires -txindex."));
14921482
}
1483+
1484+
// cache size calculations (matches Bitcoin Core's allocation so most of
1485+
// -dbcache goes to the in-memory UTXO set during IBD instead of the
1486+
// block tree DB)
1487+
const bool fHaveBlockTreeIndexes = GetBoolArg("-txindex", false) ||
1488+
GetBoolArg("-insightexplorer", false);
1489+
int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20);
1490+
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
1491+
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache
1492+
int64_t nBlockTreeDBCache = std::min(nTotalCache / 8,
1493+
(fHaveBlockTreeIndexes ? nMaxBlockDBAndTxIndexCache : nMaxBlockDBCache) << 20);
14931494
nTotalCache -= nBlockTreeDBCache;
14941495
int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
1496+
nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20);
14951497
nTotalCache -= nCoinDBCache;
14961498
nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache
14971499
LogPrintf("Cache configuration:\n");
@@ -2069,6 +2071,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
20692071
boost::ref(cs_main), boost::cref(pindexBestHeader), nPowTargetSpacing);
20702072
scheduler.scheduleEvery(f, nPowTargetSpacing);
20712073

2074+
// Periodically cap debug.log size. Runs even when -debug is enabled so a
2075+
// firehose of debug output can't fill the disk. Interval: 5 minutes.
2076+
scheduler.scheduleEvery(&ShrinkDebugFile, 300);
2077+
20722078
#ifdef ENABLE_MINING
20732079
// Generate coins in the background
20742080
GenerateBitcoins(GetBoolArg("-gen", false), GetArg("-genproclimit", 1), chainparams);

src/main.cpp

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -634,11 +634,35 @@ void FinalizeNode(NodeId nodeid) {
634634
// Remove from high-bandwidth compact block peer list
635635
lNodesAnnouncingHeaderAndIDs.remove(nodeid);
636636

637-
// Clean up any partial compact blocks from this peer
638-
for (auto it = mapPartialBlocks.begin(); it != mapPartialBlocks.end(); ) {
639-
// Note: PartiallyDownloadedBlock doesn't track which peer it came from in current implementation
640-
// This is something we should improve in future iterations
641-
++it;
637+
// Clean up any compact-block in-flight state tied to this peer.
638+
// listCompactBlocksInFlight is the source of truth; mapCompactBlocksInFlight
639+
// is the hash->iterator index. Collect affected block hashes so we can drop
640+
// their partial-block entries once no other peer is still working on them.
641+
std::set<uint256> affectedHashes;
642+
for (auto it = listCompactBlocksInFlight.begin(); it != listCompactBlocksInFlight.end(); ) {
643+
if (it->nodeid == nodeid) {
644+
affectedHashes.insert(it->hash);
645+
auto mapIt = mapCompactBlocksInFlight.find(it->hash);
646+
if (mapIt != mapCompactBlocksInFlight.end() && mapIt->second.first == nodeid) {
647+
mapCompactBlocksInFlight.erase(mapIt);
648+
}
649+
it = listCompactBlocksInFlight.erase(it);
650+
} else {
651+
++it;
652+
}
653+
}
654+
for (const uint256& hash : affectedHashes) {
655+
bool stillInFlight = false;
656+
for (const auto& marker : listCompactBlocksInFlight) {
657+
if (marker.hash == hash) {
658+
stillInFlight = true;
659+
break;
660+
}
661+
}
662+
if (!stillInFlight) {
663+
mapPartialBlocks.erase(hash);
664+
mapPartialBlocksTime.erase(hash);
665+
}
642666
}
643667

644668
mapNodeState.erase(nodeid);
@@ -6876,6 +6900,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
68766900
pcoinsTip->HaveCoins(inv.hash);
68776901
}
68786902
case MSG_BLOCK:
6903+
case MSG_CMPCT_BLOCK:
68796904
return mapBlockIndex.count(inv.hash);
68806905
}
68816906
// Don't know what it is, just say we already got one
@@ -6902,7 +6927,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
69026927
boost::this_thread::interruption_point();
69036928
it++;
69046929

6905-
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
6930+
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK)
69066931
{
69076932
bool send = false;
69086933
BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
@@ -6934,6 +6959,18 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
69346959
assert(!"cannot load block from disk");
69356960
if (inv.type == MSG_BLOCK)
69366961
pfrom->PushMessage("block", block);
6962+
else if (inv.type == MSG_CMPCT_BLOCK)
6963+
{
6964+
// BIP 152 low-bandwidth mode: peer-initiated compact block request.
6965+
// For older blocks fall back to sending the full block because the
6966+
// peer is unlikely to have the mempool state needed to reconstruct.
6967+
if (mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
6968+
CBlockHeaderAndShortTxIDs cmpctblock(block, false);
6969+
pfrom->PushMessage("cmpctblock", cmpctblock);
6970+
} else {
6971+
pfrom->PushMessage("block", block);
6972+
}
6973+
}
69376974
else // MSG_FILTERED_BLOCK)
69386975
{
69396976
LOCK(pfrom->cs_filter);
@@ -7981,7 +8018,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
79818018

79828019
// DoS protection: only accept compact blocks that are close to the tip
79838020
// This prevents attackers from wasting our CPU/bandwidth with old blocks
7984-
if (pindex->nHeight < chainActive.Height() - MAX_BLOCKTXN_DEPTH) {
8021+
if (pindex->nHeight < chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) {
79858022
LogPrint("cmpctblock", "Ignoring compact block from peer %d at height %d (tip is %d)\n",
79868023
pfrom->id, pindex->nHeight, chainActive.Height());
79878024
return true;

src/main.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,15 @@ static const int64_t DEFAULT_MAX_TIP_AGE = 4 * 60 * 60;
125125
static const int MAX_UNCONNECTING_HEADERS = 10;
126126
/** Maximum number of compact blocks in flight per block */
127127
static const unsigned int MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK = 3;
128-
/** Maximum depth for responding to GETBLOCKTXN requests */
129-
static const int MAX_BLOCKTXN_DEPTH = 10;
128+
/** Maximum depth at which we accept inbound cmpctblock messages or serve
129+
* outbound ones in response to MSG_CMPCT_BLOCK getdata requests.
130+
* Older blocks fall back to a full block because the requester's mempool
131+
* is unlikely to have the transactions needed to reconstruct.
132+
*
133+
* Sized to roughly match Bitcoin Core's ~50-minute wall-clock window.
134+
* Bitcoin Core uses 5 at 10-minute block times; fluxd runs 30-second
135+
* PON blocks, so 100 blocks ≈ 50 minutes. */
136+
static const int MAX_CMPCTBLOCK_DEPTH = 100;
130137
/** Size of extra transaction pool for compact block reconstruction */
131138
static const unsigned int EXTRA_TXNS_FOR_COMPACT_BLOCKS = 100;
132139

src/pon/pon.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ unsigned int GetNextPONWorkRequired(const CBlockIndex* pindexLast)
137137
// Target timespan = (window - 1) * target spacing (because we're measuring intervals)
138138
int64_t targetTimespan = (lookbackWindow - 1) * params.nPonTargetSpacing;
139139

140-
LogPrintf("PON: Adjustment at height %d: first=%d, last=%d, actualTimespan=%d, targetTimespan=%d\n",
140+
LogPrint("pon", "PON: Adjustment at height %d: first=%d, last=%d, actualTimespan=%d, targetTimespan=%d\n",
141141
nextHeight, pindexFirst->nHeight, pindexLast->nHeight, actualTimespan, targetTimespan);
142142

143143
// Sanity check
@@ -187,7 +187,7 @@ unsigned int GetNextPONWorkRequired(const CBlockIndex* pindexLast)
187187
newTarget = ponLimit; // Use easiest difficulty as failsafe
188188
}
189189

190-
LogPrintf("PON difficulty adjustment: height=%d, actualTimespan=%d, targetTimespan=%d, before=%08x, after=%08x\n",
190+
LogPrint("pon", "PON difficulty adjustment: height=%d, actualTimespan=%d, targetTimespan=%d, before=%08x, after=%08x\n",
191191
pindexLast->nHeight + 1, actualTimespan, targetTimespan,
192192
pindexLast->nBits, newTarget.GetCompact());
193193

src/protocol.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ static const char* ppszTypeName[] =
2525
"zn quorum",
2626
"zn announce",
2727
"zn ping",
28-
"dstx"
28+
"dstx",
29+
"cmpct block"
2930
};
3031

3132
CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn)
@@ -139,7 +140,9 @@ bool CInv::IsKnownType() const
139140

140141
bool CInv::IsFluxnodeType() const
141142
{
142-
return (type >= 4);
143+
// Types 4-10 are Flux-network-specific invs (spork, zn winner, etc).
144+
// MSG_CMPCT_BLOCK (type 11) is not a fluxnode type.
145+
return (type >= 4 && type <= 10);
143146
}
144147

145148
const char* CInv::GetCommand() const

src/protocol.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,13 @@ enum {
160160
MSG_BLOCK,
161161
// Nodes may always request a MSG_FILTERED_BLOCK in a getdata, however,
162162
// MSG_FILTERED_BLOCK should not appear in any invs except as a part of getdata.
163-
MSG_FILTERED_BLOCK
163+
MSG_FILTERED_BLOCK,
164+
// Reserved inv types 4-10 are used for fluxnode-specific invs (see
165+
// ppszTypeName in protocol.cpp).
166+
// BIP 152 low-bandwidth mode: peer-initiated compact block request.
167+
// Diverges from Bitcoin Core's MSG_CMPCT_BLOCK=4 because type 4 is
168+
// already used as "spork" on the Flux network.
169+
MSG_CMPCT_BLOCK = 11,
164170
};
165171

166172
#endif // BITCOIN_PROTOCOL_H

src/txdb.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ static const int64_t nDefaultDbCache = 450;
4646
static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024;
4747
//! min. -dbcache in (MiB)
4848
static const int64_t nMinDbCache = 4;
49+
//! Max memory allocated to block tree DB specific cache (MiB)
50+
static const int64_t nMaxBlockDBCache = 2;
51+
//! Max memory allocated to block tree DB specific cache, if -txindex or
52+
// insight-explorer indexes are enabled (MiB)
53+
static const int64_t nMaxBlockDBAndTxIndexCache = 1024;
54+
//! Max memory allocated to coin DB specific cache (MiB)
55+
static const int64_t nMaxCoinsDBCache = 8;
4956

5057
struct CDiskTxPos : public CDiskBlockPos
5158
{

src/util.cpp

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -892,37 +892,54 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
892892

893893
void ShrinkDebugFile()
894894
{
895-
// Scroll debug.log if it's getting too big
895+
// Scroll debug.log if it's getting too big.
896+
// Safe to call at any time: reads the tail into memory, writes to a temp
897+
// file, and atomically renames over debug.log. Any concurrent LogPrintStr
898+
// writes land on the unlinked inode; LogPrintStr reopens on next call
899+
// when fReopenDebugLog is set.
896900
boost::filesystem::path pathLog = GetDataDir() / "debug.log";
897-
FILE* file = fopen(pathLog.string().c_str(), "r");
898901

899-
// Get configurable size threshold (default 500MB, min 10MB, max 10GB)
900-
int64_t nMaxLogSizeMB = GetArg("-maxdebugfilesize", 500); // Default 500MB
901-
if (nMaxLogSizeMB < 10) nMaxLogSizeMB = 10; // Minimum 10MB
902-
if (nMaxLogSizeMB > 2048) nMaxLogSizeMB = 2048; // Maximum 2GB
902+
// Get configurable size threshold. Default 500 MB, min 10 MB. Upper cap
903+
// depends on whether debug logging is active: 10 GiB with -debug on so a
904+
// long debug session has room to breathe, 2 GiB otherwise.
905+
int64_t nMaxLogSizeMB = GetArg("-maxdebugfilesize", 500);
906+
const int64_t nUpperCapMB = fDebug ? 10240 : 2048;
907+
if (nMaxLogSizeMB < 10) nMaxLogSizeMB = 10;
908+
if (nMaxLogSizeMB > nUpperCapMB) nMaxLogSizeMB = nUpperCapMB;
903909
int64_t nMaxLogSize = nMaxLogSizeMB * 1000000;
904910

905-
if (file && boost::filesystem::file_size(pathLog) > nMaxLogSize)
906-
{
907-
LogPrintf("ShrinkDebugFile: debug.log size is %.1f MB, shrinking to last 50 MB...\n",
908-
boost::filesystem::file_size(pathLog) / 1000000.0);
909-
910-
// Keep last 50MB when shrinking (reasonable amount for debugging)
911-
std::vector <char> vch(50000000,0);
912-
fseek(file, -((long)vch.size()), SEEK_END);
913-
int nBytes = fread(begin_ptr(vch), 1, vch.size(), file);
914-
fclose(file);
915-
916-
file = fopen(pathLog.string().c_str(), "w");
917-
if (file)
918-
{
919-
fwrite(begin_ptr(vch), 1, nBytes, file);
920-
fclose(file);
921-
LogPrintf("ShrinkDebugFile: Shrinking complete, kept %.1f MB\n", nBytes / 1000000.0);
922-
}
911+
boost::system::error_code ec;
912+
uintmax_t curSize = boost::filesystem::file_size(pathLog, ec);
913+
if (ec || (int64_t)curSize <= nMaxLogSize)
914+
return;
915+
916+
// Keep last 50 MB when shrinking.
917+
const size_t nKeepBytes = 50 * 1000 * 1000;
918+
919+
FILE* src = fopen(pathLog.string().c_str(), "rb");
920+
if (!src) return;
921+
922+
std::vector<char> vch(nKeepBytes, 0);
923+
fseek(src, -(long)vch.size(), SEEK_END);
924+
size_t nBytes = fread(begin_ptr(vch), 1, vch.size(), src);
925+
fclose(src);
926+
927+
boost::filesystem::path pathTmp = pathLog;
928+
pathTmp += ".tmp";
929+
FILE* dst = fopen(pathTmp.string().c_str(), "wb");
930+
if (!dst) return;
931+
fwrite(begin_ptr(vch), 1, nBytes, dst);
932+
fclose(dst);
933+
934+
boost::filesystem::rename(pathTmp, pathLog, ec);
935+
if (ec) {
936+
boost::filesystem::remove(pathTmp, ec);
937+
return;
923938
}
924-
else if (file != NULL)
925-
fclose(file);
939+
940+
// Tell LogPrintStr to reopen the log on its next write so future output
941+
// goes to the new (truncated) file instead of the unlinked inode.
942+
fReopenDebugLog = true;
926943
}
927944

928945
#ifdef WIN32

0 commit comments

Comments
 (0)