-
Notifications
You must be signed in to change notification settings - Fork 182
fix: refine Arc params to reduce pointer cloning #6255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughA comprehensive refactoring systematically replacing owned Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~40 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
src/state_manager/circulating_supply.rs (1)
151-159: Simplifying market::State::load argument removes an unnecessary referencePassing
db(a&Arc<DB>) instead of&dbavoids a&&Arc<DB>and better matches how similar loaders are typically called, with no behavioral change.You might also consider updating the adjacent
miner::State::load(&db, ..)andmultisig::State::load(&db, ..)calls to usedbdirectly for consistency, though this is purely cosmetic.src/chain_sync/tipset_syncer.rs (1)
225-331: Async/blocking validation split is sensible; consider grouping state-dependent tasks if contention shows upMoving base-fee and parent-weight checks into
spawn_blockingwith ownedArcblockstore, and keeping state-root/receipt-root and consensus checks in async tasks, is a reasonable separation: CPU/blockstore-heavy, synchronous code runs off the core runtime, and async pieces reuse the existingtipset_statepipeline. Everything captured into the tasks (Arc<StateManager<_>>,Arc<Block>,Arc<Tipset>,work_addr) isSend + 'static, soJoinSetusage remains sound.If you ever see excessive contention in these per-block validations, you could explore reusing a single cloned blockstore for both blocking closures or batching the "parent-dependent" checks (base fee, weight, state/receipt roots) into one blocking job to reduce task orchestration overhead, but that’s an optional micro-optimization.
src/state_manager/mod.rs (2)
470-508: Nice early-return viatry_lookup_state_from_next_tipsetIntegrating
self.try_lookup_state_from_next_tipset(tipset)intotipset_state_outputgives a cheap, blockstore-backed fast path before kicking off a full VM state computation. The method call via&Arc<Self>auto-deref is fine, and the logic still populates caches and indexes only when the state actually has to be computed.If this optimization proves valuable, you might consider instrumenting a metric (hit/miss rate) here to validate its impact in production, but that’s optional and can be added later.
1658-1681: Borrowed-tipsetresolve_to_deterministic_addresswiring is soundChanging
resolve_to_deterministic_addressto takets: &Arc<Tipset>and updatingminer_get_base_infoto call it with&tipsetkeeps all uses onArc<Tipset>while avoiding extra cloning. The fast path (parent-stateStateTree) and slow path (tipset_state(ts)then building a newStateTree) still implement the same resolution semantics.You could consider sharing the deterministic-address resolution logic with
resolve_to_key_addrto reduce duplication, but that’s a style refactor, not required.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (18)
src/chain/store/chain_store.rs(3 hunks)src/chain_sync/chain_follower.rs(1 hunks)src/chain_sync/sync_status.rs(1 hunks)src/chain_sync/tipset_syncer.rs(4 hunks)src/fil_cns/validation.rs(2 hunks)src/interpreter/fvm2.rs(1 hunks)src/interpreter/fvm3.rs(1 hunks)src/interpreter/fvm4.rs(1 hunks)src/libp2p/service.rs(2 hunks)src/rpc/methods/eth/filter/mod.rs(1 hunks)src/rpc/methods/f3.rs(1 hunks)src/rpc/methods/gas.rs(1 hunks)src/rpc/methods/miner.rs(1 hunks)src/rpc/methods/state.rs(2 hunks)src/rpc/methods/wallet.rs(1 hunks)src/rpc/mod.rs(1 hunks)src/state_manager/circulating_supply.rs(1 hunks)src/state_manager/mod.rs(13 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5930
File: build.rs:64-77
Timestamp: 2025-08-13T09:43:20.301Z
Learning: hanabi1224 prefers hard compile-time errors in build scripts rather than runtime safeguards or collision detection, believing it's better to fail fast and fix root causes of issues like malformed snapshot names.
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6057
File: src/cli/subcommands/f3_cmd.rs:0-0
Timestamp: 2025-09-09T10:37:17.947Z
Learning: hanabi1224 prefers having default timeouts (like 10m for --no-progress-timeout) to prevent commands from hanging indefinitely, even when the timeout flag isn't explicitly provided by users. This fail-fast approach is preferred over requiring explicit flag usage.
📚 Learning: 2025-10-17T14:24:47.046Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6167
File: src/tool/subcommands/state_compute_cmd.rs:89-91
Timestamp: 2025-10-17T14:24:47.046Z
Learning: In `src/tool/subcommands/state_compute_cmd.rs`, when using `ReadOpsTrackingStore` to generate minimal snapshots, `HEAD_KEY` should be written to `db.tracker` (not `db` itself) before calling `export_forest_car()`, because the export reads from the tracker MemoryDB which accumulates only the accessed data during computation.
Applied to files:
src/rpc/mod.rssrc/state_manager/circulating_supply.rssrc/interpreter/fvm4.rssrc/chain/store/chain_store.rs
📚 Learning: 2025-09-02T10:05:34.350Z
Learnt from: akaladarshi
Repo: ChainSafe/forest PR: 5923
File: src/rpc/registry/actors/miner.rs:221-223
Timestamp: 2025-09-02T10:05:34.350Z
Learning: For miner actor ChangeOwnerAddress and ChangeOwnerAddressExported methods: versions 8-10 use bare Address as parameter type, while versions 11+ use ChangeOwnerAddressParams. This reflects the evolution of the Filecoin miner actor parameter structures across versions.
Applied to files:
src/rpc/methods/eth/filter/mod.rssrc/rpc/methods/miner.rs
📚 Learning: 2025-08-18T03:09:47.932Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5944
File: src/chain/store/index.rs:0-0
Timestamp: 2025-08-18T03:09:47.932Z
Learning: In Forest's tipset_by_height caching implementation, hanabi1224 prefers performance-conscious solutions that leverage finality guarantees rather than expensive chain walking for fork detection. The approach of constraining cache lookups to finalized epochs (using CHECKPOINT_INTERVAL >= CHAIN_FINALITY) provides fork safety without the performance cost of ancestry verification.
Applied to files:
src/interpreter/fvm4.rssrc/interpreter/fvm3.rssrc/interpreter/fvm2.rs
📚 Learning: 2025-08-08T12:11:55.266Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5867
File: src/ipld/util.rs:461-487
Timestamp: 2025-08-08T12:11:55.266Z
Learning: Forest (src/ipld/util.rs, Rust): In UnorderedChainStream::poll_next, dropping `extract_sender` (when no more tipsets and the extract queue is empty) is the intended shutdown signal for workers. Any subsequent attempt to enqueue work after this drop is a logic error and should be treated as an error; do not change `send()` to ignore a missing sender.
Applied to files:
src/interpreter/fvm3.rssrc/interpreter/fvm2.rssrc/chain_sync/chain_follower.rs
🧬 Code graph analysis (8)
src/rpc/methods/eth/filter/mod.rs (1)
src/blocks/chain4u.rs (1)
tipset(183-185)
src/rpc/methods/gas.rs (1)
src/chain/store/chain_store.rs (1)
messages_for_tipset_with_cache(618-632)
src/chain_sync/tipset_syncer.rs (4)
src/chain/store/base_fee.rs (1)
compute_base_fee(59-98)src/fil_cns/mod.rs (1)
weight(91-96)src/fil_cns/weight.rs (1)
fil_cns(23-57)src/state_manager/mod.rs (1)
get_bls_public_key(1265-1282)
src/libp2p/service.rs (1)
src/chain_sync/network_context.rs (1)
peer_manager(134-136)
src/fil_cns/validation.rs (4)
src/state_manager/mod.rs (3)
chain_store(348-350)beacon_schedule(291-293)chain_config(358-360)src/utils/misc/env.rs (1)
is_env_truthy(17-19)src/blocks/block.rs (1)
header(30-32)src/chain/store/chain_store.rs (1)
chain_config(252-254)
src/rpc/methods/f3.rs (1)
src/shim/actors/builtin/miner/mod.rs (1)
worker(896-898)
src/chain/store/chain_store.rs (3)
src/state_manager/mod.rs (4)
chain_index(353-355)chain_config(358-360)heaviest_tipset(208-210)new(180-182)src/interpreter/fvm2.rs (1)
new(44-61)src/shim/state_tree.rs (1)
new_from_tipset(190-192)
src/state_manager/mod.rs (1)
src/shim/actors/builtin/miner/mod.rs (1)
info(84-97)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Build forest binaries on Linux AMD64
- GitHub Check: tests
- GitHub Check: tests-release
- GitHub Check: All lint checks
- GitHub Check: cargo-publish-dry-run
- GitHub Check: Build MacOS
- GitHub Check: Build Ubuntu
🔇 Additional comments (28)
src/rpc/methods/f3.rs (1)
449-452: Using borrowed tipset here correctly matches the new resolve_to_deterministic_address API
tsis already a&Arc<Tipset>, so passing it directly avoids an unnecessaryArcclone while staying compatible with the updatedresolve_to_deterministic_addresssignature.src/libp2p/service.rs (2)
439-446: Switching handle_network_message to &PeerManager is type‑safe and avoids Arc noiseCallers now pass
&self.peer_manager(anArc<PeerManager>field), which deref‑coerces to&PeerManager, so this removes an extra Arc layer in the signature without changing behavior.
865-870: Borrowing PeerManager in handle_forest_behaviour_event is consistent with new call sitesAccepting
peer_manager: &PeerManageraligns with the updated run loop usage (&self.peer_manager) and keeps the handler free of unnecessary Arc cloning.src/rpc/methods/wallet.rs (1)
250-255: WalletSignMessage now correctly borrows the heaviest tipsetPassing
&tsintoresolve_to_deterministic_addressmatches the updated&Arc<Tipset>signature and removes an unnecessaryArcclone without changing semantics.src/rpc/methods/eth/filter/mod.rs (1)
293-307: Reusing the borrowed tipset in collect_events avoids redundant Arc cloning
tipsetis already a&Arc<Tipset>, andresolve_to_deterministic_addressnow takes&Arc<Tipset>, so passing it directly is correct and more efficient than cloning theArc.src/chain_sync/chain_follower.rs (1)
850-857: validate_tipset now borrows the StateManager Arc instead of taking ownershipPassing
&state_managerintovalidate_tipsetmatches the updated signature and removes an extraArcclone per task, without changing validation behavior.src/interpreter/fvm3.rs (1)
67-75: Borrowing chain_index/chain_config/heaviest_tipset into get_lookback_tipset_for_round is correctUsing
&self.chain_index,&self.chain_config, and&self.heaviest_tipsetmatches the refactoredget_lookback_tipset_for_roundsignature and avoids cloning theseArcs while preserving behavior.src/rpc/methods/gas.rs (1)
115-123: estimate_gas_premium now calls messages_for_tipset_with_cache using borrowed store and cacheSwitching to
data.store()and&data.msgs_in_tipsetaligns with the updatedmessages_for_tipset_with_cachesignature, eliminating extraArcclones without changing gas premium calculation logic.src/chain_sync/tipset_syncer.rs (4)
100-121: Arc borrowing change invalidate_tipsetlooks correctSwitching
state_managerto&Arc<StateManager<DB>>and cloning it only when spawningvalidate_blockkeeps the async tasks'staticwhile avoiding an extra Arc clone at the call site. The ownership and lifetimes still line up, andchainstorecontinues to be used only on the sync path foradd_to_tipset_tracker.
207-215:get_lookback_tipset_for_roundargument wiring is consistentPassing
state_manager.chain_store().chain_index()andstate_manager.chain_config()by reference, plus&base_tipset, matches how the helper is used elsewhere (e.g.,miner_get_base_info). The mapping to a state root handle and its later use inget_miner_work_addrpreserves the previous behavior.
352-370: BLS pubkey lookup now cleanly reuses the shared blockstore ArcUsing
let db = state_manager.blockstore();and then callingStateManager::get_bls_public_key(db, ...)avoids cloning the blockstoreArcat the call site while keeping the helper responsible for owning its ownArcclone. This keeps the interface tidy and aligns with other state-manager helpers.
483-488: Message-root computation correctly switches to borrowed blockstoreCalling
TipsetValidator::compute_msg_root(state_manager.blockstore(), ...)instead of passing an owned clone reduces unnecessaryArcchurn and is consistent with how other helpers now accept&Arc<DB>. No behavior change to the header vs. computed root comparison logic.src/state_manager/mod.rs (6)
440-451:get_all_sectorsreceiver change is consistentSwitching
get_all_sectorsto take&self(instead ofself: &Arc<Self>) matches the rest of the synchronous state-accessors in this impl and doesn’t affect behavior; it still walks throughget_actorandminer::State::loadas before.
582-668:call_raw/callborrowingselfandtipsetis an improvementUsing
&selfand&Arc<Tipset>incall_raw, and havingcallconstruct a localArc<Tipset>and pass&ts, removes unnecessaryArcindirection on the API without changing semantics. The VMExecutionContextstill receives an ownedArc::clone(tipset), so the lifetime and ownership over the heaviest tipset are preserved.
710-748: Circulating-supply call now reuses the shared blockstore handlePassing
self.blockstore()intoget_vm_circulating_supplyincall_with_gasaligns with the broader refactor of using borrowedArc<DB>everywhere. That avoids a redundantArcclone while leaving the VM construction path unchanged.
1550-1585: Range-validation helpers now use&selfcleanly
validate_rangeandvalidate_tipsetsno longer requireself: &Arc<Self>, which makes them simpler to call from both Arc-managed and direct contexts. They still pass clonedArchandles (chain index, chain config, beacon, engine) into the freevalidate_tipsetsfunction, so the actual validation behavior is untouched.
1588-1622: Search helpers now consistently borrowself
search_for_messageusing&selfand keeping the search logic incheck_search/search_back_for_messageunchanged is a straightforward API cleanup. The interplay between “from” tipset, heaviest tipset, and look-back semantics remains the same.
1265-1282:get_bls_public_keysignature matches new call sitesTaking
db: &Arc<DB>lets callers passstate_manager.blockstore()directly (as incheck_block_messages), while the helper remains responsible for cloning theArcforStateTree::new_from_root. Internally it still resolves to a key address and enforces the “must be BLS” invariant, so behavior is preserved with slightly cheaper call plumbing.src/interpreter/fvm4.rs (1)
67-75: BorrowingArcparameters here is correct and avoids extra clonesPassing
&self.chain_index,&self.chain_config, and&self.heaviest_tipsetmatches the updatedChainStore::get_lookback_tipset_for_roundsignature without changing behavior.src/interpreter/fvm2.rs (1)
63-71: Consistent Arc borrowing in lookback callUsing
&self.chain_index,&self.chain_config, and&self.heaviest_tipsetaligns with the newget_lookback_tipset_for_roundAPI and removes unnecessaryArccloning.src/rpc/mod.rs (1)
486-488: Expose the underlyingArc<DB>fromRPCState::storeReturning
&Arc<DB>directly fromchain_store().blockstore()is consistent withChainStore’s API and avoids extra indirection; existing callers can still treat it as aBlockstorevia deref.src/chain_sync/sync_status.rs (1)
141-185: Accepting&StateManager<DB>instead of&Arc<StateManager<DB>>is a safe simplificationThe method body only needs a shared reference, so dropping the
Arcfrom the signature reduces coupling and avoids needlessArccloning at call sites while preserving behavior.src/rpc/methods/miner.rs (1)
115-132: Miner lookback state now uses borrowedArcinputs correctlyPassing
ctx.chain_index(),ctx.chain_config(), and&parent_tipsetintoChainStore::get_lookback_tipset_for_round, then wrapping the returned state root inArckeeps the original semantics while avoiding redundantArcclones.src/fil_cns/validation.rs (1)
71-176: Async validation refactor correctly switches to borrowed/ArcinputsThe updated
get_lookback_tipset_for_roundcall and thespawn_blocking/async closures now:
- Move/cloned
Arcstate (state_manager,base_tipset,block,prev_beacon,lookback_state) into the tasks, and- Pass shared references (
&StateManager<_>,&Tipset,&Cid,&BeaconEntry,&Address) into the validation helpers.This removes unnecessary
Arccloning while keeping lifetimes and concurrency semantics sound.src/rpc/methods/state.rs (2)
84-95:StateCall::runnow correctly abstracts overStateManagerwithout exposingArcTaking
state_manager: &StateManager<DB>and calling it via&ctx.state_managerkeeps the call behavior identical while hidingArcfrom the API surface and avoiding redundant clones.
196-205: Pass tipset by reference intoresolve_to_deterministic_addressSwitching to
resolve_to_deterministic_address(address, &ts)avoids moving/cloning theArc<Tipset>and matches an interface that works on a borrowed tipset, without changing the call’s semantics.src/chain/store/chain_store.rs (2)
321-381: Refactored lookback helper cleanly borrowsArcinputs
get_lookback_tipset_for_roundnow:
- Accepts
&Arc<ChainIndex<_>>,&Arc<ChainConfig>, and&Arc<Tipset>,- Clones those
Arcs only where needed (apply_block_messages, return value).This preserves the prior logic while avoiding unnecessary
Arcmoves.
617-632:messages_for_tipsetnever populatesapplied/balances, so it always yields no messagesThe Arc-borrowing changes here look correct, but there is a pre‑existing logic bug in
messages_for_tipset:
appliedandbalancesstart empty.- They are only initialized when
applied.contains_key(from_address)istrue:if applied.contains_key(from_address) { let actor_state = state .get_actor(from_address)? .ok_or_else(|| Error::Other("Actor state not found".to_string()))?; applied.insert(*from_address, actor_state.sequence); balances.insert(*from_address, actor_state.balance.clone().into()); }- Because nothing is ever inserted before this check, it never runs for a new sender;
applied.get_mut(from_address)then returnsNone, and the message is skipped.- As a result,
messages_for_tipset(and anything usingmessages_for_tipset_with_cache) will always return an emptyVec<ChainMessage>for a fresh tipset.This contradicts the intended Lotus‑style behavior of initializing sender state on first encounter and then walking sequential nonces.
Flip the condition so initialization happens for first‑seen senders:
- if applied.contains_key(from_address) { + if !applied.contains_key(from_address) { let actor_state = state .get_actor(from_address)? .ok_or_else(|| Error::Other("Actor state not found".to_string()))?; applied.insert(*from_address, actor_state.sequence); balances.insert(*from_address, actor_state.balance.clone().into()); }With this change, the rest of the nonce/balance filtering logic becomes effective and
messages_for_tipsetwill actually surface valid messages as intended.Also applies to: 637-692
⛔ Skipped due to learnings
Learnt from: hanabi1224 Repo: ChainSafe/forest PR: 5867 File: src/ipld/util.rs:461-487 Timestamp: 2025-08-08T12:11:55.266Z Learning: Forest (src/ipld/util.rs, Rust): In UnorderedChainStream::poll_next, dropping `extract_sender` (when no more tipsets and the extract queue is empty) is the intended shutdown signal for workers. Any subsequent attempt to enqueue work after this drop is a logic error and should be treated as an error; do not change `send()` to ignore a missing sender.Learnt from: hanabi1224 Repo: ChainSafe/forest PR: 6167 File: src/tool/subcommands/state_compute_cmd.rs:89-91 Timestamp: 2025-10-17T14:24:47.046Z Learning: In `src/tool/subcommands/state_compute_cmd.rs`, when using `ReadOpsTrackingStore` to generate minimal snapshots, `HEAD_KEY` should be written to `db.tracker` (not `db` itself) before calling `export_forest_car()`, because the export reads from the tracker MemoryDB which accumulates only the accessed data during computation.
Summary of changes
Changes introduced in this pull request:
Reference issue to close (if applicable)
Closes
Other information and links
Change checklist
Summary by CodeRabbit