Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
21 changes: 21 additions & 0 deletions vms/saevm/metrics/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("//.bazel:defs.bzl", "go_test")

go_library(
name = "metrics",
srcs = ["metrics.go"],
importpath = "github.com/ava-labs/avalanchego/vms/saevm/metrics",
visibility = ["//visibility:public"],
deps = ["@com_github_prometheus_client_golang//prometheus"],
)

go_test(
name = "metrics_test",
srcs = ["metrics_test.go"],
embed = [":metrics"],
deps = [
"@com_github_prometheus_client_golang//prometheus",
"@com_github_prometheus_client_golang//prometheus/testutil",
"@com_github_stretchr_testify//require",
],
)
48 changes: 48 additions & 0 deletions vms/saevm/metrics/metrics.go
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think making a "metrics" package makes sense. This metrics struct IMO should be unexported in whatever package has access to each metric. Do you agree?

Copy link
Copy Markdown
Contributor Author

@JonathanOppenheimer JonathanOppenheimer May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modeled this after avalanchego/avm/metrics. I don't think it's as simple as unexporting it in whatever package has access to each metric -- sae and saeexec -- we need host it, and then export it somewhere. This package will also grow over time as we add more metrics, all of which not be as tightly coupled to an individual package.

What do you think?

I could at least unexport the gauge fields so callers can only go through MarkBlockExecuted/MarkBlockSettled.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked in the office about this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this look better?

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (C) 2019, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// Package metrics defines SAE Prometheus collectors. These lifecycle frontier
// metrics can be compared with Snowman accepted height to derive execution and
// settlement lag.
package metrics

import (
"errors"

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

// Metrics holds SAE Prometheus collectors and provides semantic update methods.
type Metrics struct {
LastExecutedHeight prometheus.Gauge
LastSettledHeight prometheus.Gauge
}

// New constructs and registers SAE metrics.
func New(reg prometheus.Registerer) (*Metrics, error) {
m := &Metrics{
LastExecutedHeight: prometheus.NewGauge(prometheus.GaugeOpts{
Name: "last_executed_height",
Help: "Height of the latest block that completed async execution.",
}),
LastSettledHeight: prometheus.NewGauge(prometheus.GaugeOpts{
Name: "last_settled_height",
Help: "Height of the latest block that has settled.",
}),
}

return m, errors.Join(
reg.Register(m.LastExecutedHeight),
reg.Register(m.LastSettledHeight),
)
}

// MarkBlockExecuted updates metrics for a block that completed async execution.
func (m *Metrics) MarkBlockExecuted(height uint64) {
m.LastExecutedHeight.Set(float64(height))
}

// MarkBlockSettled updates metrics for a block that has settled.
func (m *Metrics) MarkBlockSettled(height uint64) {
m.LastSettledHeight.Set(float64(height))
}
23 changes: 23 additions & 0 deletions vms/saevm/metrics/metrics_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (C) 2019, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package metrics

import (
"testing"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/require"
)

func TestMetrics(t *testing.T) {
metrics, err := New(prometheus.NewRegistry())
require.NoError(t, err, "New()")

metrics.MarkBlockExecuted(6)
metrics.MarkBlockSettled(7)

require.Equal(t, float64(6), testutil.ToFloat64(metrics.LastExecutedHeight), "last executed height")
require.Equal(t, float64(7), testutil.ToFloat64(metrics.LastSettledHeight), "last settled height")
}
Comment thread
JonathanOppenheimer marked this conversation as resolved.
Outdated
2 changes: 2 additions & 0 deletions vms/saevm/sae/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ go_library(
"//vms/saevm/adaptor",
"//vms/saevm/blocks",
"//vms/saevm/hook",
"//vms/saevm/metrics",
"//vms/saevm/params",
"//vms/saevm/proxytime",
"//vms/saevm/sae/rpc",
Expand Down Expand Up @@ -137,6 +138,7 @@ 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/testutil",
"@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.metrics.MarkBlockSettled(s.Height())
}

// I(s ∈ S) above, before I(b ∈ A) before X(b ∈ A)
Expand Down
38 changes: 24 additions & 14 deletions vms/saevm/sae/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"github.com/ava-labs/avalanchego/vms/saevm/txgossip"

snowcommon "github.com/ava-labs/avalanchego/snow/engine/common"
saemetrics "github.com/ava-labs/avalanchego/vms/saevm/metrics"
saetypes "github.com/ava-labs/avalanchego/vms/saevm/types"
)

Expand All @@ -52,10 +53,11 @@ 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
metrics *saemetrics.Metrics
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an intentional choice to have the metricRegistry as the *prometheus.Registry used for registration and /ext/metrics (and as already used for registerer for the bloom/gossip/p2p metrics) while metrics is used to record metrics updates.


