instantsend: optimize db and cache usage (#3986)

* instantsend: keep islocks cache when removing confirmed islocks from db

unordered_lru_cache should truncate it automagically

* instantsend: Batched write/erase for connected/disconnected blocks
This commit is contained in:
UdjinM6 2021-02-24 20:32:34 +03:00 committed by GitHub
parent 821210a2ca
commit 0b591043a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 52 deletions

View File

@ -65,10 +65,10 @@ void CInstantSendDb::WriteNewInstantSendLock(const uint256& hash, const CInstant
} }
} }
void CInstantSendDb::RemoveInstantSendLock(CDBBatch& batch, const uint256& hash, CInstantSendLockPtr islock) void CInstantSendDb::RemoveInstantSendLock(CDBBatch& batch, const uint256& hash, CInstantSendLockPtr islock, bool keep_cache)
{ {
if (!islock) { if (!islock) {
islock = GetInstantSendLockByHash(hash); islock = GetInstantSendLockByHash(hash, false);
if (!islock) { if (!islock) {
return; return;
} }
@ -80,10 +80,12 @@ void CInstantSendDb::RemoveInstantSendLock(CDBBatch& batch, const uint256& hash,
batch.Erase(std::make_tuple(std::string(DB_HASH_BY_OUTPOINT), in)); batch.Erase(std::make_tuple(std::string(DB_HASH_BY_OUTPOINT), in));
} }
islockCache.erase(hash); if (!keep_cache) {
txidCache.erase(islock->txid); islockCache.erase(hash);
for (auto& in : islock->inputs) { txidCache.erase(islock->txid);
outpointCache.erase(in); for (auto& in : islock->inputs) {
outpointCache.erase(in);
}
} }
} }
@ -94,12 +96,19 @@ static std::tuple<std::string, uint32_t, uint256> BuildInversedISLockKey(const s
void CInstantSendDb::WriteInstantSendLockMined(const uint256& hash, int nHeight) void CInstantSendDb::WriteInstantSendLockMined(const uint256& hash, int nHeight)
{ {
db.Write(BuildInversedISLockKey(DB_MINED_BY_HEIGHT_AND_HASH, nHeight, hash), true); CDBBatch batch(db);
WriteInstantSendLockMined(batch, hash, nHeight);
db.WriteBatch(batch);
} }
void CInstantSendDb::RemoveInstantSendLockMined(const uint256& hash, int nHeight) void CInstantSendDb::WriteInstantSendLockMined(CDBBatch& batch, const uint256& hash, int nHeight)
{ {
db.Erase(BuildInversedISLockKey(DB_MINED_BY_HEIGHT_AND_HASH, nHeight, hash)); batch.Write(BuildInversedISLockKey(DB_MINED_BY_HEIGHT_AND_HASH, nHeight, hash), true);
}
void CInstantSendDb::RemoveInstantSendLockMined(CDBBatch& batch, const uint256& hash, int nHeight)
{
batch.Erase(BuildInversedISLockKey(DB_MINED_BY_HEIGHT_AND_HASH, nHeight, hash));
} }
void CInstantSendDb::WriteInstantSendLockArchived(CDBBatch& batch, const uint256& hash, int nHeight) void CInstantSendDb::WriteInstantSendLockArchived(CDBBatch& batch, const uint256& hash, int nHeight)
@ -133,7 +142,7 @@ std::unordered_map<uint256, CInstantSendLockPtr> CInstantSendDb::RemoveConfirmed
} }
auto& islockHash = std::get<2>(curKey); auto& islockHash = std::get<2>(curKey);
auto islock = GetInstantSendLockByHash(islockHash); auto islock = GetInstantSendLockByHash(islockHash, false);
if (islock) { if (islock) {
RemoveInstantSendLock(batch, islockHash, islock); RemoveInstantSendLock(batch, islockHash, islock);
ret.emplace(islockHash, islock); ret.emplace(islockHash, islock);
@ -185,6 +194,39 @@ void CInstantSendDb::RemoveArchivedInstantSendLocks(int nUntilHeight)
db.WriteBatch(batch); db.WriteBatch(batch);
} }
void CInstantSendDb::WriteBlockInstantSendLocks(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected)
{
CDBBatch batch(db);
for (const auto& tx : pblock->vtx) {
if (tx->IsCoinBase() || tx->vin.empty()) {
// coinbase and TXs with no inputs can't be locked
continue;
}
uint256 islockHash = GetInstantSendLockHashByTxid(tx->GetHash());
// update DB about when an IS lock was mined
if (!islockHash.IsNull()) {
WriteInstantSendLockMined(batch, islockHash, pindexConnected->nHeight);
}
}
db.WriteBatch(batch);
}
void CInstantSendDb::RemoveBlockInstantSendLocks(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected)
{
CDBBatch batch(db);
for (const auto& tx : pblock->vtx) {
if (tx->IsCoinBase() || tx->vin.empty()) {
// coinbase and TXs with no inputs can't be locked
continue;
}
uint256 islockHash = GetInstantSendLockHashByTxid(tx->GetHash());
if (!islockHash.IsNull()) {
RemoveInstantSendLockMined(batch, islockHash, pindexDisconnected->nHeight);
}
}
db.WriteBatch(batch);
}
bool CInstantSendDb::KnownInstantSendLock(const uint256& islockHash) const bool CInstantSendDb::KnownInstantSendLock(const uint256& islockHash) const
{ {
return GetInstantSendLockByHash(islockHash) != nullptr || db.Exists(std::make_tuple(std::string(DB_ARCHIVED_BY_HASH), islockHash)); return GetInstantSendLockByHash(islockHash) != nullptr || db.Exists(std::make_tuple(std::string(DB_ARCHIVED_BY_HASH), islockHash));
@ -212,14 +254,14 @@ size_t CInstantSendDb::GetInstantSendLockCount() const
return cnt; return cnt;
} }
CInstantSendLockPtr CInstantSendDb::GetInstantSendLockByHash(const uint256& hash) const CInstantSendLockPtr CInstantSendDb::GetInstantSendLockByHash(const uint256& hash, bool use_cache) const
{ {
if (hash.IsNull()) { if (hash.IsNull()) {
return nullptr; return nullptr;
} }
CInstantSendLockPtr ret; CInstantSendLockPtr ret;
if (islockCache.get(hash, ret)) { if (use_cache && islockCache.get(hash, ret)) {
return ret; return ret;
} }
@ -300,12 +342,12 @@ std::vector<uint256> CInstantSendDb::RemoveChainedInstantSendLocks(const uint256
stack.pop_back(); stack.pop_back();
for (auto& childIslockHash : children) { for (auto& childIslockHash : children) {
auto childIsLock = GetInstantSendLockByHash(childIslockHash); auto childIsLock = GetInstantSendLockByHash(childIslockHash, false);
if (!childIsLock) { if (!childIsLock) {
continue; continue;
} }
RemoveInstantSendLock(batch, childIslockHash, childIsLock); RemoveInstantSendLock(batch, childIslockHash, childIsLock, false);
WriteInstantSendLockArchived(batch, childIslockHash, nHeight); WriteInstantSendLockArchived(batch, childIslockHash, nHeight);
result.emplace_back(childIslockHash); result.emplace_back(childIslockHash);
@ -315,7 +357,7 @@ std::vector<uint256> CInstantSendDb::RemoveChainedInstantSendLocks(const uint256
} }
} }
RemoveInstantSendLock(batch, islockHash, nullptr); RemoveInstantSendLock(batch, islockHash, nullptr, false);
WriteInstantSendLockArchived(batch, islockHash, nHeight); WriteInstantSendLockArchived(batch, islockHash, nHeight);
result.emplace_back(islockHash); result.emplace_back(islockHash);
@ -995,49 +1037,34 @@ void CInstantSendManager::BlockConnected(const std::shared_ptr<const CBlock>& pb
} }
} }
for (const auto& tx : pblock->vtx) { if (masternodeSync.IsBlockchainSynced()) {
if (tx->IsCoinBase() || tx->vin.empty()) { for (const auto& tx : pblock->vtx) {
// coinbase and TXs with no inputs can't be locked if (tx->IsCoinBase() || tx->vin.empty()) {
continue; // coinbase and TXs with no inputs can't be locked
} continue;
}
uint256 islockHash; if (!IsLocked(tx->GetHash()) && !chainLocksHandler->HasChainLock(pindex->nHeight, pindex->GetBlockHash())) {
{ ProcessTx(*tx, true, Params().GetConsensus());
LOCK(cs); // TX is not locked, so make sure it is tracked
islockHash = db.GetInstantSendLockHashByTxid(tx->GetHash()); LOCK(cs);
AddNonLockedTx(tx, pindex);
// update DB about when an IS lock was mined } else {
if (!islockHash.IsNull()) { // TX is locked, so make sure we don't track it anymore
db.WriteInstantSendLockMined(islockHash, pindex->nHeight); LOCK(cs);
RemoveNonLockedTx(tx->GetHash(), true);
} }
} }
if (!masternodeSync.IsBlockchainSynced()) {
continue;
}
if (islockHash.IsNull() && !chainLocksHandler->HasChainLock(pindex->nHeight, pindex->GetBlockHash())) {
ProcessTx(*tx, true, Params().GetConsensus());
// TX is not locked, so make sure it is tracked
LOCK(cs);
AddNonLockedTx(tx, pindex);
} else {
// TX is locked, so make sure we don't track it anymore
LOCK(cs);
RemoveNonLockedTx(tx->GetHash(), true);
}
} }
LOCK(cs);
db.WriteBlockInstantSendLocks(pblock, pindex);
} }
void CInstantSendManager::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected) void CInstantSendManager::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected)
{ {
LOCK(cs); LOCK(cs);
for (auto& tx : pblock->vtx) { db.RemoveBlockInstantSendLocks(pblock, pindexDisconnected);
auto islockHash = db.GetInstantSendLockHashByTxid(tx->GetHash());
if (!islockHash.IsNull()) {
db.RemoveInstantSendLockMined(islockHash, pindexDisconnected->nHeight);
}
}
} }
void CInstantSendManager::AddNonLockedTx(const CTransactionRef& tx, const CBlockIndex* pindexMined) void CInstantSendManager::AddNonLockedTx(const CTransactionRef& tx, const CBlockIndex* pindexMined)

