Skip to content

Commit 1a366e5

Browse files
fanquakevijaydasmp
authored andcommitted
Merge bitcoin#21464: Mempool Update Cut-Through Optimization
c5b36b1 Mempool Update Cut-Through Optimization (Jeremy Rubin) c49daf9 [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 c5b36b1 mzumsande: Code Review ACK c5b36b1 Tree-SHA512: 78b16864f77a637d8a68a65e23c019a9757d8b2243486728ef601d212ae482f6084cf8e69d810958c356f1803178046e4697207ba40d6d10529ca57de647fae6
1 parent 7614164 commit 1a366e5

File tree

4 files changed

+73
-33
lines changed

4 files changed

+73
-33
lines changed

src/txmempool.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,9 @@ size_t CTxMemPoolEntry::GetTxSize() const
124124
return GetVirtualTransactionSize(nTxSize, sigOpCount);
125125
}
126126

127-
// Update the given tx for any in-mempool descendants.
128-
// Assumes that CTxMemPool::m_children is correct for the given tx and all
129-
// descendants.
130-
void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, const std::set<uint256> &setExclude)
127+
void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendants,
128+
const std::set<uint256>& setExclude, std::set<uint256>& descendants_to_remove,
129+
uint64_t ancestor_size_limit, uint64_t ancestor_count_limit)
131130
{
132131
CTxMemPoolEntry::Children stageEntries, descendants;
133132
stageEntries = updateIt->GetMemPoolChildrenConst();
@@ -164,17 +163,18 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
164163
cachedDescendants[updateIt].insert(mapTx.iterator_to(descendant));
165164
// Update ancestor state for each descendant
166165
mapTx.modify(mapTx.iterator_to(descendant), update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCount()));
166+
// Don't directly remove the transaction here -- doing so would
167+
// invalidate iterators in cachedDescendants. Mark it for removal
168+
// by inserting into descendants_to_remove.
169+
if (descendant.GetCountWithAncestors() > ancestor_count_limit || descendant.GetSizeWithAncestors() > ancestor_size_limit) {
170+
descendants_to_remove.insert(descendant.GetTx().GetHash());
171+
}
167172
}
168173
}
169174
mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount));
170175
}
171176

172-
// vHashesToUpdate is the set of transaction hashes from a disconnected block
173-
// which has been re-added to the mempool.
174-
// for each entry, look for descendants that are outside vHashesToUpdate, and
175-
// add fee/size information for such descendants to the parent.
176-
// for each such descendant, also update the ancestor state to include the parent.
177-
void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate)
177+
void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate, uint64_t ancestor_size_limit, uint64_t ancestor_count_limit)
178178
{
179179
AssertLockHeld(cs);
180180
// 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
186186
// accounted for in the state of their ancestors)
187187
std::set<uint256> setAlreadyIncluded(vHashesToUpdate.begin(), vHashesToUpdate.end());
188188

189+
std::set<uint256> descendants_to_remove;
190+
189191
// Iterate in reverse, so that whenever we are looking at a transaction
190192
// we are sure that all in-mempool descendants have already been processed.
191193
// This maximizes the benefit of the descendant cache and guarantees that
@@ -215,7 +217,15 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
215217
}
216218
}
217219
} // release epoch guard for UpdateForDescendants
218-
UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded);
220+
UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded, descendants_to_remove, ancestor_size_limit, ancestor_count_limit);
221+
}
222+
223+
for (const auto& txid : descendants_to_remove) {
224+
// This txid may have been removed already in a prior call to removeRecursive.
225+
// Therefore we ensure it is not yet removed already.
226+
if (const std::optional<txiter> txiter = GetIter(txid)) {
227+
removeRecursive((*txiter)->GetTx(), MemPoolRemovalReason::SIZELIMIT);
228+
}
219229
}
220230
}
221231

src/txmempool.h

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -675,16 +675,25 @@ class CTxMemPool
675675
*/
676676
void RemoveStaged(setEntries& stage, bool updateDescendants, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
677677

678-
/** When adding transactions from a disconnected block back to the mempool,
679-
* new mempool entries may have children in the mempool (which is generally
680-
* not the case when otherwise adding transactions).
681-
* UpdateTransactionsFromBlock() will find child transactions and update the
682-
* descendant state for each transaction in vHashesToUpdate (excluding any
683-
* child transactions present in vHashesToUpdate, which are already accounted
684-
* for). Note: vHashesToUpdate should be the set of transactions from the
685-
* disconnected block that have been accepted back into the mempool.
678+
/** UpdateTransactionsFromBlock is called when adding transactions from a
679+
* disconnected block back to the mempool, new mempool entries may have
680+
* children in the mempool (which is generally not the case when otherwise
681+
* adding transactions).
682+
* @post updated descendant state for descendants of each transaction in
683+
* vHashesToUpdate (excluding any child transactions present in
684+
* vHashesToUpdate, which are already accounted for). Updated state
685+
* includes add fee/size information for such descendants to the
686+
* parent and updated ancestor state to include the parent.
687+
*
688+
* @param[in] vHashesToUpdate The set of txids from the
689+
* disconnected block that have been accepted back into the mempool.
690+
* @param[in] ancestor_size_limit The maximum allowed size in virtual
691+
* bytes of an entry and its ancestors
692+
* @param[in] ancestor_count_limit The maximum allowed number of
693+
* transactions including the entry and its ancestors.
686694
*/
687-
void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main) LOCKS_EXCLUDED(m_epoch);
695+
void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate,
696+
uint64_t ancestor_size_limit, uint64_t ancestor_count_limit) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main) LOCKS_EXCLUDED(m_epoch);
688697