db ethdb.Database
xdb saetypes.ExecutionResults
Expand Down Expand Up @@ -124,21 +126,26 @@ func NewVM[T hook.Transaction](
cfg.Now = time.Now
}
vm := &VM{
hooks: hooks,
config: cfg,
snowCtx: snowCtx,
metrics: prometheus.NewRegistry(),
db: db,
hooks: hooks,
config: cfg,
snowCtx: snowCtx,
metricRegistry: prometheus.NewRegistry(),
db: db,
}
defer func() {
if retErr != nil {
retErr = errors.Join(retErr, vm.close())
}
}()

if err := snowCtx.Metrics.Register("sae", vm.metrics); err != nil {
if err := snowCtx.Metrics.Register("sae", vm.metricRegistry); err != nil {
return nil, err
}
metrics, err := saemetrics.New(vm.metricRegistry)
if err != nil {
return nil, fmt.Errorf("new metrics: %w", err)
}
vm.metrics = metrics

xdb, err := hooks.ExecutionResultsDB(
filepath.Join(snowCtx.ChainDataDir, "sae_execution_results"),
Expand Down Expand Up @@ -180,6 +187,7 @@ func NewVM[T hook.Transaction](
cfg.DBConfig,
hooks,
snowCtx.Log,
vm.metrics,
)
if err != nil {
return nil, fmt.Errorf("saexec.New(...): %v", err)
Expand All @@ -201,6 +209,8 @@ func NewVM[T hook.Transaction](
vm.last.settled.Store(lastSettled)
vm.last.accepted.Store(head)
vm.preference.Store(head)
vm.metrics.MarkBlockExecuted(head.Height())
vm.metrics.MarkBlockSettled(lastSettled.Height())
Comment thread
JonathanOppenheimer marked this conversation as resolved.
Outdated
}

{ // ========== Mempool ==========
Expand All @@ -214,11 +224,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 +249,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 +263,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
15 changes: 15 additions & 0 deletions vms/saevm/sae/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,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/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
Expand Down Expand Up @@ -915,6 +916,20 @@ 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)
metrics := sut.rawVM.metrics

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()), testutil.ToFloat64(metrics.LastSettledHeight), "last settled height")
}

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 @@ -19,6 +19,7 @@ go_library(
"//vms/saevm/blocks",
"//vms/saevm/gastime",
"//vms/saevm/hook",
"//vms/saevm/metrics",
"//vms/saevm/saedb",
"//vms/saevm/types",
"@com_github_ava_labs_libevm//common",
Expand Down Expand Up @@ -49,6 +50,7 @@ go_test(
"//vms/saevm/cmputils",
"//vms/saevm/gastime",
"//vms/saevm/hook/hookstest",
"//vms/saevm/metrics",
"//vms/saevm/proxytime",
"//vms/saevm/saedb",
"//vms/saevm/saetest",
Expand All @@ -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
1 change: 1 addition & 0 deletions vms/saevm/saexec/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ func (e *Executor) afterExecution(b *blocks.Block, r *ExecutionResults) error {
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.metrics.MarkBlockExecuted(b.Height())
Comment thread
JonathanOppenheimer marked this conversation as resolved.
Outdated
e.sendPostExecutionEvents(b.EthBlock(), r.Receipts) // (3)
return nil
}
Expand Down
4 changes: 4 additions & 0 deletions vms/saevm/saexec/saexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/ava-labs/avalanchego/vms/saevm/hook"
"github.com/ava-labs/avalanchego/vms/saevm/saedb"

saemetrics "github.com/ava-labs/avalanchego/vms/saevm/metrics"
saetypes "github.com/ava-labs/avalanchego/vms/saevm/types"
)

Expand All @@ -49,6 +50,7 @@ type Executor struct {
chainConfig *params.ChainConfig
db ethdb.Database
xdb saetypes.ExecutionResults
metrics *saemetrics.Metrics
}

// New constructs and starts a new [Executor]. Call [Executor.Close] to release
Expand All @@ -66,6 +68,7 @@ func New(
saedbConfig saedb.Config,
hooks hook.Points,
log logging.Logger,
metrics *saemetrics.Metrics,
) (*Executor, error) {
t, err := saedb.NewTracker(db, saedbConfig, lastExecuted.PostExecutionStateRoot(), log)
Comment thread
JonathanOppenheimer marked this conversation as resolved.
if err != nil {
Expand All @@ -90,6 +93,7 @@ func New(
chainConfig: chainConfig,
db: db,
xdb: xdb,
metrics: metrics,
receipts: newSyncMap[common.Hash, eventual.Value[*Receipt]](),
}
e.lastExecuted.Store(lastExecuted)
Expand Down
Loading
Loading