From 4f045087c28e5f477606118f1e24d6ffa0fe7545 Mon Sep 17 00:00:00 2001 From: Esteban Dimitroff Hodi Date: Thu, 21 May 2026 08:25:14 -0300 Subject: [PATCH] fix(storage): break out of prefix iterator on mismatch in get_transaction_location MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `rust-rocksdb`'s prefix_iterator_cf seeks to the prefix but iterates forward indefinitely — the caller must break manually when the key no longer matches. The previous loop filtered non-matching keys via an `if` check but kept iterating, silently scanning to the end of the TRANSACTION_LOCATIONS column family on every call (hundreds of GB on mainnet, taking seconds to minutes per call). Add the missing `break`: once we see a non-matching key, all subsequent keys also can't match (RocksDB key order is lexicographic and all entries with the same tx_hash prefix are contiguous), so iteration can stop. Closes #6688 --- crates/storage/store.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/storage/store.rs b/crates/storage/store.rs index 4b7e9be0c6..9f30786d7e 100644 --- a/crates/storage/store.rs +++ b/crates/storage/store.rs @@ -589,15 +589,17 @@ impl Store { let tx_hash_bytes = transaction_hash.as_bytes(); let tx = db.begin_read()?; - // Use prefix iterator to find all entries with this transaction hash + // rust-rocksdb's prefix_iterator_cf seeks but does not bound iteration — + // caller must stop on the first prefix mismatch. let mut iter = tx.prefix_iterator(TRANSACTION_LOCATIONS, tx_hash_bytes)?; let mut transaction_locations = Vec::new(); while let Some(Ok((key, value))) = iter.next() { - // Ensure key is exactly tx_hash + block_hash (32 + 32 = 64 bytes) - // and starts with our exact tx_hash + // Key is tx_hash (32) + block_hash (32) = 64 bytes. if key.len() == 64 && &key[0..32] == tx_hash_bytes { transaction_locations.push(<(BlockNumber, BlockHash, Index)>::decode(&value)?); + } else { + break; } }