689698
/** Try to calculate all in-mempool ancestors of entry.
690699
* (these are all calculated including the tx itself)
@@ -828,19 +837,38 @@ class CTxMemPool
828837
/** UpdateForDescendants is used by UpdateTransactionsFromBlock to update
829838
* the descendants for a single transaction that has been added to the
830839
* mempool but may have child transactions in the mempool, eg during a
831-
* chain reorg. setExclude is the set of descendant transactions in the
832-
* mempool that must not be accounted for (because any descendants in
833-
* setExclude were added to the mempool after the transaction being
834-
* updated and hence their state is already reflected in the parent
835-
* state).
840+
* chain reorg.
841+
*
842+
* @pre CTxMemPool::m_children is correct for the given tx and all
843+
* descendants.
844+
* @pre cachedDescendants is an accurate cache where each entry has all
845+
* descendants of the corresponding key, including those that should
846+
* be removed for violation of ancestor limits.
847+
* @post if updateIt has any non-excluded descendants, cachedDescendants has
848+
* a new cache line for updateIt.
849+
* @post descendants_to_remove has a new entry for any descendant which exceeded
850+
* ancestor limits relative to updateIt.
836851
*
837-
* cachedDescendants will be updated with the descendants of the transaction
838-
* being updated, so that future invocations don't need to walk the
839-
* same transaction again, if encountered in another transaction chain.
852+
* @param[in] updateIt the entry to update for its descendants
853+
* @param[in,out] cachedDescendants a cache where each line corresponds to all
854+
* descendants. It will be updated with the descendants of the transaction
855+
* being updated, so that future invocations don't need to walk the same
856+
* transaction again, if encountered in another transaction chain.
857+
* @param[in] setExclude the set of descendant transactions in the mempool
858+
* that must not be accounted for (because any descendants in setExclude
859+
* were added to the mempool after the transaction being updated and hence
860+
* their state is already reflected in the parent state).
861+
* @param[out] descendants_to_remove Populated with the txids of entries that
862+
* exceed ancestor limits. It's the responsibility of the caller to
863+
* removeRecursive them.
864+
* @param[in] ancestor_size_limit the max number of ancestral bytes allowed
865+
* for any descendant
866+
* @param[in] ancestor_count_limit the max number of ancestor transactions
867+
* allowed for any descendant
840868
*/
841-
void UpdateForDescendants(txiter updateIt,
842-
cacheMap &cachedDescendants,
843-
const std::set<uint256> &setExclude) EXCLUSIVE_LOCKS_REQUIRED(cs);
869+
void UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendants,
870+
const std::set<uint256>& setExclude, std::set<uint256>& descendants_to_remove,
871+
uint64_t ancestor_size_limit, uint64_t ancestor_count_limit) EXCLUSIVE_LOCKS_REQUIRED(cs);
844872
/** Update ancestors of hash to add/remove it as a descendant transaction. */
845873
void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs);
846874
/** Set ancestor state for an entry */

src/validation.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,9 @@ void CChainState::MaybeUpdateMempoolForReorg(
363363
// previously-confirmed transactions back to the mempool.
364364
// UpdateTransactionsFromBlock finds descendants of any transactions in
365365
// the disconnectpool that were added back and cleans up the mempool state.
366-
m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
366+
const uint64_t ancestor_count_limit = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
367+
const uint64_t ancestor_size_limit = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
368+
m_mempool->UpdateTransactionsFromBlock(vHashUpdate, ancestor_size_limit, ancestor_count_limit);
367369

368370
// Predicate to use for filtering transactions in removeForReorg.
369371
// Checks whether the transaction is still final and, if it spends a coinbase output, mature.

test/functional/mempool_updatefromblock.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
class MempoolUpdateFromBlockTest(BitcoinTestFramework):
1818
def set_test_params(self):
1919
self.num_nodes = 1
20-
self.extra_args = [['-limitdescendantsize=1000', '-limitancestorsize=1000']]
20+
self.extra_args = [['-limitdescendantsize=1000', '-limitancestorsize=1000', '-limitancestorcount=100']]
2121

2222
def skip_test_if_missing_module(self):
2323
self.skip_if_no_wallet()

0 commit comments

Comments
 (0)