Skip to content

feat: adding additional fine grained spans#142

Open
chatton wants to merge 1 commit intomainfrom
cian/add-tier1-tracing-spans
Open

feat: adding additional fine grained spans#142
chatton wants to merge 1 commit intomainfrom
cian/add-tier1-tracing-spans

Conversation

@chatton
Copy link
Contributor

@chatton chatton commented Feb 25, 2026

Description

Follow up to #137

Adding additional spans.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Performance improvement
  • Refactoring

Related Issues

Fixes #(issue)

Checklist

  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Testing

Additional Notes

Summary by CodeRabbit

Release Notes

  • Chores
    • Enhanced internal system observability with timing measurements and monitoring instrumentation across node validation and payload processing workflows.
    • Improved diagnostic visibility into key operations including transaction validation, payload building, and block processing.
    • Expanded test suite to verify monitoring instrumentation accuracy and completeness.

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

Adds comprehensive tracing instrumentation across node payload building, validation, and transaction processing workflows. Introduces span attributes for timing metrics (duration_ms), transaction counts, and payload identifiers. Includes span field validation tests to ensure instrumentation correctness.

Changes

Cohort / File(s) Summary
Payload Building Instrumentation
crates/node/src/attributes.rs, crates/node/src/payload_service.rs
Adds tracing spans with timing measurements to payload construction. Decorates try_new and build_empty_payload with instrumentation attributes recording parent_hash, raw_tx_count, gas_limit, payload_id, and duration_ms. Includes unit tests validating span field presence.
Validation Instrumentation
crates/node/src/txpool.rs, crates/node/src/validator.rs
Instruments validation flows with span tracking. Extracts validate_evnode_inner in txpool to separate core logic from instrumentation. Adds #[instrument] attributes to parse_evolve_payload and validate_evnode recording tx_hash, is_evnode, tx_count, block_number, and duration_ms metrics. Includes span field validation tests.
Import Consistency
crates/node/src/builder.rs
Updates imports to include Span from tracing and replaces fully-qualified tracing::Span::current() calls with locally-imported Span::current() for consistency.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

Through spans of light, the traces flow,
Timing secrets all aglow,
Payloads dance with metrics bright,
Validation hops through night, 🐰
Observability takes flight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description covers the basic requirement of linking to a related issue (#137) and marking the change type, but lacks detail on what was changed and provides no testing information despite the template requiring it. Expand the description to explain which functions received spans, what fields are being instrumented, and provide details on testing performed to validate the changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: adding additional fine grained spans' clearly and concisely summarizes the main change, which is adding fine-grained tracing instrumentation across multiple files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cian/add-tier1-tracing-spans

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@claude
Copy link

claude bot commented Feb 25, 2026

Claude finished @chatton's task —— View job


Code Review: Adding Fine-Grained Tracing Spans ✅

This PR successfully adds fine-grained tracing spans across key components in the ev-reth codebase. Here's my comprehensive review:

📋 Summary of Changes

The PR adds comprehensive tracing instrumentation to 5 core files:

  • attributes.rs: Added #[instrument] to try_new() with timing and field tracking
  • builder.rs: Enhanced build_payload() and transaction execution spans
  • payload_service.rs: Added spans to try_build() and build_empty_payload()
  • txpool.rs: Added spans to transaction validation methods
  • validator.rs: Enhanced payload validation with detailed tracing

✅ Code Quality & Best Practices

Excellent tracing implementation:

  • ✅ Consistent use of #[instrument] macro across all methods
  • ✅ Proper field declarations with tracing::field::Empty for later recording
  • ✅ Duration tracking using std::time::Instant and record() calls
  • ✅ Meaningful field names that aid debugging (tx_count, block_hash, duration_ms)
  • ✅ Spans properly entered with .entered() for scoped operations

Code organization:

  • ✅ No changes to core business logic - purely additive tracing
  • ✅ Consistent patterns across all files
  • ✅ Proper imports added (tracing::{instrument, Span})

🔍 Technical Analysis

Span Structure (attributes.rs:73-78):

#[instrument(skip(parent, attributes, _version), fields(
    parent_hash = %parent,
    raw_tx_count = attributes.transactions.as_ref().map_or(0, |t| t.len()),
    gas_limit = ?attributes.gas_limit,
    duration_ms = tracing::field::Empty,
))]
  • ✅ Good use of % for Display formatting and ? for Debug formatting
  • ✅ Safe handling of Option with map_or

