From a3e88eb0c64df40697c3be774abfb65cdab4be34 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Thu, 29 Oct 2026 15:17:19 -0500 Subject: [PATCH] Stop caching every mempool tx for bitcoind With a bitcoind chain source, every mempool transaction was fetched into `mempool_txs_cache` and kept there until it left the mempool, so the node held a decoded copy of every transaction in bitcoind's mempool, which can reach hundreds of MB when the mempool is full. Only the onchain wallet consumes these transactions, and it keeps just the relevant ones. The cache only needs the transactions we have to emit again, i.e., those that entered the mempool above our best processed height. Restrict it to those. As a result, if a poll fails partway through, the retry fetches the transactions it already fetched again, rather than taking them from the cache. This only lowers steady-state memory use. `mempool_entries_cache` still holds one small entry per mempool transaction, and the first poll after startup still collects the whole mempool before handing it to the wallet. Also stop pre-sizing the transaction cache and the emit buffer to the whole mempool on every poll. This change was made with the help of Claude Code. --- src/chain/bitcoind.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/chain/bitcoind.rs b/src/chain/bitcoind.rs index 2628294160..ab9a94bf30 100644 --- a/src/chain/bitcoind.rs +++ b/src/chain/bitcoind.rs @@ -1270,15 +1270,16 @@ impl BitcoindClient { let mempool_entries_cache = mempool_entries_cache.lock().await; let mut mempool_txs_cache = mempool_txs_cache.lock().await; - mempool_txs_cache.retain(|txid, _| mempool_entries_cache.contains_key(txid)); - - if let Some(difference) = - mempool_entries_cache.len().checked_sub(mempool_txs_cache.capacity()) - { - mempool_txs_cache.reserve(difference) - } + // Only transactions we may have to emit again are cached, i.e., those that entered the + // mempool above our best processed height. Caching every mempool transaction would keep a + // full copy of the mempool in memory. + mempool_txs_cache.retain(|txid, _| { + mempool_entries_cache + .get(txid) + .is_some_and(|entry| entry.height > best_processed_height) + }); - let mut txs_to_emit = Vec::with_capacity(mempool_entries_cache.len()); + let mut txs_to_emit = Vec::new(); for (txid, entry) in mempool_entries_cache.iter() { if entry.time > latest_time { latest_time = entry.time; @@ -1302,7 +1303,9 @@ impl BitcoindClient { match self.get_raw_transaction(&entry.txid).await { Ok(Some(tx)) => { - mempool_txs_cache.insert(entry.txid, (tx.clone(), entry.time)); + if !ancestor_within_height { + mempool_txs_cache.insert(entry.txid, (tx.clone(), entry.time)); + } txs_to_emit.push((tx, entry.time)); }, Ok(None) => {