Skip to content

Commit 50c7a33

Browse files
authored
BCI-3127: monitoring report observation length (#418)
1 parent 09ebc1a commit 50c7a33

File tree

5 files changed

+273
-2
lines changed

5 files changed

+273
-2
lines changed

monitoring/cmd/monitoring/main.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,18 @@ func main() {
7878
return
7979
}
8080

81+
// per-feed factories
8182
proxySourceFactory := monitoring.NewProxySourceFactory(ocr2Client)
82-
monitor.SourceFactories = append(monitor.SourceFactories, proxySourceFactory)
83+
transmissionsDetailsSourceFactory := monitoring.NewTransmissionDetailsSourceFactory(ocr2Client)
84+
monitor.SourceFactories = append(monitor.SourceFactories, proxySourceFactory, transmissionsDetailsSourceFactory)
8385

8486
metricsBuilder := monitoring.NewMetrics(logger.With(log, "component", "starknet-metrics-builder"))
8587

8688
prometheusExporterFactory := monitoring.NewPrometheusExporterFactory(metricsBuilder)
87-
monitor.ExporterFactories = append(monitor.ExporterFactories, prometheusExporterFactory)
89+
transmissionsDetailsExporterFactory := monitoring.NewTransmissionDetailsExporterFactory(metricsBuilder)
90+
monitor.ExporterFactories = append(monitor.ExporterFactories, prometheusExporterFactory, transmissionsDetailsExporterFactory)
8891

92+
// network factories
8993
nodeBalancesSourceFactory := monitoring.NewNodeBalancesSourceFactory(strTokenClient)
9094
monitor.NetworkSourceFactories = append(monitor.NetworkSourceFactories, nodeBalancesSourceFactory)
9195

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package monitoring
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
relayMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring"
8+
)
9+
10+
// NewPrometheusExporterFactory builds an implementation of the Exporter for prometheus.
11+
func NewTransmissionDetailsExporterFactory(
12+
metrics Metrics,
13+
) relayMonitoring.ExporterFactory {
14+
return &transmissionDetailsExporterFactory{
15+
metrics,
16+
}
17+
}
18+
19+
type transmissionDetailsExporterFactory struct {
20+
metrics Metrics
21+
}
22+
23+
func (p *transmissionDetailsExporterFactory) NewExporter(
24+
params relayMonitoring.ExporterParams,
25+
) (relayMonitoring.Exporter, error) {
26+
starknetFeedConfig, ok := params.FeedConfig.(StarknetFeedConfig)
27+
if !ok {
28+
return nil, fmt.Errorf("expected feedConfig to be of type StarknetFeedConfig not %T", params.FeedConfig)
29+
}
30+
return &transmissionDetailsExporter{
31+
params.ChainConfig,
32+
starknetFeedConfig,
33+
p.metrics,
34+
}, nil
35+
}
36+
37+
type transmissionDetailsExporter struct {
38+
chainConfig relayMonitoring.ChainConfig
39+
feedConfig StarknetFeedConfig
40+
metrics Metrics
41+
}
42+
43+
func (p *transmissionDetailsExporter) Export(ctx context.Context, data interface{}) {
44+
transmissionsEnvelope, found := data.(TransmissionsEnvelope)
45+
if !found {
46+
return
47+
}
48+
49+
for _, t := range transmissionsEnvelope.Transmissions {
50+
observationLength := float64(t.ObservationLength)
51+
p.metrics.SetReportObservations(
52+
observationLength,
53+
p.feedConfig.ContractAddress,
54+
p.feedConfig.GetID(),
55+
p.chainConfig.GetChainID(),
56+
p.feedConfig.GetContractStatus(),
57+
p.feedConfig.GetContractType(),
58+
p.feedConfig.Name,
59+
p.feedConfig.Path,
60+
p.chainConfig.GetNetworkID(),
61+
p.chainConfig.GetNetworkName(),
62+
)
63+
}
64+
}
65+
66+
func (p *transmissionDetailsExporter) Cleanup(_ context.Context) {
67+
p.metrics.CleanupReportObservations(
68+
p.feedConfig.GetContractAddress(),
69+
p.feedConfig.GetID(),
70+
p.chainConfig.GetChainID(),
71+
p.feedConfig.GetContractStatus(),
72+
p.feedConfig.GetContractType(),
73+
p.feedConfig.GetName(),
74+
p.feedConfig.GetPath(),
75+
p.chainConfig.GetNetworkID(),
76+
p.chainConfig.GetNetworkName(),
77+
)
78+
}