Transaction Execution Tracing (builder.rs:152-158):

let _span = debug_span!("execute_tx",
    index = i,
    hash = %tx.tx_hash(),
    nonce = tx.nonce(),
    gas_limit = tx.gas_limit(),
).entered();
  • ✅ Individual transaction spans with relevant transaction details
  • ✅ Proper use of debug_span! for lower-level operations

Validation Tracing (txpool.rs:397-415):

  • ✅ Comprehensive validation spans with transaction metadata
  • ✅ Proper error handling preserved while adding observability

🚀 Performance Considerations

Low Performance Impact:

  • ✅ Tracing overhead is minimal when not enabled
  • ✅ Field recording happens only when spans are active
  • ✅ Duration tracking uses efficient Instant::elapsed()
  • ✅ No heap allocations in hot paths for tracing

Timing Precision:

  • ✅ Millisecond precision is appropriate for the operations being measured
  • _start variables properly scoped to method boundaries

🔐 Security Assessment

No Security Concerns:

  • ✅ No sensitive data exposed in trace fields
  • ✅ Transaction hashes and addresses are safe to log
  • ✅ No credentials or private keys in traced fields
  • ✅ Gas limits and counts are operational data, not sensitive

🧪 Test Coverage

Comprehensive Test Coverage:

  • ✅ All modified functions have corresponding test methods
  • ✅ Tests use SpanCollector utility for verification
  • ✅ Tests validate presence of expected fields in spans
  • ✅ Examples:
    • try_new_span_has_expected_fields() (attributes.rs:191)
    • build_payload_span_has_expected_fields() (builder.rs:247)
    • validate_transaction_span_has_expected_fields() (txpool.rs:789)

Test Quality:

  • ✅ Tests don't depend on actual operation success
  • ✅ Focus on instrumentation verification, not business logic
  • ✅ Proper cleanup with _guard usage

📊 Specific Technical Points

  1. Field Selection: All traced fields provide valuable debugging context without being excessive
  2. Span Hierarchy: Proper parent-child relationships maintained (e.g., execute_tx spans within build_payload)
  3. Error Handling: Tracing doesn't interfere with existing error propagation
  4. Integration: Seamless integration with existing codebase patterns

📈 Benefits

This tracing implementation will provide:

  • Performance Monitoring: Duration tracking for payload building, validation, and transaction processing
  • Debugging Support: Transaction-level visibility into execution flow
  • Operational Insights: Block building metrics and validation timing
  • Distributed Tracing: Integration with tracing infrastructure for complex debugging

✅ Recommendation

APPROVE - This is a high-quality implementation of comprehensive tracing that:

  • Follows Rust and tracing best practices
  • Has no performance or security concerns
  • Includes thorough test coverage
  • Maintains clean separation from business logic
  • Will significantly improve observability for the ev-reth system

The implementation is production-ready and will be valuable for monitoring and debugging the specialized Ethereum execution client.


@chatton chatton changed the title [FEATURE]: adding additional fine grained spans feat: adding additional fine grained spans Feb 25, 2026
@chatton chatton mentioned this pull request Feb 25, 2026
5 tasks
@chatton chatton marked this pull request as ready for review February 25, 2026 09:09
@chatton chatton requested a review from a team as a code owner February 25, 2026 09:09
Copy link

@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.

🧹 Nitpick comments (2)
crates/node/src/payload_service.rs (1)

396-476: Consider extracting shared test setup into a helper.

The genesis/provider/config/builder setup (lines 401–446) is nearly identical to the try_build_span_has_expected_fields test above (lines 312–357), and also mirrors setup in builder.rs and validator.rs tests. This boilerplate is copy-pasted across at least 6 test functions in this PR.

