eth_call metrics

This commit is contained in:
pragmaxim
2026-02-13 08:36:19 +01:00
parent f37e1e6706
commit 5880cd2129
4 changed files with 69 additions and 0 deletions

View File

@@ -154,6 +154,10 @@ func init() {
BlockChainFactories["Base Archive"] = base.NewBaseRPC
}
type metricsSetter interface {
SetMetrics(*common.Metrics)
}
// NewBlockChain creates bchain.BlockChain and bchain.Mempool for the coin passed by the parameter coin
func NewBlockChain(coin string, configfile string, pushHandler func(bchain.NotificationType), metrics *common.Metrics) (bchain.BlockChain, bchain.Mempool, error) {
data, err := os.ReadFile(configfile)
@@ -173,6 +177,9 @@ func NewBlockChain(coin string, configfile string, pushHandler func(bchain.Notif
if err != nil {
return nil, nil, err
}
if withMetrics, ok := bc.(metricsSetter); ok {
withMetrics.SetMetrics(metrics)
}
err = bc.Initialize()
if err != nil {
return nil, nil, err

View File

@@ -290,12 +290,14 @@ func (b *EthereumRPC) EthereumTypeRpcCallAtBlock(data, to, from string, blockNum
args["from"] = from
}
b.observeEthCall("single", 1)
ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
defer cancel()
var r string
blockArg := bchain.ToBlockNumArg(blockNumber)
err := b.RPC.CallContext(ctx, &r, "eth_call", args, blockArg)
if err != nil {
b.observeEthCallError("single", "rpc")
return "", err
}
return r, nil
@@ -373,6 +375,7 @@ func (b *EthereumRPC) EthereumTypeGetErc20ContractBalanceAtBlock(addrDesc, contr
}
r := parseSimpleNumericProperty(data)
if r == nil {
b.observeEthCallError("single", "invalid")
return nil, errors.New("Invalid balance")
}
return r, nil
@@ -445,14 +448,18 @@ func (b *EthereumRPC) erc20BalancesBatchAtBlock(batcher batchCaller, callData st
Result: &results[i],
}
}
b.observeEthCall("batch", len(contractDescs))
b.observeEthCallBatch(len(contractDescs))
ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
defer cancel()
if err := batcher.BatchCallContext(ctx, batch); err != nil {
b.observeEthCallError("batch", "rpc")
return nil, err
}
balances := make([]*big.Int, len(contractDescs))
for i := range batch {
if batch[i].Error != nil {
b.observeEthCallError("batch", "elem")
glog.Warningf("erc20 batch eth_call failed for %s: %v", hexutil.Encode(contractDescs[i]), batch[i].Error)
// In case of batch failure, retry missing/failed elements as single calls.
data, err := b.EthereumTypeRpcCallAtBlock(callData, hexutil.Encode(contractDescs[i]), "", blockNumber)
@@ -462,6 +469,7 @@ func (b *EthereumRPC) erc20BalancesBatchAtBlock(batcher batchCaller, callData st
}
balances[i] = parseSimpleNumericProperty(data)
if balances[i] == nil {
b.observeEthCallError("single", "invalid")
glog.Warningf("erc20 single eth_call invalid result for %s: %q", hexutil.Encode(contractDescs[i]), data)
}
continue
@@ -469,6 +477,7 @@ func (b *EthereumRPC) erc20BalancesBatchAtBlock(batcher batchCaller, callData st
// Leave nil on parse failures so callers can retry per contract if needed.
balances[i] = parseSimpleNumericProperty(results[i])
if balances[i] == nil {
b.observeEthCallError("batch", "invalid")
glog.Warningf("erc20 batch eth_call invalid result for %s: %q", hexutil.Encode(contractDescs[i]), results[i])
}
}

View File

@@ -92,6 +92,7 @@ type EthereumRPC struct {
NewTx bchain.EVMNewTxSubscriber
newTxSubscription bchain.EVMClientSubscription
ChainConfig *Configuration
metrics *common.Metrics
supportedStakingPools []string
stakingPoolNames []string
stakingPoolContracts []string
@@ -161,6 +162,31 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification
return s, nil
}
func (b *EthereumRPC) SetMetrics(metrics *common.Metrics) {
b.metrics = metrics
}
func (b *EthereumRPC) observeEthCall(mode string, count int) {
if b.metrics == nil || count <= 0 {
return
}
b.metrics.EthCallRequests.With(common.Labels{"mode": mode}).Add(float64(count))
}
func (b *EthereumRPC) observeEthCallError(mode, errType string) {
if b.metrics == nil {
return
}
b.metrics.EthCallErrors.With(common.Labels{"mode": mode, "type": errType}).Inc()
}
func (b *EthereumRPC) observeEthCallBatch(size int) {
if b.metrics == nil || size <= 0 {
return
}
b.metrics.EthCallBatchSize.Observe(float64(size))
}
// EnsureSameRPCHost validates that both RPC URLs point to the same host.
func EnsureSameRPCHost(httpURL, wsURL string) error {
if httpURL == "" || wsURL == "" {

View File

@@ -20,6 +20,9 @@ type Metrics struct {
MempoolResyncDuration prometheus.Histogram
TxCacheEfficiency *prometheus.CounterVec
RPCLatency *prometheus.HistogramVec
EthCallRequests *prometheus.CounterVec
EthCallErrors *prometheus.CounterVec
EthCallBatchSize prometheus.Histogram
IndexResyncErrors *prometheus.CounterVec
IndexDBSize prometheus.Gauge
ExplorerViews *prometheus.CounterVec
@@ -149,6 +152,30 @@ func GetMetrics(coin string) (*Metrics, error) {
},
[]string{"method", "error"},
)
metrics.EthCallRequests = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "blockbook_eth_call_requests",
Help: "Total number of eth_call requests by mode",
ConstLabels: Labels{"coin": coin},
},
[]string{"mode"},
)
metrics.EthCallErrors = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "blockbook_eth_call_errors",
Help: "Total number of eth_call errors by mode and type",
ConstLabels: Labels{"coin": coin},
},
[]string{"mode", "type"},
)
metrics.EthCallBatchSize = prometheus.NewHistogram(
prometheus.HistogramOpts{
Name: "blockbook_eth_call_batch_size",
Help: "Number of eth_call items per batch request",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200},
ConstLabels: Labels{"coin": coin},
},
)
metrics.IndexResyncErrors = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "blockbook_index_resync_errors",