-
Notifications
You must be signed in to change notification settings - Fork 182
fix: avoid deep-cloning Tipset and FullTipset #6274
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
WalkthroughThis PR replaces widespread Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Areas requiring extra attention:
Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (1)📓 Common learnings⏰ 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)
🔇 Additional comments (3)
Comment |
| pub struct Tipset { | ||
| /// Sorted | ||
| #[get_size(size_fn = nunny_vec_heap_size_helper)] | ||
| headers: NonEmpty<CachingBlockHeader>, |
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.
The major changes are within this file (tipset.rs)
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/benchmark_private/tipset_validation.rs (1)
48-66: Type mismatch:Arc<Tipset>created butTipsetexpected in return type.The function signature on line 48 declares the return type as
Tipset, but line 50 createsArc::new(snap_car.heaviest_tipset()?), which producesArc<Tipset>. ThisArc<Tipset>is then returned on line 66, causing a type mismatch.Apply this diff to remove the
Arcwrapper and align with the new API semantics:- let ts = Arc::new(snap_car.heaviest_tipset()?); + let ts = snap_car.heaviest_tipset()?;Also update line 65 to remove the redundant
.clone()on the warmup call, since the Arc wrapper is no longer needed:- validate(state_manager.clone(), ts.clone()).await; + validate(state_manager.clone(), ts.clone()).await;(Note: The
.clone()ontsis still needed sincevalidatetakes ownership of theTipset, but it should now be a cheap clone due to internal Arc-wrapped collections per the PR's design.)
🧹 Nitpick comments (4)
src/rpc/methods/f3.rs (1)
160-466: GetPowerTable::compute now borrowsTipsetdirectly; consider dropping redundant&tsin v12+ macro callsThe switch to
ts: &Tipsetaligns this helper with the rest of the state-manager APIs and removes the extra Arc layer; the internal uses (get_actor_state*,resolve_to_deterministic_address, logging, etc.) are all consistent and avoid additional cloning, which fits the PR goal.One minor clean‑up: in the v12–v17 branches you still pass
&tsintohandle_miner_state_v12_on!, which makes$tsan&&Tipsetwhile v8–v11 usetsdirectly. It’s harmless but slightly noisy and inconsistent with the earlier arms; you can simplify the call sites to taketsas an&Tipset:- power::State::V12(s) => { - handle_miner_state_v12_on!( - v12, - id_power_worker_mappings, - &ts, - s, - &from_policy_v13_to_v12(&ctx.chain_config().policy) - ); - } + power::State::V12(s) => { + handle_miner_state_v12_on!( + v12, + id_power_worker_mappings, + ts, + s, + &from_policy_v13_to_v12(&ctx.chain_config().policy) + ); + } @@ - power::State::V13(s) => { + power::State::V13(s) => { handle_miner_state_v12_on!( v13, id_power_worker_mappings, - &ts, + ts, s, &ctx.chain_config().policy ); } @@ - power::State::V14(s) => { + power::State::V14(s) => { handle_miner_state_v12_on!( v14, id_power_worker_mappings, - &ts, + ts, s, &from_policy_v13_to_v14(&ctx.chain_config().policy) ); } @@ - power::State::V15(s) => { + power::State::V15(s) => { handle_miner_state_v12_on!( v15, id_power_worker_mappings, - &ts, + ts, s, &from_policy_v13_to_v15(&ctx.chain_config().policy) ); } @@ - power::State::V16(s) => { + power::State::V16(s) => { handle_miner_state_v12_on!( v16, id_power_worker_mappings, - &ts, + ts, s, &from_policy_v13_to_v16(&ctx.chain_config().policy) ); } @@ - power::State::V17(s) => { + power::State::V17(s) => { handle_miner_state_v12_on!( v17, id_power_worker_mappings, - &ts, + ts, s, &from_policy_v13_to_v17(&ctx.chain_config().policy) ); }src/message_pool/msgpool/selection.rs (1)
9-29: Tipset-by-valuerun_head_changerefactor looks sound;BorrowMutuse is slightly overkillPassing
from/toas ownedTipsetand cloning them intoleft_chain/right_chainpreserves the previous behaviour while avoiding deep cloning, assumingTipsetstays internally Arc-backed. The epoch walk and LCA search logic are unchanged.The new
BorrowMutimport and usage (rmsgs.borrow_mut()) work but are a bit unnecessary here:rmsgsis already a&mut HashMap<…>, which is exactly whatremove_from_selected_msgsconceptually needs. If that helper is now generic overimpl BorrowMut<_>, it’s more idiomatic to take theBorrowMutat the callee boundary and keep call sites passing&mutdirectly.Not a blocker, but you could simplify by either:
- Letting
remove_from_selected_msgsitself call.borrow_mut()on its argument, or- Narrowing the parameter type back to
&mut Pendingand passingrmsgswithout the trait call here.Also applies to: 815-869
src/rpc/methods/eth/filter/mod.rs (1)
405-414: Remove unnecessary Arc wrapping.Since
collect_eventsnow accepts&Tipsetinstead of&Arc<Tipset>, theArc::new()wrapping at lines 407 and 412 is no longer necessary. Due to Deref coercion this still compiles, but it allocates an Arc unnecessarily.ParsedFilterTipsets::Hash(block_hash) => { let tipset = get_tipset_from_hash(ctx.chain_store(), block_hash)?; - let tipset = Arc::new(tipset); Self::collect_events(ctx, &tipset, Some(pf), skip_event, &mut collected_events) .await?; } ParsedFilterTipsets::Key(tsk) => { - let tipset = Arc::new(Tipset::load_required(ctx.store(), tsk)?); + let tipset = Tipset::load_required(ctx.store(), tsk)?; Self::collect_events(ctx, &tipset, Some(pf), skip_event, &mut collected_events) .await?; }src/rpc/methods/eth.rs (1)
1505-1547:get_block_receiptsstill wrapsTipsetinto anArcunnecessarilyThe function:
- Resolves
ts: Tipset, then immediately doeslet ts_ref = Arc::new(ts);.- Passes
&ts_refintoexecute_tipsetandnew_eth_tx_receipt, which now both only require&Tipset.This reintroduces an
Arc<Tipset>layer in a hot path without need; a simple refactor to keeptsas a plainTipsetand pass&tseverywhere would reduce allocations and keep the API surface cleaner.Example refactor:
- let ts = tipset_by_block_number_or_hash(...)?; - ... - let ts_ref = Arc::new(ts); - let ts_key = ts_ref.key(); + let ts = tipset_by_block_number_or_hash(...)?; + let ts_key = ts.key(); ... - let (state_root, msgs_and_receipts) = execute_tipset(ctx, &ts_ref).await?; + let (state_root, msgs_and_receipts) = execute_tipset(ctx, &ts).await?; ... - ts_ref.epoch(), - &ts_key.cid()?, + ts.epoch(), + &ts_key.cid()?, ... - let receipt = new_eth_tx_receipt(ctx, &ts_ref, &tx, &receipt).await?; + let receipt = new_eth_tx_receipt(ctx, &ts, &tx, &receipt).await?;This keeps behavior identical but avoids the extra
Arc.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (45)
src/beacon/drand.rs(0 hunks)src/benchmark_private/tipset_validation.rs(2 hunks)src/blocks/header.rs(0 hunks)src/blocks/ticket.rs(1 hunks)src/blocks/tipset.rs(11 hunks)src/blocks/vrf_proof.rs(3 hunks)src/chain/store/chain_store.rs(8 hunks)src/chain/store/index.rs(12 hunks)src/chain_sync/chain_follower.rs(19 hunks)src/chain_sync/network_context.rs(2 hunks)src/chain_sync/tipset_syncer.rs(2 hunks)src/cli/subcommands/chain_cmd.rs(1 hunks)src/cli/subcommands/f3_cmd.rs(2 hunks)src/cli/subcommands/info_cmd.rs(7 hunks)src/daemon/db_util.rs(1 hunks)src/daemon/mod.rs(2 hunks)src/fil_cns/validation.rs(1 hunks)src/interpreter/fvm2.rs(2 hunks)src/interpreter/fvm3.rs(2 hunks)src/interpreter/fvm4.rs(2 hunks)src/interpreter/vm.rs(1 hunks)src/libp2p/chain_exchange/message.rs(1 hunks)src/message_pool/msgpool/mod.rs(5 hunks)src/message_pool/msgpool/msg_pool.rs(4 hunks)src/message_pool/msgpool/provider.rs(4 hunks)src/message_pool/msgpool/selection.rs(2 hunks)src/message_pool/msgpool/test_provider.rs(4 hunks)src/rpc/methods/chain.rs(12 hunks)src/rpc/methods/eth.rs(17 hunks)src/rpc/methods/eth/filter/mod.rs(3 hunks)src/rpc/methods/f3.rs(2 hunks)src/rpc/methods/f3/types.rs(0 hunks)src/rpc/methods/gas.rs(1 hunks)src/rpc/methods/state.rs(4 hunks)src/rpc/methods/sync.rs(2 hunks)src/rpc/mod.rs(1 hunks)src/state_manager/chain_rand.rs(6 hunks)src/state_manager/mod.rs(34 hunks)src/state_manager/tests.rs(6 hunks)src/tool/offline_server/server.rs(2 hunks)src/tool/subcommands/api_cmd/api_compare_tests.rs(1 hunks)src/tool/subcommands/archive_cmd.rs(8 hunks)src/tool/subcommands/benchmark_cmd.rs(2 hunks)src/tool/subcommands/snapshot_cmd.rs(2 hunks)src/tool/subcommands/state_compute_cmd.rs(1 hunks)
💤 Files with no reviewable changes (3)
- src/blocks/header.rs
- src/beacon/drand.rs
- src/rpc/methods/f3/types.rs
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
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.
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-09-11T16:03:14.328Z
Learnt from: akaladarshi
Repo: ChainSafe/forest PR: 6000
File: src/tool/subcommands/api_cmd/state_decode_params_tests/miner.rs:4-9
Timestamp: 2025-09-11T16:03:14.328Z
Learning: In the Forest codebase's state_decode_params_tests module, common imports like `to_vec`, `TokenAmount`, `Cid`, etc. are centralized in the mod.rs file and made available to submodules through `use super::*;`, eliminating the need for duplicate imports in individual test files.
Applied to files:
src/state_manager/tests.rssrc/rpc/methods/chain.rssrc/chain/store/index.rs
📚 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/state_manager/tests.rssrc/daemon/db_util.rssrc/tool/subcommands/state_compute_cmd.rssrc/tool/subcommands/benchmark_cmd.rssrc/interpreter/fvm4.rssrc/tool/subcommands/snapshot_cmd.rssrc/tool/offline_server/server.rssrc/cli/subcommands/f3_cmd.rssrc/rpc/methods/state.rssrc/daemon/mod.rssrc/tool/subcommands/api_cmd/api_compare_tests.rssrc/interpreter/fvm2.rssrc/tool/subcommands/archive_cmd.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/rpc/mod.rssrc/tool/subcommands/snapshot_cmd.rssrc/tool/offline_server/server.rssrc/rpc/methods/sync.rssrc/chain_sync/network_context.rssrc/message_pool/msgpool/test_provider.rssrc/rpc/methods/state.rssrc/daemon/mod.rssrc/message_pool/msgpool/mod.rssrc/message_pool/msgpool/selection.rssrc/cli/subcommands/chain_cmd.rssrc/message_pool/msgpool/msg_pool.rssrc/chain_sync/chain_follower.rs
📚 Learning: 2025-08-25T13:35:24.230Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5969
File: src/tool/subcommands/snapshot_cmd.rs:412-412
Timestamp: 2025-08-25T13:35:24.230Z
Learning: In src/tool/subcommands/snapshot_cmd.rs, the +1 in `last_epoch = ts.epoch() - epochs as i64 + 1` fixes an off-by-1 bug where specifying --check-stateroots=N would validate N+1 epochs instead of N epochs, causing out-of-bounds errors when the snapshot contains only N recent state roots.
Applied to files:
src/tool/subcommands/state_compute_cmd.rssrc/tool/subcommands/snapshot_cmd.rssrc/cli/subcommands/chain_cmd.rssrc/chain/store/index.rssrc/tool/subcommands/archive_cmd.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/tool/subcommands/benchmark_cmd.rssrc/interpreter/fvm4.rssrc/tool/subcommands/snapshot_cmd.rssrc/interpreter/fvm3.rssrc/interpreter/fvm2.rssrc/chain/store/index.rssrc/tool/subcommands/archive_cmd.rs
📚 Learning: 2025-08-08T12:10:45.218Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5867
File: src/ipld/util.rs:553-558
Timestamp: 2025-08-08T12:10:45.218Z
Learning: Forest project targets Rust stable >=1.89; features stabilized in 1.88 like let-chains are acceptable in this codebase.
Applied to files:
src/interpreter/fvm4.rssrc/rpc/methods/sync.rssrc/interpreter/fvm3.rssrc/interpreter/fvm2.rs
📚 Learning: 2025-08-19T11:17:35.177Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5946
File: src/rpc/methods/state.rs:1458-1458
Timestamp: 2025-08-19T11:17:35.177Z
Learning: FuturesOrdered in the futures crate has a push_front method that pushes futures to the front of the queue, making them next to be considered for completion, in addition to the standard push/push_back methods.
Applied to files:
src/rpc/methods/state.rs
📚 Learning: 2025-08-04T13:36:22.993Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5835
File: src/chain/tests.rs:58-76
Timestamp: 2025-08-04T13:36:22.993Z
Learning: In the Forest codebase, `Vec<u8>` can be used as an `AsyncWrite` implementation in test contexts. The user confirmed that tests using `&mut Vec<u8>` with `export` and `export_v2` functions compile and pass both locally and on CI.
Applied to files:
src/chain/store/index.rs
📚 Learning: 2025-10-16T11:05:13.586Z
Learnt from: elmattic
Repo: ChainSafe/forest PR: 6166
File: src/auth/mod.rs:127-150
Timestamp: 2025-10-16T11:05:13.586Z
Learning: In Rust 2024, `std::env::set_var` and `std::env::remove_var` are unsafe functions. They require `unsafe` blocks because they are only safe in single-threaded programs (Windows is an exception). When these functions are used in tests, ensure proper serialization with `#[serial]` or similar mechanisms.
Applied to files:
src/chain/store/index.rs
📚 Learning: 2025-07-17T15:21:40.753Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5841
File: src/utils/get_size/mod.rs:10-10
Timestamp: 2025-07-17T15:21:40.753Z
Learning: The get_size2 crate's GetSize trait provides default implementations: get_stack_size() uses std::mem::size_of, get_heap_size() returns 0, and get_size() returns their sum. An empty impl like `impl GetSize for MyType {}` is valid and uses these defaults, making it suitable for simple wrapper types that don't allocate heap memory.
Applied to files:
src/blocks/vrf_proof.rs
🧬 Code graph analysis (19)
src/state_manager/tests.rs (3)
src/rpc/mod.rs (1)
chain_store(474-476)src/state_manager/mod.rs (1)
chain_store(348-350)src/daemon/context.rs (1)
chain_store(69-71)
src/interpreter/fvm4.rs (5)
src/chain/store/chain_store.rs (1)
heaviest_tipset(221-229)src/state_manager/mod.rs (1)
heaviest_tipset(208-210)src/db/car/plain.rs (1)
heaviest_tipset(208-210)src/db/car/forest.rs (1)
heaviest_tipset(186-188)src/db/car/many.rs (1)
heaviest_tipset(141-147)
src/tool/subcommands/snapshot_cmd.rs (4)
src/rpc/types/tsk_impl.rs (1)
ts(31-31)src/rpc/types/tests.rs (1)
ts(36-36)src/blocks/tipset.rs (2)
epoch(303-305)epoch(518-520)src/networks/mod.rs (1)
epoch(503-516)
src/rpc/methods/sync.rs (1)
src/rpc/types/tsk_impl.rs (1)
ts(31-31)
src/cli/subcommands/info_cmd.rs (1)
src/blocks/tipset.rs (2)
new(239-257)new(467-486)
src/interpreter/fvm3.rs (5)
src/chain/store/chain_store.rs (1)
heaviest_tipset(221-229)src/state_manager/mod.rs (1)
heaviest_tipset(208-210)src/db/car/plain.rs (1)
heaviest_tipset(208-210)src/db/car/forest.rs (1)
heaviest_tipset(186-188)src/db/car/many.rs (1)
heaviest_tipset(141-147)
src/chain_sync/network_context.rs (1)
src/blocks/tipset.rs (1)
tsk(262-266)
src/message_pool/msgpool/test_provider.rs (1)
src/message_pool/msgpool/provider.rs (4)
get_heaviest_tipset(36-36)get_heaviest_tipset(88-90)load_tipset(50-50)load_tipset(114-120)
src/fil_cns/validation.rs (2)
src/chain_sync/tipset_syncer.rs (1)
block_timestamp_checks(514-528)src/blocks/block.rs (1)
header(30-32)
src/message_pool/msgpool/mod.rs (1)
src/message_pool/msgpool/msg_pool.rs (2)
new(72-77)new(465-583)
src/message_pool/msgpool/msg_pool.rs (1)
src/blocks/tipset.rs (2)
new(239-257)new(467-486)
src/state_manager/chain_rand.rs (2)
src/blocks/chain4u.rs (1)
tipset(183-185)src/blocks/tipset.rs (2)
epoch(303-305)epoch(518-520)
src/rpc/methods/chain.rs (2)
src/blocks/tipset.rs (2)
into_lotus_json(655-657)from_lotus_json(659-661)src/lotus_json/arc.rs (2)
into_lotus_json(18-20)from_lotus_json(22-24)
src/message_pool/msgpool/provider.rs (2)
src/message_pool/msgpool/test_provider.rs (2)
get_heaviest_tipset(126-128)load_tipset(189-197)src/chain/store/index.rs (1)
load_tipset(54-72)
src/chain/store/index.rs (1)
src/blocks/tipset.rs (11)
tsk(262-266)load(261-269)from(104-109)from(159-161)from(165-167)from(171-176)from(180-185)from(204-211)from(452-457)chain(385-392)blocks(492-494)
src/chain/store/chain_store.rs (6)
src/state_manager/mod.rs (1)
heaviest_tipset(208-210)src/db/car/plain.rs (1)
heaviest_tipset(208-210)src/db/car/forest.rs (1)
heaviest_tipset(186-188)src/fil_cns/weight.rs (1)
fil_cns(23-57)src/blocks/tipset.rs (2)
weight(347-349)weight(522-524)src/fil_cns/mod.rs (1)
weight(91-96)
src/chain_sync/chain_follower.rs (1)
src/blocks/tipset.rs (4)
key(330-333)key(505-508)parents(339-341)parents(514-516)
src/rpc/methods/eth.rs (2)
src/blocks/tipset.rs (1)
chain(385-392)src/chain/store/index.rs (1)
chain(191-198)
src/state_manager/mod.rs (4)
src/chain/store/chain_store.rs (1)
heaviest_tipset(221-229)src/db/car/plain.rs (1)
heaviest_tipset(208-210)src/db/car/forest.rs (1)
heaviest_tipset(186-188)src/blocks/tipset.rs (7)
from(104-109)from(159-161)from(165-167)from(171-176)from(180-185)from(204-211)from(452-457)
⏰ 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). (6)
- GitHub Check: tests-release
- GitHub Check: Build forest binaries on Linux AMD64
- GitHub Check: tests
- GitHub Check: cargo-publish-dry-run
- GitHub Check: All lint checks
- GitHub Check: Build Ubuntu
LesnyRumcajs
left a 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.
LGTM!
Summary of changes
Changes introduced in this pull request:
TipsetandFullTipsetArcwrapper ofTipsetandFullTipsetReference issue to close (if applicable)
Closes
Other information and links
Change checklist
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.