Fix excessive memory use when flushing chainstate and EvoDB (#3008)
* Specialize Serialize(CSizeComputer&) for BLS objects Speeds up size computation for BLS objects. * Track memory usage in CDBTransaction and CEvoDB * Take memory used by CEvoDB/CDBTransaction into account when flushing * Implement specialized SerReadWrite for immer maps This allows to use READWRITE and fixes compilation errors with CSizeComputer * Log EvoDB memory usage independently Co-Authored-By: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
parent
780bffeb78
commit
b9aadc071b
@ -170,6 +170,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
inline void Serialize(CSizeComputer& s) const
|
||||||
|
{
|
||||||
|
s.seek(SerSize);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
inline void Serialize(Stream& s) const
|
inline void Serialize(Stream& s) const
|
||||||
{
|
{
|
||||||
@ -356,6 +361,11 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void Serialize(CSizeComputer& s) const
|
||||||
|
{
|
||||||
|
s.seek(BLSObject::SerSize);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Stream>
|
template<typename Stream>
|
||||||
inline void Serialize(Stream& s) const
|
inline void Serialize(Stream& s) const
|
||||||
{
|
{
|
||||||
|
@ -550,6 +550,7 @@ class CDBTransaction {
|
|||||||
protected:
|
protected:
|
||||||
Parent &parent;
|
Parent &parent;
|
||||||
CommitTarget &commitTarget;
|
CommitTarget &commitTarget;
|
||||||
|
ssize_t memoryUsage{0}; // signed, just in case we made an error in the calculations so that we don't get an overflow
|
||||||
|
|
||||||
struct DataStreamCmp {
|
struct DataStreamCmp {
|
||||||
static bool less(const CDataStream& a, const CDataStream& b) {
|
static bool less(const CDataStream& a, const CDataStream& b) {
|
||||||
@ -563,6 +564,8 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ValueHolder {
|
struct ValueHolder {
|
||||||
|
size_t memoryUsage;
|
||||||
|
ValueHolder(size_t _memoryUsage) : memoryUsage(_memoryUsage) {}
|
||||||
virtual ~ValueHolder() = default;
|
virtual ~ValueHolder() = default;
|
||||||
virtual void Write(const CDataStream& ssKey, CommitTarget &parent) = 0;
|
virtual void Write(const CDataStream& ssKey, CommitTarget &parent) = 0;
|
||||||
};
|
};
|
||||||
@ -570,7 +573,7 @@ protected:
|
|||||||
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
struct ValueHolderImpl : ValueHolder {
|
struct ValueHolderImpl : ValueHolder {
|
||||||
ValueHolderImpl(const V &_value) : value(_value) { }
|
ValueHolderImpl(const V &_value, size_t _memoryUsage) : ValueHolder(_memoryUsage), value(_value) {}
|
||||||
|
|
||||||
virtual void Write(const CDataStream& ssKey, CommitTarget &commitTarget) {
|
virtual void Write(const CDataStream& ssKey, CommitTarget &commitTarget) {
|
||||||
// we're moving the value instead of copying it. This means that Write() can only be called once per
|
// we're moving the value instead of copying it. This means that Write() can only be called once per
|
||||||
@ -604,9 +607,18 @@ public:
|
|||||||
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
void Write(const CDataStream& ssKey, const V& v) {
|
void Write(const CDataStream& ssKey, const V& v) {
|
||||||
deletes.erase(ssKey);
|
auto valueMemoryUsage = ::GetSerializeSize(v, SER_DISK, CLIENT_VERSION);
|
||||||
|
|
||||||
|
if (deletes.erase(ssKey)) {
|
||||||
|
memoryUsage -= ssKey.size();
|
||||||
|
}
|
||||||
auto it = writes.emplace(ssKey, nullptr).first;
|
auto it = writes.emplace(ssKey, nullptr).first;
|
||||||
it->second = std::make_unique<ValueHolderImpl<V>>(v);
|
if (it->second) {
|
||||||
|
memoryUsage -= ssKey.size() + it->second->memoryUsage;
|
||||||
|
}
|
||||||
|
it->second = std::make_unique<ValueHolderImpl<V>>(v, valueMemoryUsage);
|
||||||
|
|
||||||
|
memoryUsage += ssKey.size() + valueMemoryUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename K, typename V>
|
template <typename K, typename V>
|
||||||
@ -656,13 +668,20 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Erase(const CDataStream& ssKey) {
|
void Erase(const CDataStream& ssKey) {
|
||||||
writes.erase(ssKey);
|
auto it = writes.find(ssKey);
|
||||||
deletes.emplace(ssKey);
|
if (it != writes.end()) {
|
||||||
|
memoryUsage -= ssKey.size() + it->second->memoryUsage;
|
||||||
|
writes.erase(it);
|
||||||
|
}
|
||||||
|
if (deletes.emplace(ssKey).second) {
|
||||||
|
memoryUsage += ssKey.size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clear() {
|
void Clear() {
|
||||||
writes.clear();
|
writes.clear();
|
||||||
deletes.clear();
|
deletes.clear();
|
||||||
|
memoryUsage = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Commit() {
|
void Commit() {
|
||||||
@ -679,6 +698,19 @@ public:
|
|||||||
return writes.empty() && deletes.empty();
|
return writes.empty() && deletes.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t GetMemoryUsage() const {
|
||||||
|
if (memoryUsage < 0) {
|
||||||
|
// something went wrong when we accounted/calculated used memory...
|
||||||
|
static volatile bool didPrint = false;
|
||||||
|
if (!didPrint) {
|
||||||
|
LogPrintf("CDBTransaction::%s -- negative memoryUsage (%d)", __func__, memoryUsage);
|
||||||
|
didPrint = true;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (size_t)memoryUsage;
|
||||||
|
}
|
||||||
|
|
||||||
CDBTransactionIterator<CDBTransaction>* NewIterator() {
|
CDBTransactionIterator<CDBTransaction>* NewIterator() {
|
||||||
return new CDBTransactionIterator<CDBTransaction>(*this);
|
return new CDBTransactionIterator<CDBTransaction>(*this);
|
||||||
}
|
}
|
||||||
|
@ -195,6 +195,21 @@ void UnserializeImmerMap(Stream& is, immer::map<K, T, Hash, Equal>& m)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For some reason the compiler is not able to choose the correct Serialize/Deserialize methods without a specialized
|
||||||
|
// version of SerReadWrite. It otherwise always chooses the version that calls a.Serialize()
|
||||||
|
template<typename Stream, typename K, typename T, typename Hash, typename Equal>
|
||||||
|
inline void SerReadWrite(Stream& s, const immer::map<K, T, Hash, Equal>& m, CSerActionSerialize ser_action)
|
||||||
|
{
|
||||||
|
::SerializeImmerMap(s, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Stream, typename K, typename T, typename Hash, typename Equal>
|
||||||
|
inline void SerReadWrite(Stream& s, immer::map<K, T, Hash, Equal>& obj, CSerActionUnserialize ser_action)
|
||||||
|
{
|
||||||
|
::UnserializeImmerMap(s, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class CDeterministicMNList
|
class CDeterministicMNList
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -226,13 +241,8 @@ public:
|
|||||||
{
|
{
|
||||||
READWRITE(blockHash);
|
READWRITE(blockHash);
|
||||||
READWRITE(nHeight);
|
READWRITE(nHeight);
|
||||||
if (ser_action.ForRead()) {
|
READWRITE(mnMap);
|
||||||
UnserializeImmerMap(s, mnMap);
|
READWRITE(mnUniquePropertyMap);
|
||||||
UnserializeImmerMap(s, mnUniquePropertyMap);
|
|
||||||
} else {
|
|
||||||
SerializeImmerMap(s, mnMap);
|
|
||||||
SerializeImmerMap(s, mnUniquePropertyMap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -73,6 +73,11 @@ public:
|
|||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t GetMemoryUsage()
|
||||||
|
{
|
||||||
|
return rootDBTransaction.GetMemoryUsage();
|
||||||
|
}
|
||||||
|
|
||||||
bool CommitRootTransaction();
|
bool CommitRootTransaction();
|
||||||
|
|
||||||
bool VerifyBestBlock(const uint256& hash);
|
bool VerifyBestBlock(const uint256& hash);
|
||||||
|
@ -2423,6 +2423,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
|
|||||||
}
|
}
|
||||||
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
|
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
|
||||||
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage() * DB_PEAK_USAGE_FACTOR;
|
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage() * DB_PEAK_USAGE_FACTOR;
|
||||||
|
cacheSize += evoDb->GetMemoryUsage() * DB_PEAK_USAGE_FACTOR;
|
||||||
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
|
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
|
||||||
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
|
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
|
||||||
bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024);
|
bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024);
|
||||||
@ -2563,6 +2564,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
|
|||||||
log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx,
|
log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx,
|
||||||
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
|
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
|
||||||
GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
|
GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
|
||||||
|
strMessage += strprintf(" evodb_cache=%.1fMiB", evoDb->GetMemoryUsage() * (1.0 / (1<<20)));
|
||||||
if (!warningMessages.empty())
|
if (!warningMessages.empty())
|
||||||
strMessage += strprintf(" warning='%s'", boost::algorithm::join(warningMessages, ", "));
|
strMessage += strprintf(" warning='%s'", boost::algorithm::join(warningMessages, ", "));
|
||||||
LogPrintf("%s\n", strMessage);
|
LogPrintf("%s\n", strMessage);
|
||||||
|
Loading…
Reference in New Issue
Block a user