mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 19:00:46 +00:00
miner: add OpenTelemetry tracing spans to block building
Add tracing instrumentation to the payload building pipeline so operators can identify where time is spent during block construction. This adds four spans: - miner.buildPayload (root span per build iteration) - miner.prepareWork (header + state setup) - miner.fillTransactions (transaction selection + execution) - miner.finalizeAndAssemble (block finalization) The root span is created via StartRootSpan since the background build goroutine has no parent RPC context. Context is threaded through generateWork → prepareWork/fillTransactions to enable child spans. This complements the existing RPC-level tracing (jsonrpc.engine/getPayloadV* server spans with rpc.encodeJSONResponse children) by providing visibility into the block building phase itself. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d318e8eba9
commit
42fe582e44
4 changed files with 39 additions and 9 deletions
|
|
@ -88,6 +88,13 @@ func StartServerSpan(ctx context.Context, tracer trace.Tracer, rpc RPCInfo, othe
|
||||||
return ctx, end
|
return ctx, end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartRootSpan creates a SpanKind=INTERNAL root span that does not require
|
||||||
|
// a parent span in the context. Use this for background operations that are
|
||||||
|
// not triggered by an incoming RPC request.
|
||||||
|
func StartRootSpan(spanName string, attributes ...Attribute) (context.Context, trace.Span, func(*error)) {
|
||||||
|
return startSpan(context.Background(), otel.Tracer(""), trace.SpanKindInternal, spanName, attributes...)
|
||||||
|
}
|
||||||
|
|
||||||
// startSpan creates a span with the given kind.
|
// startSpan creates a span with the given kind.
|
||||||
func startSpan(ctx context.Context, tracer trace.Tracer, kind trace.SpanKind, spanName string, attributes ...Attribute) (context.Context, trace.Span, func(*error)) {
|
func startSpan(ctx context.Context, tracer trace.Tracer, kind trace.SpanKind, spanName string, attributes ...Attribute) (context.Context, trace.Span, func(*error)) {
|
||||||
ctx, span := tracer.Start(ctx, spanName, trace.WithSpanKind(kind))
|
ctx, span := tracer.Start(ctx, spanName, trace.WithSpanKind(kind))
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
package miner
|
package miner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -156,7 +157,7 @@ func (miner *Miner) getPending() *newPayloadResult {
|
||||||
if miner.chainConfig.IsShanghai(new(big.Int).Add(header.Number, big.NewInt(1)), timestamp) {
|
if miner.chainConfig.IsShanghai(new(big.Int).Add(header.Number, big.NewInt(1)), timestamp) {
|
||||||
withdrawal = []*types.Withdrawal{}
|
withdrawal = []*types.Withdrawal{}
|
||||||
}
|
}
|
||||||
ret := miner.generateWork(&generateParams{
|
ret := miner.generateWork(context.Background(), &generateParams{
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
forceTime: false,
|
forceTime: false,
|
||||||
parentHash: header.Hash(),
|
parentHash: header.Hash(),
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package miner
|
package miner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -28,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/stateless"
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/telemetry"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
|
@ -225,7 +227,7 @@ func (miner *Miner) buildPayload(args *BuildPayloadArgs, witness bool) (*Payload
|
||||||
slotNum: args.SlotNum,
|
slotNum: args.SlotNum,
|
||||||
noTxs: true,
|
noTxs: true,
|
||||||
}
|
}
|
||||||
empty := miner.generateWork(emptyParams, witness)
|
empty := miner.generateWork(context.Background(), emptyParams, witness)
|
||||||
if empty.err != nil {
|
if empty.err != nil {
|
||||||
return nil, empty.err
|
return nil, empty.err
|
||||||
}
|
}
|
||||||
|
|
@ -261,11 +263,17 @@ func (miner *Miner) buildPayload(args *BuildPayloadArgs, witness bool) (*Payload
|
||||||
select {
|
select {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
r := miner.generateWork(fullParams, witness)
|
ctx, _, spanEnd := telemetry.StartRootSpan("miner.buildPayload",
|
||||||
|
telemetry.Int64Attribute("block.number", int64(args.Timestamp)),
|
||||||
|
telemetry.StringAttribute("payload.id", args.Id().String()),
|
||||||
|
)
|
||||||
|
r := miner.generateWork(ctx, fullParams, witness)
|
||||||
if r.err == nil {
|
if r.err == nil {
|
||||||
payload.update(r, time.Since(start))
|
payload.update(r, time.Since(start))
|
||||||
|
spanEnd(nil)
|
||||||
} else {
|
} else {
|
||||||
log.Info("Error while generating work", "id", payload.id, "err", r.err)
|
log.Info("Error while generating work", "id", payload.id, "err", r.err)
|
||||||
|
spanEnd(&r.err)
|
||||||
}
|
}
|
||||||
timer.Reset(max(0, miner.config.Recommit-time.Since(start)))
|
timer.Reset(max(0, miner.config.Recommit-time.Since(start)))
|
||||||
case <-payload.stop:
|
case <-payload.stop:
|
||||||
|
|
@ -296,7 +304,7 @@ func (miner *Miner) BuildTestingPayload(args *BuildPayloadArgs, transactions []*
|
||||||
overrideExtraData: extraData,
|
overrideExtraData: extraData,
|
||||||
overrideTxs: transactions,
|
overrideTxs: transactions,
|
||||||
}
|
}
|
||||||
res := miner.generateWork(fullParams, false)
|
res := miner.generateWork(context.Background(), fullParams, false)
|
||||||
if res.err != nil {
|
if res.err != nil {
|
||||||
return nil, res.err
|
return nil, res.err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package miner
|
package miner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -32,6 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/telemetry"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
@ -120,8 +122,8 @@ type generateParams struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateWork generates a sealing block based on the given parameters.
|
// generateWork generates a sealing block based on the given parameters.
|
||||||
func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPayloadResult {
|
func (miner *Miner) generateWork(ctx context.Context, genParam *generateParams, witness bool) *newPayloadResult {
|
||||||
work, err := miner.prepareWork(genParam, witness)
|
work, err := miner.prepareWork(ctx, genParam, witness)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &newPayloadResult{err: err}
|
return &newPayloadResult{err: err}
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +159,7 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay
|
||||||
})
|
})
|
||||||
defer timer.Stop()
|
defer timer.Stop()
|
||||||
|
|
||||||
err := miner.fillTransactions(interrupt, work)
|
err := miner.fillTransactions(ctx, interrupt, work)
|
||||||
if errors.Is(err, errBlockInterruptedByTimeout) {
|
if errors.Is(err, errBlockInterruptedByTimeout) {
|
||||||
log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(miner.config.Recommit))
|
log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(miner.config.Recommit))
|
||||||
}
|
}
|
||||||
|
|
@ -192,10 +194,16 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay
|
||||||
work.header.RequestsHash = &reqHash
|
work.header.RequestsHash = &reqHash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var finalizeErr error
|
||||||
|
_, _, spanEnd := telemetry.StartSpan(ctx, "miner.finalizeAndAssemble")
|
||||||
block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts)
|
block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
finalizeErr = err
|
||||||
|
spanEnd(&finalizeErr)
|
||||||
return &newPayloadResult{err: err}
|
return &newPayloadResult{err: err}
|
||||||
}
|
}
|
||||||
|
spanEnd(&finalizeErr)
|
||||||
|
|
||||||
return &newPayloadResult{
|
return &newPayloadResult{
|
||||||
block: block,
|
block: block,
|
||||||
fees: totalFees(block, work.receipts),
|
fees: totalFees(block, work.receipts),
|
||||||
|
|
@ -210,7 +218,10 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay
|
||||||
// prepareWork constructs the sealing task according to the given parameters,
|
// prepareWork constructs the sealing task according to the given parameters,
|
||||||
// either based on the last chain head or specified parent. In this function
|
// either based on the last chain head or specified parent. In this function
|
||||||
// the pending transactions are not filled yet, only the empty task returned.
|
// the pending transactions are not filled yet, only the empty task returned.
|
||||||
func (miner *Miner) prepareWork(genParams *generateParams, witness bool) (*environment, error) {
|
func (miner *Miner) prepareWork(ctx context.Context, genParams *generateParams, witness bool) (_ *environment, err error) {
|
||||||
|
_, _, spanEnd := telemetry.StartSpan(ctx, "miner.prepareWork")
|
||||||
|
defer spanEnd(&err)
|
||||||
|
|
||||||
miner.confMu.RLock()
|
miner.confMu.RLock()
|
||||||
defer miner.confMu.RUnlock()
|
defer miner.confMu.RUnlock()
|
||||||
|
|
||||||
|
|
@ -502,7 +513,10 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran
|
||||||
// fillTransactions retrieves the pending transactions from the txpool and fills them
|
// fillTransactions retrieves the pending transactions from the txpool and fills them
|
||||||
// into the given sealing block. The transaction selection and ordering strategy can
|
// into the given sealing block. The transaction selection and ordering strategy can
|
||||||
// be customized with the plugin in the future.
|
// be customized with the plugin in the future.
|
||||||
func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment) error {
|
func (miner *Miner) fillTransactions(ctx context.Context, interrupt *atomic.Int32, env *environment) (err error) {
|
||||||
|
_, _, spanEnd := telemetry.StartSpan(ctx, "miner.fillTransactions")
|
||||||
|
defer spanEnd(&err)
|
||||||
|
|
||||||
miner.confMu.RLock()
|
miner.confMu.RLock()
|
||||||
tip := miner.config.GasPrice
|
tip := miner.config.GasPrice
|
||||||
prio := miner.prio
|
prio := miner.prio
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue