diff --git a/mempool/src/pool/tests/orphans.rs b/mempool/src/pool/tests/orphans.rs index 1b1f4dcbfc..f726531b2a 100644 --- a/mempool/src/pool/tests/orphans.rs +++ b/mempool/src/pool/tests/orphans.rs @@ -258,9 +258,9 @@ async fn transaction_graph_subset_permutation(#[case] seed: Seed) { log::info!( "Stats: count {}, memory {}, encoded size {}", - mempool.tx_store().txs_by_id.len(), + mempool.tx_store().txs_by_id().len(), mempool.memory_usage(), - mempool.tx_store().txs_by_id.values().map(|e| e.size().get()).sum::(), + mempool.tx_store().txs_by_id().values().map(|e| e.size().get()).sum::(), ); // Check the final state of each transaction in the original sequence diff --git a/mempool/src/pool/tx_pool/collect_txs.rs b/mempool/src/pool/tx_pool/collect_txs.rs index e60627f325..2078c8e7c4 100644 --- a/mempool/src/pool/tx_pool/collect_txs.rs +++ b/mempool/src/pool/tx_pool/collect_txs.rs @@ -133,7 +133,7 @@ pub fn collect_txs( // Transaction IDs taken from mempool to fill in the rest of the block let mempool_txids = { // Get transactions from mempool by score - let txids = mempool.store.txs_by_ancestor_score.iter().map(|x| &x.1).rev(); + let txids = mempool.store.txs_by_ancestor_score().iter().map(|x| &x.1).rev(); // Take the appropriate amount of them as determined by the packing strategy txids.take(match packing_strategy { PackingStrategy::FillSpaceFromMempool => usize::MAX, diff --git a/mempool/src/pool/tx_pool/mod.rs b/mempool/src/pool/tx_pool/mod.rs index 2497ad088b..9dbc5aea61 100644 --- a/mempool/src/pool/tx_pool/mod.rs +++ b/mempool/src/pool/tx_pool/mod.rs @@ -172,7 +172,7 @@ impl TxPool { pub fn get_all(&self) -> Vec { self.store - .txs_by_descendant_score + .txs_by_descendant_score() .iter() .map(|(_score, id)| self.store.get_entry(id).expect("entry").transaction().clone()) .collect() @@ -486,7 +486,7 @@ impl TxPool { conflicts_with_descendants: &StoreHashSet>, ) -> Result { let conflicts_with_descendants = conflicts_with_descendants.iter().map(|conflict_id| { - self.store.txs_by_id.get(conflict_id).expect("tx should exist in mempool") + self.store.txs_by_id().get(conflict_id).expect("tx should exist in mempool") }); let total_conflict_fees = conflicts_with_descendants @@ -626,7 +626,7 @@ impl TxPool { let expired_ids = self .store - .txs_by_creation_time + .txs_by_creation_time() .iter() // Note: entries in txs_by_creation_time are sorted by the creation time in ascending order, // so once we find a tx that is not expired, the rest will not be expired either. @@ -653,16 +653,22 @@ impl TxPool { fn trim(&mut self) -> Result, MempoolPolicyError> { let mut removed_fees = Vec::new(); - while !self.store.is_empty() && self.memory_usage() > self.max_size.as_bytes() { + loop { + self.store.shrink_capacity_if_needed(); + + if self.store.is_empty() || self.memory_usage() <= self.max_size.as_bytes() { + break; + } + // TODO sort by descendant score, not by fee let removed_id = self .store - .txs_by_descendant_score + .txs_by_descendant_score() .iter() .map(|(_score, entry)| *entry) .next() .expect("pool not empty"); - let removed = self.store.txs_by_id.get(&removed_id).expect("tx with id should exist"); + let removed = self.store.txs_by_id().get(&removed_id).expect("tx with id should exist"); log::debug!( "Mempool trim: Evicting tx {:x} which has a descendant score of {:?} and has size {}", @@ -673,6 +679,7 @@ impl TxPool { removed_fees.push(FeeRate::from_total_tx_fee(removed.fee(), removed.size())?); self.remove_tx_and_descendants(&removed_id, MempoolRemovalReason::SizeLimit); } + Ok(removed_fees) } @@ -946,8 +953,8 @@ impl TxPool { in_top_x_mb, &self.mempool_config, &self.rolling_fee_rate.read(), - &self.store.txs_by_descendant_score, - &self.store.txs_by_id, + self.store.txs_by_descendant_score(), + self.store.txs_by_id(), ) } @@ -991,8 +998,8 @@ impl TxPool { num_points, &self.mempool_config, &self.rolling_fee_rate.read(), - &self.store.txs_by_descendant_score, - &self.store.txs_by_id, + self.store.txs_by_descendant_score(), + self.store.txs_by_id(), ) } diff --git a/mempool/src/pool/tx_pool/store/mod.rs b/mempool/src/pool/tx_pool/store/mod.rs index 862e333930..4381589b33 100644 --- a/mempool/src/pool/tx_pool/store/mod.rs +++ b/mempool/src/pool/tx_pool/store/mod.rs @@ -124,7 +124,7 @@ pub struct MempoolStore { // and doesn't free the memory when an item is removed - it's only replaced with a tombstone. // Since TxMempoolEntry is relatively big (size_of = 350+ bytes), we'd waste a noticeable // amount of memory without boxing.) - pub txs_by_id: TrackedHashMap, Tracked, StrictDropPolicy>>, + txs_by_id: TrackedHashMap, Tracked, StrictDropPolicy>>, // Mempool entries sorted by descendant score. // We keep this index so that when the mempool grows full, we know which transactions are the @@ -136,22 +136,22 @@ pub struct MempoolStore { // max(fee/size of entry's tx, fee/size with all descendants). // TODO if we wish to follow Bitcoin Core, "size" is not simply the encoded size, but // rather a value that takes into account witness and sigop data (see CTxMemPoolEntry::GetTxSize). - pub txs_by_descendant_score: TrackedTxIdMultiMap, + txs_by_descendant_score: TrackedTxIdMultiMap, // Mempool entries sorted by ancestor score. // This is used to select the most economically attractive transactions for block production. // The ancestor score of an entry is defined as // min(fee/size of entry's tx, fee/size with all ancestors). - pub txs_by_ancestor_score: TrackedTxIdMultiMap, + txs_by_ancestor_score: TrackedTxIdMultiMap, // Entries that have remained in the mempool for a long time (see DEFAULT_MEMPOOL_EXPIRY) are // evicted. To efficiently know which entries to evict, we store the mempool entries sorted by // their creation time, from earliest to latest. - pub txs_by_creation_time: TrackedTxIdMultiMap