A shared test helper (e.g., in test_utils.rs) returning (EvolveEnginePayloadBuilder<MockEthProvider>, Header, B256) would reduce duplication and make future test additions easier.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/node/src/payload_service.rs` around lines 396 - 476, Extract the
repeated genesis/provider/config/builder setup into a shared test helper (e.g.,
in a new test_utils.rs) that returns the needed items like
(EvolveEnginePayloadBuilder<MockEthProvider>, Header, B256, ChainSpec or config)
and update tests such as build_empty_payload_span_has_expected_fields and
try_build_span_has_expected_fields to call that helper instead of duplicating
lines ~401–446; locate the setup by looking for MockEthProvider,
EvolveEnginePayloadBuilder, RpcPayloadAttributes, SealedHeader and PayloadConfig
usage, move the creation of
genesis_hash/genesis_header/provider/config/evm_config/evolve_builder into the
helper, and have tests use the returned tuple to construct their specific Attrs
or PayloadConfig before calling build_empty_payload or other builder methods.
crates/node/src/builder.rs (1)

65-76: duration_ms won't be recorded on error paths.

If build_payload returns early via any of the ? operators (lines 78–184), duration_ms stays Empty on the span. This means error cases won't have timing data, which can be useful for diagnosing slow failures. Consider recording duration_ms unconditionally, e.g., with a drop guard or by restructuring to always record before returning.

This pattern is repeated across all files in this PR, so noting it here as the primary location.

Example: use a scope guard to always record duration

One approach is a small helper or a defer-style pattern:

     pub async fn build_payload(
         &self,
         attributes: EvolvePayloadAttributes,
     ) -> Result<SealedBlock<ev_primitives::Block>, PayloadBuilderError> {
         let _start = std::time::Instant::now();
+        // Record duration on all exit paths (success and error).
+        let _record_duration = scopeguard::guard((), |_| {
+            Span::current().record("duration_ms", _start.elapsed().as_millis() as u64);
+        });

Alternatively, the inner/outer pattern already used in validate_evnode / validate_evnode_inner in txpool.rs would work here too.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/node/src/builder.rs` around lines 65 - 76, The span field duration_ms
(declared in the #[instrument(...)] on build_payload) is left Empty on
early-return/error paths because duration is only computed at function end; add
an unconditional recorder (e.g., a small Drop guard created right after let
_start = Instant::now() or restructure into inner/outer functions) that computes
Instant::elapsed() and calls tracing::Span::current().record("duration_ms",
&elapsed.as_millis()) so duration_ms is always set even when build_payload (or
any early-return using ?) exits early; apply the same pattern wherever this
#[instrument(..., duration_ms = tracing::field::Empty, ...)] pattern is used.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/node/src/builder.rs`:
- Around line 65-76: The span field duration_ms (declared in the
#[instrument(...)] on build_payload) is left Empty on early-return/error paths
because duration is only computed at function end; add an unconditional recorder
(e.g., a small Drop guard created right after let _start = Instant::now() or
restructure into inner/outer functions) that computes Instant::elapsed() and
calls tracing::Span::current().record("duration_ms", &elapsed.as_millis()) so
duration_ms is always set even when build_payload (or any early-return using ?)
exits early; apply the same pattern wherever this #[instrument(..., duration_ms
= tracing::field::Empty, ...)] pattern is used.

In `@crates/node/src/payload_service.rs`:
- Around line 396-476: Extract the repeated genesis/provider/config/builder
setup into a shared test helper (e.g., in a new test_utils.rs) that returns the
needed items like (EvolveEnginePayloadBuilder<MockEthProvider>, Header, B256,
ChainSpec or config) and update tests such as
build_empty_payload_span_has_expected_fields and
try_build_span_has_expected_fields to call that helper instead of duplicating
lines ~401–446; locate the setup by looking for MockEthProvider,
EvolveEnginePayloadBuilder, RpcPayloadAttributes, SealedHeader and PayloadConfig
usage, move the creation of
genesis_hash/genesis_header/provider/config/evm_config/evolve_builder into the
helper, and have tests use the returned tuple to construct their specific Attrs
or PayloadConfig before calling build_empty_payload or other builder methods.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ecfefef and c840315.

📒 Files selected for processing (5)
  • crates/node/src/attributes.rs
  • crates/node/src/builder.rs
  • crates/node/src/payload_service.rs
  • crates/node/src/txpool.rs
  • crates/node/src/validator.rs

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant