Skip to content

Commit 8a4f6cf

Browse files
authored
Instrument tracing on block production code path (#8017)
Partially #7814. Instrument block production code path. New root spans: * `produce_block_v3` * `produce_block_v2` Example traces: <img width="518" height="432" alt="image" src="https://github.com/user-attachments/assets/a9413d25-501c-49dc-95cc-623db5988981" /> Co-Authored-By: Jimmy Chen <[email protected]>
1 parent 2b22903 commit 8a4f6cf

File tree

7 files changed

+99
-48
lines changed

7 files changed

+99
-48
lines changed

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
14371437
///
14381438
/// Returns `None` when the state is not found in the database or there is an error skipping
14391439
/// to a future state.
1440+
#[instrument(level = "debug", skip_all)]
14401441
pub fn state_at_slot(
14411442
&self,
14421443
slot: Slot,
@@ -4466,6 +4467,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
44664467
}
44674468

44684469
/// If configured, wait for the fork choice run at the start of the slot to complete.
4470+
#[instrument(level = "debug", skip_all)]
44694471
fn wait_for_fork_choice_before_block_production(
44704472
self: &Arc<Self>,
44714473
slot: Slot,
@@ -4528,10 +4530,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
45284530
//
45294531
// Load the parent state from disk.
45304532
let chain = self.clone();
4533+
let span = Span::current();
45314534
let (state, state_root_opt) = self
45324535
.task_executor
45334536
.spawn_blocking_handle(
4534-
move || chain.load_state_for_block_production(slot),
4537+
move || {
4538+
let _guard =
4539+
debug_span!(parent: span, "load_state_for_block_production").entered();
4540+
chain.load_state_for_block_production(slot)
4541+
},
45354542
"load_state_for_block_production",
45364543
)
45374544
.ok_or(BlockProductionError::ShuttingDown)?
@@ -4618,6 +4625,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
46184625
/// Fetch the beacon state to use for producing a block if a 1-slot proposer re-org is viable.
46194626
///
46204627
/// This function will return `None` if proposer re-orgs are disabled.
4628+
#[instrument(skip_all, level = "debug")]
46214629
fn get_state_for_re_org(
46224630
&self,
46234631
slot: Slot,
@@ -5072,6 +5080,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
50725080
/// equal to the root of `state`. Providing this value will serve as an optimization to avoid
50735081
/// performing a tree hash in some scenarios.
50745082
#[allow(clippy::too_many_arguments)]
5083+
#[instrument(level = "debug", skip_all)]
50755084
pub async fn produce_block_on_state(
50765085
self: &Arc<Self>,
50775086
state: BeaconState<T::EthSpec>,
@@ -5091,10 +5100,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
50915100
.graffiti_calculator
50925101
.get_graffiti(validator_graffiti)
50935102
.await;
5103+
let span = Span::current();
50945104
let mut partial_beacon_block = self
50955105
.task_executor
50965106
.spawn_blocking_handle(
50975107
move || {
5108+
let _guard =
5109+
debug_span!(parent: span, "produce_partial_beacon_block").entered();
50985110
chain.produce_partial_beacon_block(
50995111
state,
51005112
state_root_opt,
@@ -5130,10 +5142,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
51305142
match block_contents_type {
51315143
BlockProposalContentsType::Full(block_contents) => {
51325144
let chain = self.clone();
5145+
let span = Span::current();
51335146
let beacon_block_response = self
51345147
.task_executor
51355148
.spawn_blocking_handle(
51365149
move || {
5150+
let _guard =
5151+
debug_span!(parent: span, "complete_partial_beacon_block")
5152+
.entered();
51375153
chain.complete_partial_beacon_block(
51385154
partial_beacon_block,
51395155
Some(block_contents),
@@ -5150,10 +5166,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
51505166
}
51515167
BlockProposalContentsType::Blinded(block_contents) => {
51525168
let chain = self.clone();
5169+
let span = Span::current();
51535170
let beacon_block_response = self
51545171
.task_executor
51555172
.spawn_blocking_handle(
51565173
move || {
5174+
let _guard =
5175+
debug_span!(parent: span, "complete_partial_beacon_block")
5176+
.entered();
51575177
chain.complete_partial_beacon_block(
51585178
partial_beacon_block,
51595179
Some(block_contents),
@@ -5171,10 +5191,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
51715191
}
51725192
} else {
51735193
let chain = self.clone();
5194+
let span = Span::current();
51745195
let beacon_block_response = self
51755196
.task_executor
51765197
.spawn_blocking_handle(
51775198
move || {
5199+
let _guard =
5200+
debug_span!(parent: span, "complete_partial_beacon_block").entered();
51785201
chain.complete_partial_beacon_block(
51795202
partial_beacon_block,
51805203
None,
@@ -5276,51 +5299,54 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
52765299

52775300
// Iterate through the naive aggregation pool and ensure all the attestations from there
52785301
// are included in the operation pool.
5279-
let unagg_import_timer =
5280-
metrics::start_timer(&metrics::BLOCK_PRODUCTION_UNAGGREGATED_TIMES);
5281-
for attestation in self.naive_aggregation_pool.read().iter() {
5282-
let import = |attestation: &Attestation<T::EthSpec>| {
5283-
let attesting_indices =
5284-
get_attesting_indices_from_state(&state, attestation.to_ref())?;
5285-
self.op_pool
5286-
.insert_attestation(attestation.clone(), attesting_indices)
5287-
};
5288-
if let Err(e) = import(attestation) {
5289-
// Don't stop block production if there's an error, just create a log.
5290-
error!(
5291-
reason = ?e,
5292-
"Attestation did not transfer to op pool"
5293-
);
5302+
{
5303+
let _guard = debug_span!("import_naive_aggregation_pool").entered();
5304+
let _unagg_import_timer =
5305+
metrics::start_timer(&metrics::BLOCK_PRODUCTION_UNAGGREGATED_TIMES);
5306+
for attestation in self.naive_aggregation_pool.read().iter() {
5307+
let import = |attestation: &Attestation<T::EthSpec>| {
5308+
let attesting_indices =
5309+
get_attesting_indices_from_state(&state, attestation.to_ref())?;
5310+
self.op_pool
5311+
.insert_attestation(attestation.clone(), attesting_indices)
5312+
};
5313+
if let Err(e) = import(attestation) {
5314+
// Don't stop block production if there's an error, just create a log.
5315+
error!(
5316+
reason = ?e,
5317+
"Attestation did not transfer to op pool"
5318+
);
5319+
}
52945320
}
5295-
}
5296-
drop(unagg_import_timer);
5321+
};
52975322

5298-
let attestation_packing_timer =
5299-
metrics::start_timer(&metrics::BLOCK_PRODUCTION_ATTESTATION_TIMES);
5323+
let mut attestations = {
5324+
let _guard = debug_span!("pack_attestations").entered();
5325+
let _attestation_packing_timer =
5326+
metrics::start_timer(&metrics::BLOCK_PRODUCTION_ATTESTATION_TIMES);
53005327

5301-
// Epoch cache and total balance cache are required for op pool packing.
5302-
state.build_total_active_balance_cache(&self.spec)?;
5303-
initialize_epoch_cache(&mut state, &self.spec)?;
5328+
// Epoch cache and total balance cache are required for op pool packing.
5329+
state.build_total_active_balance_cache(&self.spec)?;
5330+
initialize_epoch_cache(&mut state, &self.spec)?;
53045331

5305-
let mut prev_filter_cache = HashMap::new();
5306-
let prev_attestation_filter = |att: &CompactAttestationRef<T::EthSpec>| {
5307-
self.filter_op_pool_attestation(&mut prev_filter_cache, att, &state)
5308-
};
5309-
let mut curr_filter_cache = HashMap::new();
5310-
let curr_attestation_filter = |att: &CompactAttestationRef<T::EthSpec>| {
5311-
self.filter_op_pool_attestation(&mut curr_filter_cache, att, &state)
5312-
};
5332+
let mut prev_filter_cache = HashMap::new();
5333+
let prev_attestation_filter = |att: &CompactAttestationRef<T::EthSpec>| {
5334+
self.filter_op_pool_attestation(&mut prev_filter_cache, att, &state)
5335+
};
5336+
let mut curr_filter_cache = HashMap::new();
5337+
let curr_attestation_filter = |att: &CompactAttestationRef<T::EthSpec>| {
5338+
self.filter_op_pool_attestation(&mut curr_filter_cache, att, &state)
5339+
};
53135340

5314-
let mut attestations = self
5315-
.op_pool
5316-
.get_attestations(
5317-
&state,
5318-
prev_attestation_filter,
5319-
curr_attestation_filter,
5320-
&self.spec,
5321-
)
5322-
.map_err(BlockProductionError::OpPoolError)?;
5323-
drop(attestation_packing_timer);
5341+
self.op_pool
5342+
.get_attestations(
5343+
&state,
5344+
prev_attestation_filter,
5345+
curr_attestation_filter,
5346+
&self.spec,
5347+
)
5348+
.map_err(BlockProductionError::OpPoolError)?
5349+
};
53245350

53255351
// If paranoid mode is enabled re-check the signatures of every included message.
53265352
// This will be a lot slower but guards against bugs in block production and can be

beacon_node/beacon_chain/src/execution_payload.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use state_processing::per_block_processing::{
2424
};
2525
use std::sync::Arc;
2626
use tokio::task::JoinHandle;
27-
use tracing::{debug, warn};
27+
use tracing::{Instrument, debug, debug_span, warn};
2828
use tree_hash::TreeHash;
2929
use types::payload::BlockProductionVersion;
3030
use types::*;
@@ -403,8 +403,9 @@ pub fn get_execution_payload<T: BeaconChainTypes>(
403403
block_production_version,
404404
)
405405
.await
406-
},
407-
"get_execution_payload",
406+
}
407+
.instrument(debug_span!("prepare_execution_payload")),
408+
"prepare_execution_payload",
408409
)
409410
.ok_or(BlockProductionError::ShuttingDown)?;
410411

@@ -503,6 +504,7 @@ where
503504
},
504505
"prepare_execution_payload_forkchoice_update_params",
505506
)
507+
.instrument(debug_span!("forkchoice_update_params"))
506508
.await
507509
.map_err(|e| BlockProductionError::BeaconChain(Box::new(e)))?;
508510

beacon_node/execution_layer/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use tokio::{
4343
time::sleep,
4444
};
4545
use tokio_stream::wrappers::WatchStream;
46-
use tracing::{debug, error, info, warn};
46+
use tracing::{Instrument, debug, debug_span, error, info, instrument, warn};
4747
use tree_hash::TreeHash;
4848
use types::beacon_block_body::KzgCommitments;
4949
use types::builder_bid::BuilderBid;
@@ -851,6 +851,7 @@ impl<E: EthSpec> ExecutionLayer<E> {
851851
}
852852

853853
/// Returns the fee-recipient address that should be used to build a block
854+
#[instrument(level = "debug", skip_all)]
854855
pub async fn get_suggested_fee_recipient(&self, proposer_index: u64) -> Address {
855856
if let Some(preparation_data_entry) =
856857
self.proposer_preparation_data().await.get(&proposer_index)
@@ -875,6 +876,7 @@ impl<E: EthSpec> ExecutionLayer<E> {
875876
}
876877
}
877878

879+
#[instrument(level = "debug", skip_all)]
878880
pub async fn get_proposer_gas_limit(&self, proposer_index: u64) -> Option<u64> {
879881
self.proposer_preparation_data()
880882
.await
@@ -891,6 +893,7 @@ impl<E: EthSpec> ExecutionLayer<E> {
891893
///
892894
/// The result will be returned from the first node that returns successfully. No more nodes
893895
/// will be contacted.
896+
#[instrument(level = "debug", skip_all)]
894897
pub async fn get_payload(
895898
&self,
896899
payload_parameters: PayloadParameters<'_>,
@@ -996,6 +999,7 @@ impl<E: EthSpec> ExecutionLayer<E> {
996999
timed_future(metrics::GET_BLINDED_PAYLOAD_BUILDER, async {
9971000
builder
9981001
.get_builder_header::<E>(slot, parent_hash, pubkey)
1002+
.instrument(debug_span!("get_builder_header"))
9991003
.await
10001004
}),
10011005
timed_future(metrics::GET_BLINDED_PAYLOAD_LOCAL, async {
@@ -1237,6 +1241,7 @@ impl<E: EthSpec> ExecutionLayer<E> {
12371241
.await
12381242
}
12391243

1244+
#[instrument(level = "debug", skip_all)]
12401245
async fn get_full_payload_with(
12411246
&self,
12421247
payload_parameters: PayloadParameters<'_>,

beacon_node/http_api/src/produce_block.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ use beacon_chain::{
1010
BeaconBlockResponseWrapper, BeaconChain, BeaconChainTypes, ProduceBlockVerification,
1111
};
1212
use eth2::types::{self as api_types, ProduceBlockV3Metadata, SkipRandaoVerification};
13+
use lighthouse_tracing::{SPAN_PRODUCE_BLOCK_V2, SPAN_PRODUCE_BLOCK_V3};
1314
use ssz::Encode;
1415
use std::sync::Arc;
16+
use tracing::instrument;
1517
use types::{payload::BlockProductionVersion, *};
1618
use warp::{
1719
Reply,
@@ -40,6 +42,11 @@ pub fn get_randao_verification(
4042
Ok(randao_verification)
4143
}
4244

45+
#[instrument(
46+
name = SPAN_PRODUCE_BLOCK_V3,
47+
skip_all,
48+
fields(%slot)
49+
)]
4350
pub async fn produce_block_v3<T: BeaconChainTypes>(
4451
accept_header: Option<api_types::Accept>,
4552
chain: Arc<BeaconChain<T>>,
@@ -155,6 +162,11 @@ pub async fn produce_blinded_block_v2<T: BeaconChainTypes>(
155162
build_response_v2(chain, block_response_type, accept_header)
156163
}
157164

165+
#[instrument(
166+
name = SPAN_PRODUCE_BLOCK_V2,
167+
skip_all,
168+
fields(%slot)
169+
)]
158170
pub async fn produce_block_v2<T: BeaconChainTypes>(
159171
accept_header: Option<api_types::Accept>,
160172
chain: Arc<BeaconChain<T>>,

beacon_node/lighthouse_tracing/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
//! TODO: These span identifiers will be used to implement selective tracing export (to be implemented),
44
//! where only the listed root spans and their descendants will be exported to the tracing backend.
55
6-
/// Root span name for publish_block
6+
/// Root span names for block production and publishing
7+
pub const SPAN_PRODUCE_BLOCK_V2: &str = "produce_block_v2";
8+
pub const SPAN_PRODUCE_BLOCK_V3: &str = "produce_block_v3";
79
pub const SPAN_PUBLISH_BLOCK: &str = "publish_block";
810

911
/// Data Availability checker span identifiers
@@ -42,11 +44,14 @@ pub const SPAN_HANDLE_LIGHT_CLIENT_FINALITY_UPDATE: &str = "handle_light_client_
4244
/// Only these spans and their descendants will be processed to reduce noise from
4345
/// uninstrumented code paths. New root spans must be added to this list to be traced.
4446
pub const LH_BN_ROOT_SPAN_NAMES: &[&str] = &[
45-
SPAN_SYNCING_CHAIN,
47+
SPAN_PRODUCE_BLOCK_V2,
48+
SPAN_PRODUCE_BLOCK_V3,
49+
SPAN_PUBLISH_BLOCK,
4650
SPAN_PENDING_COMPONENTS,
4751
SPAN_PROCESS_GOSSIP_DATA_COLUMN,
4852
SPAN_PROCESS_GOSSIP_BLOB,
4953
SPAN_PROCESS_GOSSIP_BLOCK,
54+
SPAN_SYNCING_CHAIN,
5055
SPAN_OUTGOING_RANGE_REQUEST,
5156
SPAN_SINGLE_BLOCK_LOOKUP,
5257
SPAN_PROCESS_RPC_BLOCK,

consensus/fork_choice/src/fork_choice.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ where
523523
///
524524
/// You *must* call `get_head` for the proposal slot prior to calling this function and pass
525525
/// in the result of `get_head` as `canonical_head`.
526+
#[instrument(level = "debug", skip_all)]
526527
pub fn get_proposer_head(
527528
&self,
528529
current_slot: Slot,

consensus/state_processing/src/per_slot_processing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ impl From<ArithError> for Error {
2626
/// If the root of the supplied `state` is known, then it can be passed as `state_root`. If
2727
/// `state_root` is `None`, the root of `state` will be computed using a cached tree hash.
2828
/// Providing the `state_root` makes this function several orders of magnitude faster.
29-
#[instrument(skip_all)]
29+
#[instrument(level = "debug", skip_all)]
3030
pub fn per_slot_processing<E: EthSpec>(
3131
state: &mut BeaconState<E>,
3232
state_root: Option<Hash256>,

0 commit comments

Comments
 (0)