From 3016c6850cc37849380ad4b58c5e658378b3ff7d Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Thu, 16 Apr 2026 21:58:33 +0800 Subject: [PATCH] fix(perf): cache resolve_to_deterministic_address result --- src/rpc/methods/eth/filter/mod.rs | 42 +++++++++++++++---------------- src/state_manager/mod.rs | 31 ++++++++++++++++------- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/rpc/methods/eth/filter/mod.rs b/src/rpc/methods/eth/filter/mod.rs index 6f9fca9e620..86a1737fea8 100644 --- a/src/rpc/methods/eth/filter/mod.rs +++ b/src/rpc/methods/eth/filter/mod.rs @@ -303,6 +303,7 @@ impl EthEventHandler { let ExecutedTipset { executed_messages, .. } = ctx.state_manager.load_executed_tipset(tipset).await?; + let mut resolved_id_addrs = HashMap::default(); let mut event_count = 0; for ( msg_idx, @@ -315,37 +316,36 @@ impl EthEventHandler { let event_idx_base = u64::try_from(event_count)?; event_count += events.len(); for (event_idx, event) in (event_idx_base..).zip(events.iter()) { - let id_addr = Address::new_id(event.emitter()); - let result = ctx - .state_manager - .resolve_to_deterministic_address(id_addr, tipset) - .await - .with_context(|| { - format!( - "resolving address {} failed (EPOCH = {})", - id_addr, - tipset.epoch() - ) - }); - let resolved = if let Ok(resolved) = result { + let emitter = event.emitter(); + let id_addr = Address::new_id(emitter); + let resolved_opt = if let Some(r) = resolved_id_addrs.get(&emitter) { + *r + } else { + let r = ctx + .state_manager + .resolve_to_deterministic_address(id_addr, tipset) + .await + .ok(); + resolved_id_addrs.insert(emitter, r); + r + }; + let resolved = if let Some(resolved) = resolved_opt { resolved + } else if matches!(skip_event, SkipEvent::OnUnresolvedAddress) { + // Skip event + continue; } else { - if let SkipEvent::OnUnresolvedAddress = skip_event { - // Skip event - continue; - } else { - id_addr - } + id_addr }; - let entries: Vec = event.entries(); + let entries = event.entries(); let matched = if let Some(spec) = spec { spec.matches(&resolved, &entries)? } else { true }; if matched { - let entries: Vec = entries + let entries = entries .into_iter() .map(|entry| { let (flags, key, codec, value) = entry.into_parts(); diff --git a/src/state_manager/mod.rs b/src/state_manager/mod.rs index 716da9c26cc..635152f9c62 100644 --- a/src/state_manager/mod.rs +++ b/src/state_manager/mod.rs @@ -55,6 +55,7 @@ use crate::state_manager::cache::TipsetStateCache; use crate::state_manager::chain_rand::draw_randomness; use crate::state_migration::run_state_migrations; use crate::utils::ShallowClone as _; +use crate::utils::cache::SizeTrackingLruCache; use crate::utils::get_size::{GetSize, vec_heap_size_helper}; use ahash::{HashMap, HashMapExt}; use anyhow::{Context as _, bail, ensure}; @@ -84,6 +85,7 @@ use tokio::sync::{RwLock, broadcast::error::RecvError}; use tracing::{error, info, instrument, warn}; const DEFAULT_TIPSET_CACHE_SIZE: NonZeroUsize = nonzero!(1024usize); +const DEFAULT_ID_TO_DETERMINISTIC_ADDRESS_CACHE_SIZE: NonZeroUsize = nonzero!(1024usize); pub const EVENTS_AMT_BITWIDTH: u32 = 5; /// Result of executing an individual chain message in a tipset. @@ -190,6 +192,7 @@ pub struct StateManager { cs: Arc>, /// This is a cache which indexes tipsets to their calculated state output (state root, receipt root). cache: TipsetStateCache, + id_to_deterministic_address_cache: SizeTrackingLruCache, beacon: Arc, engine: Arc, } @@ -217,6 +220,10 @@ where cache: TipsetStateCache::new("executed_tipset"), // For StateOutput beacon, engine, + id_to_deterministic_address_cache: SizeTrackingLruCache::new_with_metrics( + "id_to_deterministic_address".into(), + DEFAULT_ID_TO_DETERMINISTIC_ADDRESS_CACHE_SIZE, + ), }) } @@ -1790,20 +1797,26 @@ where match address.protocol() { BLS | Secp256k1 | Delegated => Ok(address), Actor => anyhow::bail!("cannot resolve actor address to key address"), - _ => { + ID => { + let id = address.id()?; + if let Some(cached) = self.id_to_deterministic_address_cache.get_cloned(&id) { + return Ok(cached); + } // First try to resolve the actor in the parent state, so we don't have to compute anything. - if let Ok(state) = + let resolved = if let Ok(state) = StateTree::new_from_root(self.blockstore_owned(), ts.parent_state()) && let Ok(address) = state .resolve_to_deterministic_addr(self.chain_store().blockstore(), address) { - return Ok(address); - } - - // If that fails, compute the tip-set and try again. - let TipsetState { state_root, .. } = self.load_tipset_state(ts).await?; - let state = StateTree::new_from_root(self.blockstore_owned(), &state_root)?; - state.resolve_to_deterministic_addr(self.chain_store().blockstore(), address) + address + } else { + // If that fails, compute the tip-set and try again. + let TipsetState { state_root, .. } = self.load_tipset_state(ts).await?; + let state = StateTree::new_from_root(self.blockstore_owned(), &state_root)?; + state.resolve_to_deterministic_addr(self.chain_store().blockstore(), address)? + }; + self.id_to_deterministic_address_cache.push(id, resolved); + Ok(resolved) } } }