mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
Merge bitcoin/bitcoin#21464: Mempool Update Cut-Through Optimization
c5b36b1c1b11f04e5da7fb44183f61d09a14e40d Mempool Update Cut-Through Optimization (Jeremy Rubin) c49daf9885e86ba08acdc8332d2a34bc5951a487 [TESTS] Increase limitancestorcount in tournament RPC test to showcase improved algorithm (Jeremy Rubin) Pull request description: Often when we're updating mempool entries we update entries that we ultimately end up removing the updated entries shortly thereafter. This patch makes it so that we filter for such entries a bit earlier in processing, which yields a mild improvement for these cases, and is negligible overhead otherwise. There's potential for a better -- but more sophisticated -- algorithm that can be used taking advantage of epochs, but I figured it is better to do something that is simple and works first and upgrade it later as the other epoch mempool work proceeds as it makes the patches for the epoch algorithm simpler to understand, so you can consider this as preparatory work. It could either go in now if it is not controversial, or we could wait until the other patch is ready to go. ACKs for top commit: instagibbs: reACK c5b36b1 sipa: utACK c5b36b1c1b11f04e5da7fb44183f61d09a14e40d mzumsande: Code Review ACK c5b36b1c1b11f04e5da7fb44183f61d09a14e40d Tree-SHA512: 78b16864f77a637d8a68a65e23c019a9757d8b2243486728ef601d212ae482f6084cf8e69d810958c356f1803178046e4697207ba40d6d10529ca57de647fae6
This commit is contained in:
parent
136bf01154
commit
2d0e5d7370
@ -124,10 +124,9 @@ size_t CTxMemPoolEntry::GetTxSize() const
|
|||||||
return GetVirtualTransactionSize(nTxSize, sigOpCount);
|
return GetVirtualTransactionSize(nTxSize, sigOpCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the given tx for any in-mempool descendants.
|
void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendants,
|
||||||
// Assumes that CTxMemPool::m_children is correct for the given tx and all
|
const std::set<uint256>& setExclude, std::set<uint256>& descendants_to_remove,
|
||||||
// descendants.
|
uint64_t ancestor_size_limit, uint64_t ancestor_count_limit)
|
||||||
void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, const std::set<uint256> &setExclude)
|
|
||||||
{
|
{
|
||||||
CTxMemPoolEntry::Children stageEntries, descendants;
|
CTxMemPoolEntry::Children stageEntries, descendants;
|
||||||
stageEntries = updateIt->GetMemPoolChildrenConst();
|
stageEntries = updateIt->GetMemPoolChildrenConst();
|
||||||
@ -164,17 +163,18 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
|
|||||||
cachedDescendants[updateIt].insert(mapTx.iterator_to(descendant));
|
cachedDescendants[updateIt].insert(mapTx.iterator_to(descendant));
|
||||||
// Update ancestor state for each descendant
|
// Update ancestor state for each descendant
|
||||||
mapTx.modify(mapTx.iterator_to(descendant), update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCount()));
|
mapTx.modify(mapTx.iterator_to(descendant), update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCount()));
|
||||||
|
// Don't directly remove the transaction here -- doing so would
|
||||||
|
// invalidate iterators in cachedDescendants. Mark it for removal
|
||||||
|
// by inserting into descendants_to_remove.
|
||||||
|
if (descendant.GetCountWithAncestors() > ancestor_count_limit || descendant.GetSizeWithAncestors() > ancestor_size_limit) {
|
||||||
|
descendants_to_remove.insert(descendant.GetTx().GetHash());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount));
|
mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
// vHashesToUpdate is the set of transaction hashes from a disconnected block
|
void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate, uint64_t ancestor_size_limit, uint64_t ancestor_count_limit)
|
||||||
// which has been re-added to the mempool.
|
|
||||||
// for each entry, look for descendants that are outside vHashesToUpdate, and
|
|
||||||
// add fee/size information for such descendants to the parent.
|
|
||||||
// for each such descendant, also update the ancestor state to include the parent.
|
|
||||||
void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate)
|
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs);
|
AssertLockHeld(cs);
|
||||||
// For each entry in vHashesToUpdate, store the set of in-mempool, but not
|
// For each entry in vHashesToUpdate, store the set of in-mempool, but not
|
||||||
@ -186,6 +186,8 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
|
|||||||
// accounted for in the state of their ancestors)
|
// accounted for in the state of their ancestors)
|
||||||
std::set<uint256> setAlreadyIncluded(vHashesToUpdate.begin(), vHashesToUpdate.end());
|
std::set<uint256> setAlreadyIncluded(vHashesToUpdate.begin(), vHashesToUpdate.end());
|
||||||
|
|
||||||
|
std::set<uint256> descendants_to_remove;
|
||||||
|
|
||||||
// Iterate in reverse, so that whenever we are looking at a transaction
|
// Iterate in reverse, so that whenever we are looking at a transaction
|
||||||
// we are sure that all in-mempool descendants have already been processed.
|
// we are sure that all in-mempool descendants have already been processed.
|
||||||
// This maximizes the benefit of the descendant cache and guarantees that
|
// This maximizes the benefit of the descendant cache and guarantees that
|
||||||
@ -215,7 +217,15 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // release epoch guard for UpdateForDescendants
|
} // release epoch guard for UpdateForDescendants
|
||||||
UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded);
|
UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded, descendants_to_remove, ancestor_size_limit, ancestor_count_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& txid : descendants_to_remove) {
|
||||||
|
// This txid may have been removed already in a prior call to removeRecursive.
|
||||||
|
// Therefore we ensure it is not yet removed already.
|
||||||
|
if (const std::optional<txiter> txiter = GetIter(txid)) {
|
||||||
|
removeRecursive((*txiter)->GetTx(), MemPoolRemovalReason::SIZELIMIT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,16 +675,25 @@ public:
|
|||||||
*/
|
*/
|
||||||
void RemoveStaged(setEntries& stage, bool updateDescendants, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
void RemoveStaged(setEntries& stage, bool updateDescendants, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
|
|
||||||
/** When adding transactions from a disconnected block back to the mempool,
|
/** UpdateTransactionsFromBlock is called when adding transactions from a
|
||||||
* new mempool entries may have children in the mempool (which is generally
|
* disconnected block back to the mempool, new mempool entries may have
|
||||||
* not the case when otherwise adding transactions).
|
* children in the mempool (which is generally not the case when otherwise
|
||||||
* UpdateTransactionsFromBlock() will find child transactions and update the
|
* adding transactions).
|
||||||
* descendant state for each transaction in vHashesToUpdate (excluding any
|
* @post updated descendant state for descendants of each transaction in
|
||||||
* child transactions present in vHashesToUpdate, which are already accounted
|
* vHashesToUpdate (excluding any child transactions present in
|
||||||
* for). Note: vHashesToUpdate should be the set of transactions from the
|
* vHashesToUpdate, which are already accounted for). Updated state
|
||||||
* disconnected block that have been accepted back into the mempool.
|
* includes add fee/size information for such descendants to the
|
||||||
|
* parent and updated ancestor state to include the parent.
|
||||||
|
*
|
||||||
|
* @param[in] vHashesToUpdate The set of txids from the
|
||||||
|
* disconnected block that have been accepted back into the mempool.
|
||||||
|
* @param[in] ancestor_size_limit The maximum allowed size in virtual
|
||||||
|
* bytes of an entry and its ancestors
|
||||||
|
* @param[in] ancestor_count_limit The maximum allowed number of
|
||||||
|
* transactions including the entry and its ancestors.
|
||||||
*/
|
*/
|
||||||
void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main) LOCKS_EXCLUDED(m_epoch);
|
void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate,
|
||||||
|
uint64_t ancestor_size_limit, uint64_t ancestor_count_limit) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main) LOCKS_EXCLUDED(m_epoch);
|
||||||
|
|
||||||
/** Try to calculate all in-mempool ancestors of entry.
|
/** Try to calculate all in-mempool ancestors of entry.
|
||||||
* (these are all calculated including the tx itself)
|
* (these are all calculated including the tx itself)
|
||||||
@ -828,19 +837,38 @@ private:
|
|||||||
/** UpdateForDescendants is used by UpdateTransactionsFromBlock to update
|
/** UpdateForDescendants is used by UpdateTransactionsFromBlock to update
|
||||||
* the descendants for a single transaction that has been added to the
|
* the descendants for a single transaction that has been added to the
|
||||||
* mempool but may have child transactions in the mempool, eg during a
|
* mempool but may have child transactions in the mempool, eg during a
|
||||||
* chain reorg. setExclude is the set of descendant transactions in the
|
* chain reorg.
|
||||||
* mempool that must not be accounted for (because any descendants in
|
|
||||||
* setExclude were added to the mempool after the transaction being
|
|
||||||
* updated and hence their state is already reflected in the parent
|
|
||||||
* state).
|
|
||||||
*
|
*
|
||||||
* cachedDescendants will be updated with the descendants of the transaction
|
* @pre CTxMemPool::m_children is correct for the given tx and all
|
||||||
* being updated, so that future invocations don't need to walk the
|
* descendants.
|
||||||
* same transaction again, if encountered in another transaction chain.
|
* @pre cachedDescendants is an accurate cache where each entry has all
|
||||||
|
* descendants of the corresponding key, including those that should
|
||||||
|
* be removed for violation of ancestor limits.
|
||||||
|
* @post if updateIt has any non-excluded descendants, cachedDescendants has
|
||||||
|
* a new cache line for updateIt.
|
||||||
|
* @post descendants_to_remove has a new entry for any descendant which exceeded
|
||||||
|
* ancestor limits relative to updateIt.
|
||||||
|
*
|
||||||
|
* @param[in] updateIt the entry to update for its descendants
|
||||||
|
* @param[in,out] cachedDescendants a cache where each line corresponds to all
|
||||||
|
* descendants. It will be updated with the descendants of the transaction
|
||||||
|
* being updated, so that future invocations don't need to walk the same
|
||||||
|
* transaction again, if encountered in another transaction chain.
|
||||||
|
* @param[in] setExclude the set of descendant transactions in the mempool
|
||||||
|
* that must not be accounted for (because any descendants in setExclude
|
||||||
|
* were added to the mempool after the transaction being updated and hence
|
||||||
|
* their state is already reflected in the parent state).
|
||||||
|
* @param[out] descendants_to_remove Populated with the txids of entries that
|
||||||
|
* exceed ancestor limits. It's the responsibility of the caller to
|
||||||
|
* removeRecursive them.
|
||||||
|
* @param[in] ancestor_size_limit the max number of ancestral bytes allowed
|
||||||
|
* for any descendant
|
||||||
|
* @param[in] ancestor_count_limit the max number of ancestor transactions
|
||||||
|
* allowed for any descendant
|
||||||
*/
|
*/
|
||||||
void UpdateForDescendants(txiter updateIt,
|
void UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendants,
|
||||||
cacheMap &cachedDescendants,
|
const std::set<uint256>& setExclude, std::set<uint256>& descendants_to_remove,
|
||||||
const std::set<uint256> &setExclude) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
uint64_t ancestor_size_limit, uint64_t ancestor_count_limit) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
/** Update ancestors of hash to add/remove it as a descendant transaction. */
|
/** Update ancestors of hash to add/remove it as a descendant transaction. */
|
||||||
void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
/** Set ancestor state for an entry */
|
/** Set ancestor state for an entry */
|
||||||
|
@ -365,7 +365,9 @@ void CChainState::MaybeUpdateMempoolForReorg(
|
|||||||
// previously-confirmed transactions back to the mempool.
|
// previously-confirmed transactions back to the mempool.
|
||||||
// UpdateTransactionsFromBlock finds descendants of any transactions in
|
// UpdateTransactionsFromBlock finds descendants of any transactions in
|
||||||
// the disconnectpool that were added back and cleans up the mempool state.
|
// the disconnectpool that were added back and cleans up the mempool state.
|
||||||
m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
|
const uint64_t ancestor_count_limit = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
|
||||||
|
const uint64_t ancestor_size_limit = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
|
||||||
|
m_mempool->UpdateTransactionsFromBlock(vHashUpdate, ancestor_size_limit, ancestor_count_limit);
|
||||||
|
|
||||||
// Predicate to use for filtering transactions in removeForReorg.
|
// Predicate to use for filtering transactions in removeForReorg.
|
||||||
// Checks whether the transaction is still final and, if it spends a coinbase output, mature.
|
// Checks whether the transaction is still final and, if it spends a coinbase output, mature.
|
||||||
|
@ -17,7 +17,7 @@ from test_framework.util import assert_equal
|
|||||||
class MempoolUpdateFromBlockTest(BitcoinTestFramework):
|
class MempoolUpdateFromBlockTest(BitcoinTestFramework):
|
||||||
def set_test_params(self):
|
def set_test_params(self):
|
||||||
self.num_nodes = 1
|
self.num_nodes = 1
|
||||||
self.extra_args = [['-limitdescendantsize=1000', '-limitancestorsize=1000']]
|
self.extra_args = [['-limitdescendantsize=1000', '-limitancestorsize=1000', '-limitancestorcount=100']]
|
||||||
|
|
||||||
def skip_test_if_missing_module(self):
|
def skip_test_if_missing_module(self):
|
||||||
self.skip_if_no_wallet()
|
self.skip_if_no_wallet()
|
||||||
|
Loading…
Reference in New Issue
Block a user