View File

@ -49,21 +49,25 @@ private:
mutable unordered_lru_cache<uint256, uint256, StaticSaltedHasher, 10000> txidCache; mutable unordered_lru_cache<uint256, uint256, StaticSaltedHasher, 10000> txidCache;
mutable unordered_lru_cache<COutPoint, uint256, SaltedOutpointHasher, 10000> outpointCache; mutable unordered_lru_cache<COutPoint, uint256, SaltedOutpointHasher, 10000> outpointCache;
void WriteInstantSendLockMined(CDBBatch& batch, const uint256& hash, int nHeight);
void RemoveInstantSendLockMined(CDBBatch& batch, const uint256& hash, int nHeight);
public: public:
explicit CInstantSendDb(CDBWrapper& _db) : db(_db) {} explicit CInstantSendDb(CDBWrapper& _db) : db(_db) {}
void WriteNewInstantSendLock(const uint256& hash, const CInstantSendLock& islock); void WriteNewInstantSendLock(const uint256& hash, const CInstantSendLock& islock);
void RemoveInstantSendLock(CDBBatch& batch, const uint256& hash, CInstantSendLockPtr islock); void RemoveInstantSendLock(CDBBatch& batch, const uint256& hash, CInstantSendLockPtr islock, bool keep_cache = true);
void WriteInstantSendLockMined(const uint256& hash, int nHeight); void WriteInstantSendLockMined(const uint256& hash, int nHeight);
void RemoveInstantSendLockMined(const uint256& hash, int nHeight);
static void WriteInstantSendLockArchived(CDBBatch& batch, const uint256& hash, int nHeight); static void WriteInstantSendLockArchived(CDBBatch& batch, const uint256& hash, int nHeight);
std::unordered_map<uint256, CInstantSendLockPtr> RemoveConfirmedInstantSendLocks(int nUntilHeight); std::unordered_map<uint256, CInstantSendLockPtr> RemoveConfirmedInstantSendLocks(int nUntilHeight);
void RemoveArchivedInstantSendLocks(int nUntilHeight); void RemoveArchivedInstantSendLocks(int nUntilHeight);
void WriteBlockInstantSendLocks(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected);
void RemoveBlockInstantSendLocks(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected);
bool KnownInstantSendLock(const uint256& islockHash) const; bool KnownInstantSendLock(const uint256& islockHash) const;
size_t GetInstantSendLockCount() const; size_t GetInstantSendLockCount() const;
CInstantSendLockPtr GetInstantSendLockByHash(const uint256& hash) const; CInstantSendLockPtr GetInstantSendLockByHash(const uint256& hash, bool use_cache = true) const;
uint256 GetInstantSendLockHashByTxid(const uint256& txid) const; uint256 GetInstantSendLockHashByTxid(const uint256& txid) const;
CInstantSendLockPtr GetInstantSendLockByTxid(const uint256& txid) const; CInstantSendLockPtr GetInstantSendLockByTxid(const uint256& txid) const;
CInstantSendLockPtr GetInstantSendLockByInput(const COutPoint& outpoint) const; CInstantSendLockPtr GetInstantSendLockByInput(const COutPoint& outpoint) const;