monitoring/pkg/monitoring/metrics.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99

1010
// Metrics is an interface for prometheus metrics. Makes testing easier.
1111
type Metrics interface {
12+
SetReportObservations(answer float64, accountAddress, feedID, chainID, contractStatus, contractType, feedName, feedPath, networkID, networkName string)
13+
CleanupReportObservations(accountAddress, feedID, chainID, contractStatus, contractType, feedName, feedPath, networkID, networkName string)
1214
SetProxyAnswersRaw(answer float64, proxyContractAddress, feedID, chainID, contractStatus, contractType, feedName, feedPath, networkID, networkName string)
1315
SetProxyAnswers(answer float64, proxyContractAddress, feedID, chainID, contractStatus, contractType, feedName, feedPath, networkID, networkName string)
1416
CleanupProxy(proxyContractAddress, feedID, chainID, contractStatus, contractType, feedName, feedPath, networkID, networkName string)
@@ -17,6 +19,23 @@ type Metrics interface {
1719
}
1820

1921
var (
22+
reportObservations = promauto.NewGaugeVec(
23+
prometheus.GaugeOpts{
24+
Name: "report_observations",
25+
Help: "Reports # of observations included in a transmission report",
26+
},
27+
[]string{
28+
"account_address",
29+
"feed_id",
30+
"chain_id",
31+
"contract_status",
32+
"contract_type",
33+
"feed_name",
34+
"feed_path",
35+
"network_id",
36+
"network_name",
37+
},
38+
)
2039
proxyAnswersRaw = promauto.NewGaugeVec(
2140
prometheus.GaugeOpts{
2241
Name: "proxy_answers_raw",
@@ -72,6 +91,37 @@ func (d *defaultMetrics) CleanupBalance(contractAddress, alias, networkId, netwo
7291
}
7392
}
7493

94+
func (d *defaultMetrics) SetReportObservations(answer float64, accountAddress, feedID, chainID, contractStatus, contractType, feedName, feedPath, networkID, networkName string) {
95+
reportObservations.With(prometheus.Labels{
96+
"account_address": accountAddress,
97+
"feed_id": feedID,
98+
"chain_id": chainID,
99+
"contract_status": contractStatus,
100+
"contract_type": contractType,
101+
"feed_name": feedName,
102+
"feed_path": feedPath,
103+
"network_id": networkID,
104+
"network_name": networkName,
105+
}).Set(answer)
106+
}
107+
108+
func (d *defaultMetrics) CleanupReportObservations(accountAddress, feedID, chainID, contractStatus, contractType, feedName, feedPath, networkID, networkName string) {
109+
labels := prometheus.Labels{
110+
"account_address": accountAddress,
111+
"feed_id": feedID,
112+
"chain_id": chainID,
113+
"contract_status": contractStatus,
114+
"contract_type": contractType,
115+
"feed_name": feedName,
116+
"feed_path": feedPath,
117+
"network_id": networkID,
118+
"network_name": networkName,
119+
}
120+
if !reportObservations.Delete(labels) {
121+
d.log.Errorw("failed to delete metric", "name", "report_observations", "labels", labels)
122+
}
123+
}
124+
75125
func (d *defaultMetrics) SetProxyAnswersRaw(answer float64, proxyContractAddress, feedID, chainID, contractStatus, contractType, feedName, feedPath, networkID, networkName string) {
76126
proxyAnswersRaw.With(prometheus.Labels{
77127
"proxy_contract_address": proxyContractAddress,
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package monitoring
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"math/big"
7+
8+
"github.com/NethermindEth/juno/core/felt"
9+
starknetutils "github.com/NethermindEth/starknet.go/utils"
10+
11+
relayMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring"
12+
13+
"github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/ocr2"
14+
)
15+
16+
type TransmissionInfo struct {
17+
GasPrice *big.Int
18+
ObservationLength uint32
19+
}
20+
21+
type TransmissionsEnvelope struct {
22+
Transmissions []TransmissionInfo
23+
}
24+
25+
func NewTransmissionDetailsSourceFactory(
26+
ocr2Reader ocr2.OCR2Reader,
27+
) relayMonitoring.SourceFactory {
28+
return &transmissionDetailsSourceFactory{
29+
ocr2Reader,
30+
}
31+
}
32+
33+
type transmissionDetailsSourceFactory struct {
34+
ocr2Reader ocr2.OCR2Reader
35+
}
36+
37+
func (s *transmissionDetailsSourceFactory) NewSource(
38+
_ relayMonitoring.ChainConfig,
39+
feedConfig relayMonitoring.FeedConfig,
40+
) (relayMonitoring.Source, error) {
41+
starknetFeedConfig, ok := feedConfig.(StarknetFeedConfig)
42+
if !ok {
43+
return nil, fmt.Errorf("expected feedConfig to be of type StarknetFeedConfig not %T", feedConfig)
44+
}
45+
contractAddress, err := starknetutils.HexToFelt(starknetFeedConfig.ContractAddress)
46+
if err != nil {
47+
return nil, err
48+
}
49+
return &transmissionDetailsSource{
50+
contractAddress,
51+
s.ocr2Reader,
52+
}, nil
53+
}
54+
55+
func (s *transmissionDetailsSourceFactory) GetType() string {
56+
return "transmission details"
57+
}
58+
59+
type transmissionDetailsSource struct {
60+
contractAddress *felt.Felt
61+
ocr2Reader ocr2.OCR2Reader
62+
}
63+
64+
func (s *transmissionDetailsSource) Fetch(ctx context.Context) (interface{}, error) {
65+
latestRound, err := s.ocr2Reader.LatestRoundData(ctx, s.contractAddress)
66+
if err != nil {
67+
return nil, fmt.Errorf("failed to fetch latest_round_data: %w", err)
68+
}
69+
transmissions, err := s.ocr2Reader.NewTransmissionsFromEventsAt(ctx, s.contractAddress, latestRound.BlockNumber)
70+
if err != nil {
71+
return nil, fmt.Errorf("couldn't fetch transmission events: %w", err)
72+
}
73+
var envelope TransmissionsEnvelope
74+
for _, t := range transmissions {
75+
envelope.Transmissions = append(
76+
envelope.Transmissions,
77+
TransmissionInfo{GasPrice: t.GasPrice, ObservationLength: t.ObservationsLen},
78+
)
79+
}
80+
81+
return envelope, nil
82+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package monitoring
2+
3+
import (
4+
"context"
5+
"math/big"
6+
"testing"
7+
8+
starknetutils "github.com/NethermindEth/starknet.go/utils"
9+
"github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/ocr2"
10+
"github.com/stretchr/testify/mock"
11+
"github.com/stretchr/testify/require"
12+
13+
ocr2Mocks "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/ocr2/mocks"
14+
)
15+
16+
func TestTransmissionDetailsSource(t *testing.T) {
17+
chainConfig := generateChainConfig()
18+
feedConfig := generateFeedConfig()
19+
20+
contractAddressFelt, err := starknetutils.HexToFelt(feedConfig.ContractAddress)
21+
require.NoError(t, err)
22+
23+
ocr2Reader := ocr2Mocks.NewOCR2Reader(t)
24+
blockNumber := uint64(777)
25+
ocr2Reader.On(
26+
"LatestRoundData",
27+
mock.Anything, // ctx
28+
contractAddressFelt,
29+
).Return(ocr2.RoundData{BlockNumber: blockNumber}, nil).Once()
30+
ocr2Reader.On(
31+
"NewTransmissionsFromEventsAt",
32+
mock.Anything, // ctx
33+
contractAddressFelt,
34+
blockNumber,
35+
).Return(
36+
[]ocr2.NewTransmissionEvent{
37+
{
38+
GasPrice: new(big.Int).SetUint64(7),
39+
ObservationsLen: 7,
40+
},
41+
},
42+
nil,
43+
).Once()
44+
45+
factory := NewTransmissionDetailsSourceFactory(ocr2Reader)
46+
source, err := factory.NewSource(chainConfig, feedConfig)
47+
require.NoError(t, err)
48+
49+
transmissionsEnvelope, err := source.Fetch(context.Background())
50+
require.NoError(t, err)
51+
envelope, ok := transmissionsEnvelope.(TransmissionsEnvelope)
52+
require.True(t, ok)
53+
54+
require.Equal(t, len(envelope.Transmissions), 1)
55+
require.Equal(t, envelope.Transmissions[0].GasPrice.Uint64(), uint64(7))
56+
require.Equal(t, envelope.Transmissions[0].ObservationLength, uint32(7))
57+
}

0 commit comments

Comments
 (0)