Skip to content

fix: optimize Block::from_filecoin_tipset#7062

Open
hanabi1224 wants to merge 1 commit into
mainfrom
hm/refactor-from_filecoin_tipset
Open

fix: optimize Block::from_filecoin_tipset#7062
hanabi1224 wants to merge 1 commit into
mainfrom
hm/refactor-from_filecoin_tipset

Conversation

@hanabi1224
Copy link
Copy Markdown
Contributor

@hanabi1224 hanabi1224 commented May 15, 2026

Summary of changes

Changes introduced in this pull request:

Reference issue to close (if applicable)

Closes

Other information and links

Change checklist

  • I have performed a self-review of my own code,
  • I have made corresponding changes to the documentation. All new code adheres to the team's documentation standards,
  • I have added tests that prove my fix is effective or that my feature works (if possible),
  • I have made sure the CHANGELOG is up-to-date. All user-facing changes should be reflected in this document.

Outside contributions

  • I have read and agree to the CONTRIBUTING document.
  • I have read and agree to the AI Policy document. I understand that failure to comply with the guidelines will lead to rejection of the pull request.

Summary by CodeRabbit

  • New Features

    • Ethereum RPC block queries now use an improved caching strategy, returning shared cached block data for more efficient responses.
  • Refactor

    • Core caching system generalized for greater flexibility and performance.
    • Cache initialization streamlined across the codebase for more consistent metrics and naming.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

Walkthrough

Refactors LRU cache key trait and constructors, updates many call sites to pass cache identifiers as string literals, generalizes TipsetStateCache into ForestLruCache<K,V> and wires it into StateManager, and reworks Ethereum RPC block construction to cache and return Arc via ForestLruCache.

Changes

LRU Cache Infrastructure and Ethereum Block Caching

Layer / File(s) Summary
LRU trait & constructor API
src/utils/cache/lru.rs, src/utils/cache/mod.rs
Add LruKeyConstraints, change SizeTrackingLruCache key bound to LruKeyConstraints, and make public constructors accept cache_name: impl Into<Cow<'static, str>>; re-export the trait.
Call-site updates to cache name args
src/beacon/drand.rs, src/chain/store/chain_store.rs, src/chain/store/index.rs, src/chain_sync/bad_block_cache.rs, src/db/blockstore_with_read_cache.rs, src/db/car/mod.rs, src/message_pool/msgpool/msg_pool.rs, src/state_migration/common/mod.rs, src/rpc/methods/f3.rs
Replace .into()-based cache-name construction with direct string literals when calling the updated SizeTrackingLruCache constructors.
Generic ForestLruCache implementation
src/state_manager/cache.rs
Replace TipsetStateCache with ForestLruCache<K,V> backed by SizeTrackingLruCache<K,V>, generalize pending-computation map to key type K, update APIs (get_or_else, get_map, get, insert, remove) and tests to use the generic key type.
StateManager cache wiring
src/state_manager/mod.rs
Make cache module public, import TipsetKey and ForestLruCache, switch StateManager.cache to ForestLruCache<TipsetKey, ExecutedTipset>, and initialize it with ForestLruCache::new("tipset_state_executed_tipset").
Ethereum Block construction and caching
src/rpc/methods/eth.rs, src/rpc/methods/eth/types.rs, src/rpc/methods/chain.rs
Use ForestLruCache to cache Arc<Block> for full and hash-only variants; change Block::from_filecoin_tipset to return Arc<Block> and avoid mutating shared Arc when producing hash-only views; update EthGetBlockByHash/EthGetBlockByNumber return types and consolidate eth imports in chain.rs; change ApiHeaders to hold Arc<Block>.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • akaladarshi
  • LesnyRumcajs
  • sudo-shashank
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix: optimize Block::from_filecoin_tipset' accurately reflects the main change: Block::from_filecoin_tipset was refactored to use Arc-based caching with separate caches for full and hash-only transactions, which is a significant optimization of that specific method.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hm/refactor-from_filecoin_tipset
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch hm/refactor-from_filecoin_tipset

Comment @coderabbitai help to get the list of available commands and usage tips.

