Skip to content

Commit

Permalink
SOM: nop success count per feed (#720)
Browse files Browse the repository at this point in the history
* SOM: nop success count per feed

* fix logger name
  • Loading branch information
aalu1418 committed May 24, 2024
1 parent a10ff1f commit bd0c027
Show file tree
Hide file tree
Showing 9 changed files with 319 additions and 15 deletions.
9 changes: 7 additions & 2 deletions cmd/monitoring/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,22 @@ func main() {
metrics.NewFeedBalances(logger.With(log, "component", promMetrics)),
)
reportObservationsFactory := exporter.NewReportObservationsFactory(
logger.With(log, "component", "solana-prome-exporter"),
logger.With(log, "component", promExporter),
metrics.NewReportObservations(logger.With(log, "component", promMetrics)),
)
feesFactory := exporter.NewFeesFactory(
logger.With(log, "component", "solana-prome-exporter"),
logger.With(log, "component", promExporter),
metrics.NewFees(logger.With(log, "component", promMetrics)),
)
nodeSuccessFactory := exporter.NewNodeSuccessFactory(
logger.With(log, "component", promExporter),
metrics.NewNodeSuccess(logger.With(log, "component", promMetrics)),
)
monitor.ExporterFactories = append(monitor.ExporterFactories,
feedBalancesExporterFactory,
reportObservationsFactory,
feesFactory,
nodeSuccessFactory,
)

// network exporters
Expand Down
111 changes: 111 additions & 0 deletions pkg/monitoring/exporter/nodesuccess.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package exporter

import (
"context"

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

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

func NewNodeSuccessFactory(
log commonMonitoring.Logger,
metrics metrics.NodeSuccess,
) commonMonitoring.ExporterFactory {
return &nodeSuccessFactory{
log,
metrics,
}
}

type nodeSuccessFactory struct {
log commonMonitoring.Logger
metrics metrics.NodeSuccess
}

func (p *nodeSuccessFactory) NewExporter(
params commonMonitoring.ExporterParams,
) (commonMonitoring.Exporter, error) {
nodes, err := config.MakeSolanaNodeConfigs(params.Nodes)
if err != nil {
return nil, err
}

nodesMap := map[solana.PublicKey]string{}
for _, v := range nodes {
pubkey, err := v.PublicKey()
if err != nil {
return nil, err
}
nodesMap[pubkey] = v.GetName()
}

return &nodeSuccess{
metrics.FeedInput{
AccountAddress: params.FeedConfig.GetContractAddress(),
FeedID: params.FeedConfig.GetContractAddress(),
ChainID: params.ChainConfig.GetChainID(),
ContractStatus: params.FeedConfig.GetContractStatus(),
ContractType: params.FeedConfig.GetContractType(),
FeedName: params.FeedConfig.GetName(),
FeedPath: params.FeedConfig.GetPath(),
NetworkID: params.ChainConfig.GetNetworkID(),
NetworkName: params.ChainConfig.GetNetworkName(),
},
nodesMap,
p.log,
p.metrics,
}, nil
}

type nodeSuccess struct {
feedLabel metrics.FeedInput // static for each feed
nodes map[solana.PublicKey]string
log commonMonitoring.Logger
metrics metrics.NodeSuccess
}

func (p *nodeSuccess) Export(ctx context.Context, data interface{}) {
details, err := types.MakeTxDetails(data)
if err != nil {
return // skip if input could not be parsed
}

// skip on no updates
if len(details) == 0 {
return
}

// calculate count
count := map[solana.PublicKey]int{}
for _, d := range details {
count[d.Sender]++
}

for k, v := range count {
name, isOperator := p.nodes[k]
if !isOperator {
p.log.Debugw("Sender does not match known operator", "sender", k)
continue // skip if not known operator
}

p.metrics.Add(v, metrics.NodeFeedInput{
NodeAddress: k.String(),
NodeOperator: name,
FeedInput: p.feedLabel,
})
}
}

func (p *nodeSuccess) Cleanup(_ context.Context) {
for k, v := range p.nodes {
p.metrics.Cleanup(metrics.NodeFeedInput{
NodeAddress: k.String(),
NodeOperator: v,
FeedInput: p.feedLabel,
})
}
}
55 changes: 55 additions & 0 deletions pkg/monitoring/exporter/nodesuccess_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package exporter

import (
"testing"

"github.com/gagliardetto/solana-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"

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

"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/config"
"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 TestNodeSuccess(t *testing.T) {
zeroAddress := solana.PublicKey{}
ctx := tests.Context(t)
lgr, logs := logger.TestObserved(t, zapcore.DebugLevel)
m := mocks.NewNodeSuccess(t)
m.On("Add", mock.Anything, mock.Anything).Once()
m.On("Cleanup", mock.Anything).Once()

factory := NewNodeSuccessFactory(lgr, m)

chainConfig := testutils.GenerateChainConfig()
feedConfig := testutils.GenerateFeedConfig()
exporter, err := factory.NewExporter(commonMonitoring.ExporterParams{ChainConfig: chainConfig,
FeedConfig: feedConfig,
Nodes: []commonMonitoring.NodeConfig{
config.SolanaNodeConfig{
NodeAddress: []string{zeroAddress.String()}},
}})
require.NoError(t, err)

// happy path - only one call (only 1 address is recognized)
exporter.Export(ctx, []types.TxDetails{
{Sender: zeroAddress},
{Sender: solana.PublicKey{1}},
})
exporter.Cleanup(ctx)
assert.Equal(t, 1, logs.FilterMessageSnippet("Sender does not match known operator").Len())

// not txdetails type - no calls to mock
assert.NotPanics(t, func() { exporter.Export(ctx, 1) })

// zero txdetails - no calls to mock
exporter.Export(ctx, []types.TxDetails{})
}
26 changes: 13 additions & 13 deletions pkg/monitoring/metrics/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,30 @@ func newSimpleGauge(log commonMonitoring.Logger, name string) simpleGauge {
return simpleGauge{log, name}
}

func (sg simpleGauge) set(value float64, labels prometheus.Labels) {
func (sg simpleGauge) run(
f func(*prometheus.GaugeVec),
) {
if gauges == nil {
sg.log.Fatalw("gauges is nil")
return
}

gauge, ok := gauges[sg.metricName]
if !ok {
if !ok || gauge == nil {
sg.log.Errorw("gauge not found", "name", sg.metricName)
return
}
gauge.With(labels).Set(value)
f(gauge)
}

func (sg simpleGauge) set(value float64, labels prometheus.Labels) {
sg.run(func(g *prometheus.GaugeVec) { g.With(labels).Set(value) })
}

func (sg simpleGauge) delete(labels prometheus.Labels) {
if gauges == nil {
sg.log.Fatalw("gauges is nil")
return
}
sg.run(func(g *prometheus.GaugeVec) { g.Delete(labels) })
}

gauge, ok := gauges[sg.metricName]
if !ok {
sg.log.Errorw("gauge not found", "name", sg.metricName)
return
}
gauge.Delete(labels)
func (sg simpleGauge) add(value float64, labels prometheus.Labels) {
sg.run(func(g *prometheus.GaugeVec) { g.With(labels).Add(value) })
}
25 changes: 25 additions & 0 deletions pkg/monitoring/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ var (
"network_name",
}

nodeFeedLabels = append([]string{
"node_address",
"node_operator",
}, feedLabels...)

nodeLabels = []string{
"account_address",
"node_operator",
Expand Down Expand Up @@ -67,6 +72,14 @@ func init() {
)
}

// init gauge for node success per feed per node
gauges[types.NodeSuccessMetric] = promauto.NewGaugeVec(
prometheus.GaugeOpts{
Name: types.NodeSuccessMetric,
},
nodeFeedLabels,
)

// init gauge for slot height
gauges[types.SlotHeightMetric] = promauto.NewGaugeVec(
prometheus.GaugeOpts{
Expand All @@ -93,3 +106,15 @@ func (i FeedInput) ToPromLabels() prometheus.Labels {
"network_name": i.NetworkName,
}
}

type NodeFeedInput struct {
NodeAddress, NodeOperator string
FeedInput
}

func (i NodeFeedInput) ToPromLabels() prometheus.Labels {
l := i.FeedInput.ToPromLabels()
l["node_address"] = i.NodeAddress
l["node_operator"] = i.NodeOperator
return l
}
38 changes: 38 additions & 0 deletions pkg/monitoring/metrics/mocks/NodeSuccess.go

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

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

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

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

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

type NodeSuccess interface {
Add(count int, i NodeFeedInput)
Cleanup(i NodeFeedInput)
}

var _ NodeSuccess = (*nodeSuccess)(nil)

type nodeSuccess struct {
simpleGauge
}

func NewNodeSuccess(log commonMonitoring.Logger) *nodeSuccess {
return &nodeSuccess{newSimpleGauge(log, types.NodeSuccessMetric)}
}

func (ro *nodeSuccess) Add(count int, i NodeFeedInput) {
ro.add(float64(count), i.ToPromLabels())
}

func (ro *nodeSuccess) Cleanup(i NodeFeedInput) {
ro.delete(i.ToPromLabels())
}
36 changes: 36 additions & 0 deletions pkg/monitoring/metrics/nodesuccess_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package metrics

import (
"testing"

"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 TestNodeSuccess(t *testing.T) {
lgr := logger.Test(t)
m := NewNodeSuccess(lgr)

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

v := 100
inputs := NodeFeedInput{FeedInput: FeedInput{NetworkName: t.Name()}}

// set gauge
assert.NotPanics(t, func() { m.Add(v, inputs) })
assert.NotPanics(t, func() { m.Add(v, inputs) })
promBal := testutil.ToFloat64(g.With(inputs.ToPromLabels()))
assert.Equal(t, float64(2*v), promBal)

// cleanup gauges
assert.Equal(t, 1, testutil.CollectAndCount(g))
assert.NotPanics(t, func() { m.Cleanup(inputs) })
assert.Equal(t, 0, testutil.CollectAndCount(g))
}
2 changes: 2 additions & 0 deletions pkg/monitoring/types/txdetails.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ var (
ReportObservationMetric = "report_observations"
TxFeeMetric = "tx_fee"
ComputeUnitPriceMetric = "tx_compute_unit_price"
NodeSuccessMetric = "node_success" // per node per feed

// these metrics are per feed
TxDetailsMetrics = []string{
ReportObservationMetric,
TxFeeMetric,
Expand Down

0 comments on commit bd0c027

Please sign in to comment.