Skip to content

Commit

Permalink
SOM: slot height (#663)
Browse files Browse the repository at this point in the history
* wip: slot height tracking

* add slot height metric + SlotHeight type

* implement exporter

* fix: file naming

* share naming + test cov
  • Loading branch information
aalu1418 committed Apr 18, 2024
1 parent 6fe3ee0 commit 811bed3
Show file tree
Hide file tree
Showing 12 changed files with 343 additions and 4 deletions.
22 changes: 18 additions & 4 deletions cmd/monitoring/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,27 @@ func main() {
chainReader,
logger.With(log, "component", "source-node-balances"),
)
slotHeightSourceFactory := monitoring.NewSlotHeightSourceFactory(
chainReader,
logger.With(log, "component", "souce-slot-height"),
)
monitor.NetworkSourceFactories = append(monitor.NetworkSourceFactories,
nodeBalancesSourceFactory,
slotHeightSourceFactory,
)

// exporter names
promExporter := "solana-prom-exporter"
promMetrics := "solana-metrics"

// per-feed exporters
feedBalancesExporterFactory := exporter.NewFeedBalancesFactory(
logger.With(log, "component", "solana-prom-exporter"),
metrics.NewFeedBalances(logger.With(log, "component", "solana-metrics")),
logger.With(log, "component", promExporter),
metrics.NewFeedBalances(logger.With(log, "component", promMetrics)),
)
reportObservationsFactory := exporter.NewReportObservationsFactory(
logger.With(log, "component", "solana-prome-exporter"),
metrics.NewReportObservations(logger.With(log, "component", "solana-metrics")),
metrics.NewReportObservations(logger.With(log, "component", promMetrics)),
)
monitor.ExporterFactories = append(monitor.ExporterFactories,
feedBalancesExporterFactory,
Expand All @@ -96,11 +105,16 @@ func main() {

// network exporters
nodeBalancesExporterFactory := exporter.NewNodeBalancesFactory(
logger.With(log, "component", "solana-prom-exporter"),
logger.With(log, "component", promExporter),
metrics.NewNodeBalances,
)
slotHeightExporterFactory := exporter.NewSlotHeightFactory(
logger.With(log, "component", promExporter),
metrics.NewSlotHeight(logger.With(log, "component", promMetrics)),
)
monitor.NetworkExporterFactories = append(monitor.NetworkExporterFactories,
nodeBalancesExporterFactory,
slotHeightExporterFactory,
)

monitor.Run()
Expand Down
5 changes: 5 additions & 0 deletions pkg/monitoring/chain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type ChainReader interface {
GetBalance(ctx context.Context, account solana.PublicKey, commitment rpc.CommitmentType) (out *rpc.GetBalanceResult, err error)
GetSignaturesForAddressWithOpts(ctx context.Context, account solana.PublicKey, opts *rpc.GetSignaturesForAddressOpts) (out []*rpc.TransactionSignature, err error)
GetTransaction(ctx context.Context, txSig solana.Signature, opts *rpc.GetTransactionOpts) (out *rpc.GetTransactionResult, err error)
GetSlot(ctx context.Context) (slot uint64, err error)
}

func NewChainReader(client *rpc.Client) ChainReader {
Expand Down Expand Up @@ -50,3 +51,7 @@ func (c *chainReader) GetSignaturesForAddressWithOpts(ctx context.Context, accou
func (c *chainReader) GetTransaction(ctx context.Context, txSig solana.Signature, opts *rpc.GetTransactionOpts) (out *rpc.GetTransactionResult, err error) {
return c.client.GetTransaction(ctx, txSig, opts)
}

func (c *chainReader) GetSlot(ctx context.Context) (uint64, error) {
return c.client.GetSlot(ctx, rpc.CommitmentProcessed) // get latest height
}
50 changes: 50 additions & 0 deletions pkg/monitoring/exporter/slotheight.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package exporter

import (
"context"

commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring"
"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics"
"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types"
)

func NewSlotHeightFactory(
_ commonMonitoring.Logger,
metrics metrics.SlotHeight,
) commonMonitoring.ExporterFactory {
return &slotHeightFactory{
metrics,
}
}

type slotHeightFactory struct {
metrics metrics.SlotHeight
}

func (p *slotHeightFactory) NewExporter(
params commonMonitoring.ExporterParams,
) (commonMonitoring.Exporter, error) {
return &slotHeight{
params.ChainConfig.GetNetworkName(),
params.ChainConfig.GetRPCEndpoint(),
p.metrics,
}, nil
}

type slotHeight struct {
chain, url string
metrics metrics.SlotHeight
}

func (p *slotHeight) Export(ctx context.Context, data interface{}) {
slot, ok := data.(types.SlotHeight)
if !ok {
return // skip if input could not be parsed
}

p.metrics.Set(slot, p.chain, p.url)
}

func (p *slotHeight) Cleanup(_ context.Context) {
p.metrics.Cleanup()
}
36 changes: 36 additions & 0 deletions pkg/monitoring/exporter/slotheight_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package exporter

import (
"testing"

"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink-common/pkg/logger"
commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring"
"github.com/smartcontractkit/chainlink-common/pkg/utils"
"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics/mocks"
"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils"
"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types"
)

func TestSlotHeight(t *testing.T) {
ctx := utils.Context(t)
m := mocks.NewSlotHeight(t)
m.On("Set", mock.Anything, mock.Anything, mock.Anything).Once()
m.On("Cleanup").Once()

factory := NewSlotHeightFactory(logger.Test(t), m)

chainConfig := testutils.GenerateChainConfig()
exporter, err := factory.NewExporter(commonMonitoring.ExporterParams{ChainConfig: chainConfig})
require.NoError(t, err)

// happy path
exporter.Export(ctx, types.SlotHeight(10))
exporter.Cleanup(ctx)

// test passing uint64 instead of SlotHeight - should not call mock
// SlotHeight alias of uint64
exporter.Export(ctx, uint64(10))
}
8 changes: 8 additions & 0 deletions pkg/monitoring/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ func init() {
},
feedLabels,
)

// init gauge for slot height
gauges[types.SlotHeightMetric] = promauto.NewGaugeVec(
prometheus.GaugeOpts{
Name: types.SlotHeightMetric,
},
[]string{"chain", "url"},
)
}

type FeedInput struct {
Expand Down
38 changes: 38 additions & 0 deletions pkg/monitoring/metrics/mocks/SlotHeight.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions pkg/monitoring/metrics/slotheight.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package metrics

import (
"github.com/prometheus/client_golang/prometheus"
commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring"

"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types"
)

//go:generate mockery --name SlotHeight --output ./mocks/

type SlotHeight interface {
Set(slot types.SlotHeight, chain, url string)
Cleanup()
}

var _ SlotHeight = (*slotHeight)(nil)

type slotHeight struct {
simpleGauge
labels prometheus.Labels
}

func NewSlotHeight(log commonMonitoring.Logger) *slotHeight {
return &slotHeight{
simpleGauge: newSimpleGauge(log, types.SlotHeightMetric),
}
}

func (sh *slotHeight) Set(slot types.SlotHeight, chain, url string) {
sh.labels = prometheus.Labels{"chain": chain, "url": url}
sh.set(float64(slot), sh.labels)
}

func (sh *slotHeight) Cleanup() {
sh.delete(sh.labels)
}
38 changes: 38 additions & 0 deletions pkg/monitoring/metrics/slotheight_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package metrics

import (
"testing"

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

"github.com/smartcontractkit/chainlink-common/pkg/logger"

"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types"
)

func TestSlotHeight(t *testing.T) {
lgr := logger.Test(t)
m := NewSlotHeight(lgr)

// fetching gauges
g, ok := gauges[types.SlotHeightMetric]
require.True(t, ok)

v := 100

// set gauge
assert.NotPanics(t, func() { m.Set(types.SlotHeight(v), t.Name(), t.Name()+"_url") })
promBal := testutil.ToFloat64(g.With(prometheus.Labels{
"chain": t.Name(),
"url": t.Name() + "_url",
}))
assert.Equal(t, float64(v), promBal)

// cleanup gauges
assert.Equal(t, 1, testutil.CollectAndCount(g))
assert.NotPanics(t, func() { m.Cleanup() })
assert.Equal(t, 0, testutil.CollectAndCount(g))
}
24 changes: 24 additions & 0 deletions pkg/monitoring/mocks/ChainReader.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions pkg/monitoring/source_slotheight.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package monitoring

import (
"context"

commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring"

"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types"
)

func NewSlotHeightSourceFactory(
client ChainReader,
log commonMonitoring.Logger,
) commonMonitoring.NetworkSourceFactory {
return &slotHeightSourceFactory{
client,
log,
}
}

type slotHeightSourceFactory struct {
client ChainReader
log commonMonitoring.Logger
}

func (s *slotHeightSourceFactory) NewSource(
_ commonMonitoring.ChainConfig,
_ []commonMonitoring.NodeConfig,
) (commonMonitoring.Source, error) {
return &slotHeightSource{s.client}, nil
}

func (s *slotHeightSourceFactory) GetType() string {
return types.SlotHeightType
}

type slotHeightSource struct {
client ChainReader
}

func (t *slotHeightSource) Fetch(ctx context.Context) (interface{}, error) {
slot, err := t.client.GetSlot(ctx)
return types.SlotHeight(slot), err
}
34 changes: 34 additions & 0 deletions pkg/monitoring/source_slotheight_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package monitoring

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/utils"
"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/mocks"
"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types"
)

func TestSlotHeightSource(t *testing.T) {
cr := mocks.NewChainReader(t)
lgr := logger.Test(t)
ctx := utils.Context(t)

factory := NewSlotHeightSourceFactory(cr, lgr)
assert.Equal(t, types.SlotHeightType, factory.GetType())

// generate source
source, err := factory.NewSource(nil, nil)
cr.On("GetSlot", mock.Anything, mock.Anything, mock.Anything).Return(uint64(1), nil).Once()

// happy path
out, err := source.Fetch(ctx)
require.NoError(t, err)
slot, ok := out.(types.SlotHeight)
require.True(t, ok)
assert.Equal(t, types.SlotHeight(1), slot)
}

0 comments on commit 811bed3

Please sign in to comment.