@hanabi1224 hanabi1224 marked this pull request as ready for review May 15, 2026 10:01
@hanabi1224 hanabi1224 requested a review from a team as a code owner May 15, 2026 10:01
@hanabi1224 hanabi1224 requested review from LesnyRumcajs and akaladarshi and removed request for a team May 15, 2026 10:01
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/state_manager/cache.rs (1)

113-121: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clean up pending on compute failure.

When compute().await returns Err, the key remains in pending because cleanup only happens in insert/remove. This can cause unbounded growth of pending for repeated failures across unique keys.

Proposed fix
-                        let value = compute().await?;
-                        // Write back to cache, release lock and return value
-                        self.insert(key.clone(), value.clone());
-                        Ok(value)
+                        match compute().await {
+                            Ok(value) => {
+                                // Write back to cache, release lock and return value
+                                self.insert(key.clone(), value.clone());
+                                Ok(value)
+                            }
+                            Err(err) => {
+                                // Avoid leaking failed keys in pending map
+                                self.cache.write().pending.remove(key);
+                                Err(err)
+                            }
+                        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/state_manager/cache.rs` around lines 113 - 121, The failure path
currently leaves the key in pending when compute().await returns Err, causing
pending to grow; modify the None branch so you capture the result of
compute().await into a variable, then match on it: on Ok(value) call
self.insert(key.clone(), value.clone()) and return Ok(value), and on Err(err)
call self.remove(&key) (or otherwise remove the key from pending) before
returning Err(err); ensure pending cleanup happens on both success and failure
paths and reference the existing pending, compute(), insert, and remove symbols
to locate and update the code.
🧹 Nitpick comments (1)
src/state_manager/cache.rs (1)

97-99: ⚡ Quick win

Use a cache-specific metric label instead of STATE_MANAGER_TIPSET.

ForestLruCache is now generic, but hit/miss metrics are still hardcoded to a tipset-specific label. This makes metrics inaccurate when the cache is reused for other key/value domains.

Also applies to: 107-109, 115-117

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/state_manager/cache.rs` around lines 97 - 99, The LRU cache metrics are
using the hardcoded label STATE_MANAGER_TIPSET which makes ForestLruCache's
hit/miss counters inaccurate when reused; update ForestLruCache to accept or
derive a cache-specific metric label (e.g., add a label field or generic
parameter on ForestLruCache) and replace the direct uses of
crate::metrics::values::STATE_MANAGER_TIPSET in the
crate::metrics::LRU_CACHE_HIT and crate::metrics::LRU_CACHE_MISS calls
(locations around the inc() calls at the hit/miss sites) to use that
cache-specific label instead, ensuring the constructor or factory for
ForestLruCache sets the appropriate label for each cache instance.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/state_manager/cache.rs`:
- Around line 113-121: The failure path currently leaves the key in pending when
compute().await returns Err, causing pending to grow; modify the None branch so
you capture the result of compute().await into a variable, then match on it: on
Ok(value) call self.insert(key.clone(), value.clone()) and return Ok(value), and
on Err(err) call self.remove(&key) (or otherwise remove the key from pending)
before returning Err(err); ensure pending cleanup happens on both success and
failure paths and reference the existing pending, compute(), insert, and remove
symbols to locate and update the code.

---

Nitpick comments:
In `@src/state_manager/cache.rs`:
- Around line 97-99: The LRU cache metrics are using the hardcoded label
STATE_MANAGER_TIPSET which makes ForestLruCache's hit/miss counters inaccurate
when reused; update ForestLruCache to accept or derive a cache-specific metric
label (e.g., add a label field or generic parameter on ForestLruCache) and
replace the direct uses of crate::metrics::values::STATE_MANAGER_TIPSET in the
crate::metrics::LRU_CACHE_HIT and crate::metrics::LRU_CACHE_MISS calls
(locations around the inc() calls at the hit/miss sites) to use that
cache-specific label instead, ensuring the constructor or factory for
ForestLruCache sets the appropriate label for each cache instance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1580e29f-62bf-49f9-9e75-85cf4bac19ab

📥 Commits

Reviewing files that changed from the base of the PR and between c89d3e3 and 040c058.

📒 Files selected for processing (16)
  • src/beacon/drand.rs
  • src/chain/store/chain_store.rs
  • src/chain/store/index.rs
  • src/chain_sync/bad_block_cache.rs
  • src/db/blockstore_with_read_cache.rs
  • src/db/car/mod.rs
  • src/message_pool/msgpool/msg_pool.rs
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/f3.rs
  • src/state_manager/cache.rs
  • src/state_manager/mod.rs
  • src/state_migration/common/mod.rs
  • src/utils/cache/lru.rs
  • src/utils/cache/mod.rs

@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

❌ Patch coverage is 85.07463% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.33%. Comparing base (c89d3e3) to head (d1531d8).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/rpc/methods/eth.rs 80.00% 11 Missing and 7 partials ⚠️
src/rpc/methods/f3.rs 0.00% 1 Missing ⚠️
src/state_manager/cache.rs 95.23% 1 Missing ⚠️
Additional details and impacted files
Files with missing lines Coverage Δ
src/beacon/drand.rs 75.26% <100.00%> (ø)
src/chain/store/chain_store.rs 69.93% <100.00%> (ø)
src/chain/store/index.rs 89.62% <100.00%> (-0.05%) ⬇️
src/chain_sync/bad_block_cache.rs 79.48% <100.00%> (ø)
src/db/blockstore_with_read_cache.rs 93.33% <ø> (-0.09%) ⬇️
src/db/car/mod.rs 85.71% <100.00%> (ø)
src/message_pool/msgpool/msg_pool.rs 88.79% <100.00%> (+0.14%) ⬆️
src/rpc/methods/chain.rs 55.65% <ø> (ø)
src/rpc/methods/eth/types.rs 64.14% <ø> (ø)
src/state_manager/mod.rs 65.43% <100.00%> (ø)
... and 5 more

... and 8 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c89d3e3...d1531d8. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@hanabi1224 hanabi1224 added the RPC requires calibnet RPC checks to run on CI label May 15, 2026
@hanabi1224 hanabi1224 force-pushed the hm/refactor-from_filecoin_tipset branch from 040c058 to d1531d8 Compare May 15, 2026 11:50
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/rpc/methods/eth.rs (1)

486-499: ⚡ Quick win

Add a regression test for full/hash cache isolation.

The non-obvious contract here is that a hash-only request must never mutate the cached full-tx block. A focused test that fetches TxInfo::Full and then TxInfo::Hash for the same tipset, and asserts the original full block still contains Transactions::Full, would lock this in.

Also applies to: 593-598

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rpc/methods/eth.rs` around lines 486 - 499, Add a regression test that
verifies hash-only requests don't mutate cached full-tx blocks: call the
existing method that uses TxInfo::Full (which triggers
from_filecoin_tipset_with_full_tx and caches a full block), then call the path
that uses TxInfo::Hash (which uses ETH_BLOCK_HASH_TX_CACHE and
downcast_full_transaction_to_hash), and finally assert the originally returned
full block still contains Transactions::Full (i.e., its transaction entries
remain full, not converted to hashes). Repeat the same pattern for the other
code path referenced (the similar TxInfo handling around the other cache usage)
to ensure both cache branches preserve full/hash isolation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/rpc/methods/eth.rs`:
- Around line 481-484: The two caches ETH_BLOCK_HASH_TX_CACHE and
ETH_BLOCK_FULL_TX_CACHE are both using Block::block_cache_size(), doubling the
intended budget; update initialization to either split the existing
FOREST_ETH_BLOCK_CACHE_SIZE between them (e.g., use Block::block_cache_size() /
2 for each) or introduce separate configuration knobs (e.g.,
FOREST_ETH_BLOCK_HASH_CACHE_SIZE and FOREST_ETH_BLOCK_FULL_CACHE_SIZE) and use
those values when constructing ForestLruCache in ETH_BLOCK_HASH_TX_CACHE and
ETH_BLOCK_FULL_TX_CACHE (and the other similar cache inits around the same file)
so the total memory matches the intended budget and each cache uses the correct
size parameter.

---

Nitpick comments:
In `@src/rpc/methods/eth.rs`:
- Around line 486-499: Add a regression test that verifies hash-only requests
don't mutate cached full-tx blocks: call the existing method that uses
TxInfo::Full (which triggers from_filecoin_tipset_with_full_tx and caches a full
block), then call the path that uses TxInfo::Hash (which uses
ETH_BLOCK_HASH_TX_CACHE and downcast_full_transaction_to_hash), and finally
assert the originally returned full block still contains Transactions::Full
(i.e., its transaction entries remain full, not converted to hashes). Repeat the
same pattern for the other code path referenced (the similar TxInfo handling
around the other cache usage) to ensure both cache branches preserve full/hash
isolation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 220b1b85-e462-4128-bb0f-96edb4532652

📥 Commits

Reviewing files that changed from the base of the PR and between 040c058 and d1531d8.

📒 Files selected for processing (16)
  • src/beacon/drand.rs
  • src/chain/store/chain_store.rs
  • src/chain/store/index.rs
  • src/chain_sync/bad_block_cache.rs
  • src/db/blockstore_with_read_cache.rs
  • src/db/car/mod.rs
  • src/message_pool/msgpool/msg_pool.rs
  • src/rpc/methods/chain.rs
  • src/rpc/methods/eth.rs
  • src/rpc/methods/eth/types.rs
  • src/rpc/methods/f3.rs
  • src/state_manager/cache.rs
  • src/state_manager/mod.rs
  • src/state_migration/common/mod.rs
  • src/utils/cache/lru.rs
  • src/utils/cache/mod.rs
✅ Files skipped from review due to trivial changes (6)
  • src/utils/cache/mod.rs
  • src/chain_sync/bad_block_cache.rs
  • src/db/car/mod.rs
  • src/message_pool/msgpool/msg_pool.rs
  • src/chain/store/chain_store.rs
  • src/rpc/methods/chain.rs
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/rpc/methods/f3.rs
  • src/beacon/drand.rs
  • src/rpc/methods/eth/types.rs
  • src/state_manager/mod.rs
  • src/utils/cache/lru.rs
  • src/state_manager/cache.rs

Comment thread src/rpc/methods/eth.rs
Comment on lines +481 to 484
static ETH_BLOCK_HASH_TX_CACHE: LazyLock<ForestLruCache<CidWrapper, Arc<Block>>> =
LazyLock::new(|| {
const DEFAULT_CACHE_SIZE: NonZeroUsize = nonzero!(500usize);
let cache_size = std::env::var("FOREST_ETH_BLOCK_CACHE_SIZE")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(DEFAULT_CACHE_SIZE);
SizeTrackingLruCache::new_with_metrics("eth_block".into(), cache_size)
ForestLruCache::with_size("eth_block_hash_tx", Block::block_cache_size())
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

FOREST_ETH_BLOCK_CACHE_SIZE now budgets two independent caches.

Both eth_block_hash_tx and eth_block_full_tx are initialized with Block::block_cache_size(), so a setting of N now allows up to 2N cached blocks. On this path that can materially increase resident memory compared to the previous single-cache budget.

Please either split the existing budget across the two caches or introduce separate knobs with explicit names.

Also applies to: 508-510, 582-590

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/rpc/methods/eth.rs` around lines 481 - 484, The two caches
ETH_BLOCK_HASH_TX_CACHE and ETH_BLOCK_FULL_TX_CACHE are both using
Block::block_cache_size(), doubling the intended budget; update initialization
to either split the existing FOREST_ETH_BLOCK_CACHE_SIZE between them (e.g., use
Block::block_cache_size() / 2 for each) or introduce separate configuration
knobs (e.g., FOREST_ETH_BLOCK_HASH_CACHE_SIZE and
FOREST_ETH_BLOCK_FULL_CACHE_SIZE) and use those values when constructing
ForestLruCache in ETH_BLOCK_HASH_TX_CACHE and ETH_BLOCK_FULL_TX_CACHE (and the
other similar cache inits around the same file) so the total memory matches the
intended budget and each cache uses the correct size parameter.

Comment thread src/rpc/methods/eth.rs
static ETH_BLOCK_HASH_TX_CACHE: LazyLock<ForestLruCache<CidWrapper, Arc<Block>>> =
LazyLock::new(|| {
const DEFAULT_CACHE_SIZE: NonZeroUsize = nonzero!(500usize);
let cache_size = std::env::var("FOREST_ETH_BLOCK_CACHE_SIZE")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this must be removed from the docs, no?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah no, I see it's now used for full and hash.

Copy link
Copy Markdown
Member

@LesnyRumcajs LesnyRumcajs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, let's have some numbers to confirm the optimization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RPC requires calibnet RPC checks to run on CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants