Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

### Metrics

- Renamed Coreth and Subnet-EVM state-sync p2p metrics (`{vmName}` is `evm` for Coreth/C-Chain and `subnetevm` for Subnet-EVM chains):
- Added `avalanche_{vmName}_sae_last_executed_height` and `avalanche_{vmName}_sae_last_settled_height` gauges, exposing SAE async-execution and settlement heights.
- Renamed Coreth and Subnet-EVM state-sync p2p metrics:
- `avalanche_{vmName}_eth_net_tracked_peers` -> `avalanche_{vmName}_sdk_sync_peer_tracker_num_tracked_peers`
- `avalanche_{vmName}_eth_net_responsive_peers` -> `avalanche_{vmName}_sdk_sync_peer_tracker_num_responsive_peers`
- `avalanche_{vmName}_eth_net_average_bandwidth` -> `avalanche_{vmName}_sdk_sync_peer_tracker_average_bandwidth`

NOTE: `{vmName}` is `evm` for Coreth/C-Chain and `subnetevm` for Subnet-EVM chains

### Fixes
- Updated minimum Go version from `v1.25.8` to `v1.25.10`.

Expand Down
7 changes: 7 additions & 0 deletions vms/saevm/sae/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ go_library(
importpath = "github.com/ava-labs/avalanchego/vms/saevm/sae",
visibility = ["//visibility:public"],
deps = [
"//api/metrics",
"//database",
"//ids",
"//network/p2p",
Expand Down Expand Up @@ -110,6 +111,7 @@ go_test(
"//vms/saevm/saedb",
"//vms/saevm/saetest",
"//vms/saevm/saetest/escrow",
"//vms/saevm/saexec",
"//vms/saevm/txgossip/txgossiptest",
"//vms/saevm/types",
"@com_github_arr4n_shed//testerr",
Expand Down Expand Up @@ -140,6 +142,8 @@ go_test(
"@com_github_google_go_cmp//cmp",
"@com_github_google_go_cmp//cmp/cmpopts",
"@com_github_holiman_uint256//:uint256",
"@com_github_prometheus_client_golang//prometheus",
"@com_github_prometheus_client_model//go",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_uber_go_goleak//:goleak",
Expand Down Expand Up @@ -195,6 +199,7 @@ go_test(
"//vms/saevm/saedb",
"//vms/saevm/saetest",
"//vms/saevm/saetest/escrow",
"//vms/saevm/saexec",
"//vms/saevm/txgossip/txgossiptest",
"//vms/saevm/types",
"//vms/saevm/worstcase",
Expand Down Expand Up @@ -225,6 +230,8 @@ go_test(
"@com_github_google_go_cmp//cmp",
"@com_github_google_go_cmp//cmp/cmpopts",
"@com_github_holiman_uint256//:uint256",
"@com_github_prometheus_client_golang//prometheus",
"@com_github_prometheus_client_model//go",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_uber_go_goleak//:goleak",
Expand Down
1 change: 1 addition & 0 deletions vms/saevm/sae/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func (vm *VM) AcceptBlock(ctx context.Context, b *blocks.Block) error {
if err := s.MarkSettled(&vm.last.settled); err != nil {
return err
}
vm.exec.MarkSettled(s.Height())
}

// I(s ∈ S) above, before I(b ∈ A) before X(b ∈ A)
Expand Down
38 changes: 21 additions & 17 deletions vms/saevm/sae/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/ava-labs/libevm/params"
"github.com/prometheus/client_golang/prometheus"

"github.com/ava-labs/avalanchego/api/metrics"
"github.com/ava-labs/avalanchego/network/p2p"
"github.com/ava-labs/avalanchego/network/p2p/gossip"
"github.com/ava-labs/avalanchego/snow"
Expand Down Expand Up @@ -52,10 +53,10 @@ type VM struct {
Peers *p2p.Peers
ValidatorPeers *p2p.Validators

hooks hook.Points
config Config
snowCtx *snow.Context
metrics *prometheus.Registry
hooks hook.Points
config Config
snowCtx *snow.Context
metricRegistry *prometheus.Registry

db ethdb.Database
xdb saetypes.ExecutionResults
Expand Down Expand Up @@ -123,23 +124,23 @@ func NewVM[T hook.Transaction](
if cfg.Now == nil {
cfg.Now = time.Now
}
reg, err := metrics.MakeAndRegister(snowCtx.Metrics, "sae")
Comment thread
JonathanOppenheimer marked this conversation as resolved.
if err != nil {
return nil, err
}
vm := &VM{
hooks: hooks,
config: cfg,
snowCtx: snowCtx,
metrics: prometheus.NewRegistry(),
db: db,
hooks: hooks,
config: cfg,
snowCtx: snowCtx,
metricRegistry: reg,
db: db,
}
defer func() {
if retErr != nil {
retErr = errors.Join(retErr, vm.close())
}
}()

if err := snowCtx.Metrics.Register("sae", vm.metrics); err != nil {
return nil, err
}

xdb, err := hooks.ExecutionResultsDB(
filepath.Join(snowCtx.ChainDataDir, "sae_execution_results"),
)
Expand Down Expand Up @@ -180,6 +181,7 @@ func NewVM[T hook.Transaction](
cfg.DBConfig,
hooks,
snowCtx.Log,
vm.metricRegistry,
)
if err != nil {
return nil, fmt.Errorf("saexec.New(...): %v", err)
Expand All @@ -201,6 +203,8 @@ func NewVM[T hook.Transaction](
vm.last.settled.Store(lastSettled)
vm.last.accepted.Store(head)
vm.preference.Store(head)
// [saexec.New] already records the initial executed height.
vm.exec.MarkSettled(lastSettled.Height())
}

{ // ========== Mempool ==========
Expand All @@ -214,11 +218,11 @@ func NewVM[T hook.Transaction](
}
vm.toClose = append(vm.toClose, txPool)

metrics, err := bloom.NewMetrics("mempool", vm.metrics)
bloomMetrics, err := bloom.NewMetrics("mempool", vm.metricRegistry)
if err != nil {
return nil, err
}
conf := gossip.BloomSetConfig{Metrics: metrics}
conf := gossip.BloomSetConfig{Metrics: bloomMetrics}
pool, err := txgossip.NewSet(txPool, conf)
if err != nil {
return nil, err
Expand All @@ -239,7 +243,7 @@ func NewVM[T hook.Transaction](
}

{ // ========== P2P Gossip ==========
network, peers, validatorPeers, err := newNetwork(snowCtx, sender, vm.metrics)
network, peers, validatorPeers, err := newNetwork(snowCtx, sender, vm.metricRegistry)
if err != nil {
return nil, fmt.Errorf("newNetwork(...): %v", err)
}
Expand All @@ -253,7 +257,7 @@ func NewVM[T hook.Transaction](
txgossip.Marshaller{},
gossip.SystemConfig{
Log: snowCtx.Log,
Registry: vm.metrics,
Registry: vm.metricRegistry,
Namespace: "gossip",
RequestPeriod: pullGossipPeriod,
},
Expand Down
32 changes: 32 additions & 0 deletions vms/saevm/sae/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"math/big"
"net/http/httptest"
"os"
"slices"
"sync"
"testing"
"time"
Expand All @@ -32,6 +33,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/holiman/uint256"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
Expand All @@ -54,12 +56,14 @@ import (
"github.com/ava-labs/avalanchego/vms/saevm/hook"
"github.com/ava-labs/avalanchego/vms/saevm/hook/hookstest"
"github.com/ava-labs/avalanchego/vms/saevm/saetest"
"github.com/ava-labs/avalanchego/vms/saevm/saexec"
"github.com/ava-labs/avalanchego/vms/saevm/txgossip/txgossiptest"

snowcommon "github.com/ava-labs/avalanchego/snow/engine/common"
saeparams "github.com/ava-labs/avalanchego/vms/saevm/params"
saetypes "github.com/ava-labs/avalanchego/vms/saevm/types"
libevmhookstest "github.com/ava-labs/libevm/libevm/hookstest"
dto "github.com/prometheus/client_model/go"
)

func TestMain(m *testing.M) {
Expand Down Expand Up @@ -1014,6 +1018,34 @@ func TestGossip(t *testing.T) {
requireNotReceiveTx(t, nonValidators[1:], tx.Hash())
}

func TestSettlementMetric(t *testing.T) {
opt, vmTime := withVMTime(t, time.Unix(saeparams.TauSeconds, 0))
ctx, sut := newSUT(t, 1, opt)

executed := sut.runConsensusLoop(t)
require.NoErrorf(t, executed.WaitUntilExecuted(ctx), "%T.WaitUntilExecuted()", executed)

vmTime.advanceToSettle(ctx, t, executed)
settledBy := sut.runConsensusLoop(t)
require.NoErrorf(t, settledBy.WaitUntilExecuted(ctx), "%T.WaitUntilExecuted()", settledBy)
require.Equal(t, float64(executed.Height()), gaugeValue(t, sut.rawVM.metricRegistry, saexec.LastSettledHeightName), "last settled height")
}

// gaugeValue returns the current value of a single-series gauge from `g` by
// name, failing the test if it is missing or has more than one series.
func gaugeValue(t *testing.T, g prometheus.Gatherer, name string) float64 {
t.Helper()
mfs, err := g.Gather()
require.NoError(t, err, "Gather()")
i := slices.IndexFunc(mfs, func(mf *dto.MetricFamily) bool {
return mf.GetName() == name
})
require.GreaterOrEqualf(t, i, 0, "metric %q not found", name)
series := mfs[i].GetMetric()
require.Lenf(t, series, 1, "metric %q series count", name)
return series[0].GetGauge().GetValue()
}

func TestBlockSources(t *testing.T) {
opt, vmTime := withVMTime(t, time.Unix(saeparams.TauSeconds, 0))
ctx, sut := newSUT(t, 1, opt)
Expand Down
4 changes: 4 additions & 0 deletions vms/saevm/saexec/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ go_library(
srcs = [
"context.go",
"execution.go",
"metrics.go",
"receipts.go",
"saexec.go",
"subscription.go",
Expand All @@ -32,6 +33,7 @@ go_library(
"@com_github_ava_labs_libevm//libevm/eventual",
"@com_github_ava_labs_libevm//params",
"@com_github_holiman_uint256//:uint256",
"@com_github_prometheus_client_golang//prometheus",
"@org_uber_go_zap//:zap",
],
)
Expand Down Expand Up @@ -71,6 +73,8 @@ go_test(
"@com_github_google_go_cmp//cmp",
"@com_github_google_go_cmp//cmp/cmpopts",
"@com_github_holiman_uint256//:uint256",
"@com_github_prometheus_client_golang//prometheus",
"@com_github_prometheus_client_golang//prometheus/testutil",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_uber_go_goleak//:goleak",
Expand Down
10 changes: 6 additions & 4 deletions vms/saevm/saexec/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,16 +292,18 @@ func (e *Executor) afterExecution(b *blocks.Block, r *ExecutionResults) error {
// post-execution state to no longer be consensus-critical.
e.Tracker.Track(root)

// The strict ordering of the next 3 calls guarantees invariants that MUST
// NOT be broken:
// The strict ordering of the following calls guarantees invariants that
// MUST NOT be broken:
//
// 1. [blocks.Block.MarkExecuted] guarantees disk then in-memory changes.
// 2. Internal indicator of last executed MUST follow in-memory change.
// 3. External indicator of last executed MUST follow internal indicator.
// 3. Metrics indicator of last executed MUST follow internal indicator.
// 4. External indicator of last executed MUST follow internal indicator.
if err := b.MarkExecuted(e.db, e.xdb, r.FinishBy.Gas.Clone(), r.FinishBy.Wall, r.BaseFee.ToBig(), r.Receipts, root, &e.lastExecuted /* (2) */); err != nil {
return err
}
e.sendPostExecutionEvents(b.EthBlock(), r.Receipts) // (3)
e.metrics.markExecuted(b.Height()) // (3)
e.sendPostExecutionEvents(b.EthBlock(), r.Receipts) // (4)
return nil
}

Expand Down
53 changes: 53 additions & 0 deletions vms/saevm/saexec/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (C) 2019, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package saexec

import (
"errors"

"github.com/prometheus/client_golang/prometheus"
)

// Exported metric names for the SAE lifecycle bucket. Cross-package callers
// (tests, alert/dashboard wiring) should reference these constants rather
// than the raw strings.
const (
LastExecutedHeightName = "last_executed_height"
LastSettledHeightName = "last_settled_height"
)

// metrics holds the SAE block-lifecycle gauges. Both live with [Executor]
// because it owns the execution lifecycle: [Executor.afterExecution]
// writes last_executed_height directly, and [Executor.MarkSettled]
// exposes the settled gauge for the sae VM to drive. Co-locating them
// keeps registration in one place.
type metrics struct {
lastExecutedHeight prometheus.Gauge
lastSettledHeight prometheus.Gauge
}

func newMetrics(reg prometheus.Registerer) (*metrics, error) {
m := &metrics{
lastExecutedHeight: prometheus.NewGauge(prometheus.GaugeOpts{
Name: LastExecutedHeightName,
Help: "Height of the latest block that completed async execution.",
}),
lastSettledHeight: prometheus.NewGauge(prometheus.GaugeOpts{
Name: LastSettledHeightName,
Help: "Height of the latest block that has settled.",
}),
}
return m, errors.Join(
reg.Register(m.lastExecutedHeight),
reg.Register(m.lastSettledHeight),
)
}

func (m *metrics) markExecuted(height uint64) {
m.lastExecutedHeight.Set(float64(height))
}

func (m *metrics) markSettled(height uint64) {
m.lastSettledHeight.Set(float64(height))
}
Loading
Loading