Skip to content

Commit 8b75d1c

Browse files
authored
[Solana TXM] Adding beholder metrics (#1359)
* adding beholder metrics * tidy * chainId string for solana * Start txm in a test * Starting txm in another test * just test unit test first * use attributes to pass in labels * tidY * pass proper ctx, initmetrics, move metrics to prom struct * passing in ctx * pass proper ctx * revert * return * nit
1 parent ea6e50e commit 8b75d1c

File tree

10 files changed

+375
-187
lines changed

10 files changed

+375
-187
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ require (
3333
github.com/smartcontractkit/freeport v0.1.3-0.20250716200817-cb5dfd0e369e
3434
github.com/smartcontractkit/libocr v0.0.0-20250905115425-2785a5cee79d
3535
github.com/stretchr/testify v1.10.0
36+
go.opentelemetry.io/otel v1.37.0
37+
go.opentelemetry.io/otel/metric v1.37.0
3638
go.uber.org/zap v1.27.0
3739
golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc
3840
golang.org/x/sync v0.16.0
@@ -154,7 +156,6 @@ require (
154156
go.mongodb.org/mongo-driver v1.17.0 // indirect
155157
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
156158
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
157-
go.opentelemetry.io/otel v1.37.0 // indirect
158159
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 // indirect
159160
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 // indirect
160161
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 // indirect
@@ -166,7 +167,6 @@ require (
166167
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect
167168
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 // indirect
168169
go.opentelemetry.io/otel/log v0.13.0 // indirect
169-
go.opentelemetry.io/otel/metric v1.37.0 // indirect
170170
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
171171
go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect
172172
go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect

pkg/solana/chain_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,10 +609,10 @@ loop:
609609
for {
610610
select {
611611
case <-ctx.Done():
612-
assert.Equal(t, 0, testChain.txm.InflightTxs())
612+
assert.Equal(t, 0, testChain.txm.InflightTxs(ctx))
613613
break loop
614614
case <-ticker.C:
615-
if testChain.txm.InflightTxs() == 0 {
615+
if testChain.txm.InflightTxs(ctx) == 0 {
616616
cancel() // exit for loop
617617
}
618618
}

pkg/solana/txm/metrics.go

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
package txm
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/prometheus/client_golang/prometheus"
8+
"github.com/prometheus/client_golang/prometheus/promauto"
9+
"go.opentelemetry.io/otel/attribute"
10+
"go.opentelemetry.io/otel/metric"
11+
12+
"github.com/smartcontractkit/chainlink-common/pkg/beholder"
13+
"github.com/smartcontractkit/chainlink-common/pkg/metrics"
14+
)
15+
16+
var (
17+
// successful transactions
18+
promSolTxmSuccessTxs = promauto.NewCounterVec(prometheus.CounterOpts{
19+
Name: "solana_txm_tx_success",
20+
Help: "Number of transactions that are included and successfully executed on chain",
21+
}, []string{"chainID"})
22+
promSolTxmFinalizedTxs = promauto.NewCounterVec(prometheus.CounterOpts{
23+
Name: "solana_txm_tx_finalized",
24+
Help: "Number of transactions that are finalized on chain",
25+
}, []string{"chainID"})
26+
27+
// inflight transactions
28+
promSolTxmPendingTxs = promauto.NewGaugeVec(prometheus.GaugeOpts{
29+
Name: "solana_txm_tx_pending",
30+
Help: "Number of transactions that are pending confirmation",
31+
}, []string{"chainID"})
32+
33+
// error cases
34+
promSolTxmErrorTxs = promauto.NewCounterVec(prometheus.CounterOpts{
35+
Name: "solana_txm_tx_error",
36+
Help: "Number of transactions that have errored across all cases",
37+
}, []string{"chainID"})
38+
promSolTxmRevertTxs = promauto.NewCounterVec(prometheus.CounterOpts{
39+
Name: "solana_txm_tx_error_revert",
40+
Help: "Number of transactions that are included and failed onchain",
41+
}, []string{"chainID"})
42+
promSolTxmRejectTxs = promauto.NewCounterVec(prometheus.CounterOpts{
43+
Name: "solana_txm_tx_error_reject",
44+
Help: "Number of transactions that the RPC immediately rejected",
45+
}, []string{"chainID"})
46+
promSolTxmDropTxs = promauto.NewCounterVec(prometheus.CounterOpts{
47+
Name: "solana_txm_tx_error_drop",
48+
Help: "Number of transactions that timed out during confirmation. Note: tx is likely dropped from the chain, but may still be included.",
49+
}, []string{"chainID"})
50+
promSolTxmSimRevertTxs = promauto.NewCounterVec(prometheus.CounterOpts{
51+
Name: "solana_txm_tx_error_sim_revert",
52+
Help: "Number of transactions that reverted during simulation. Note: tx may still be included onchain",
53+
}, []string{"chainID"})
54+
promSolTxmSimOtherTxs = promauto.NewCounterVec(prometheus.CounterOpts{
55+
Name: "solana_txm_tx_error_sim_other",
56+
Help: "Number of transactions that failed simulation with an unrecognized error. Note: tx may still be included onchain",
57+
}, []string{"chainID"})
58+
promSolTxmDependencyFailTxs = promauto.NewCounterVec(prometheus.CounterOpts{
59+
Name: "solana_txm_tx_error_dependency",
60+
Help: "Number of transactions that failed due to a dependency tx failing.",
61+
}, []string{"chainID"})
62+
)
63+
64+
type solTxmMetrics struct {
65+
metrics.Labeler
66+
chainID string
67+
68+
// successful transactions
69+
successTxs metric.Int64Counter
70+
finalizedTxs metric.Int64Counter
71+
72+
// inflight transactions
73+
pendingTxs metric.Int64Gauge
74+
75+
// error cases
76+
errorTxs metric.Int64Counter
77+
revertTxs metric.Int64Counter
78+
rejectTxs metric.Int64Counter
79+
dropTxs metric.Int64Counter
80+
simRevertTxs metric.Int64Counter
81+
simOtherTxs metric.Int64Counter
82+
dependencyFailTxs metric.Int64Counter
83+
}
84+
85+
func NewSolTxmMetrics(chainID string) (*solTxmMetrics, error) {
86+
m := beholder.GetMeter()
87+
var err error
88+
89+
successTxs, err := m.Int64Counter("solana_txm_tx_success")
90+
if err != nil {
91+
return nil, fmt.Errorf("failed to register solana success txs: %w", err)
92+
}
93+
94+
finalizedTxs, err := m.Int64Counter("solana_txm_tx_finalized")
95+
if err != nil {
96+
return nil, fmt.Errorf("failed to register solana finalized txs: %w", err)
97+
}
98+
99+
pendingTxs, err := m.Int64Gauge("solana_txm_tx_pending")
100+
if err != nil {
101+
return nil, fmt.Errorf("failed to register solana pending txs: %w", err)
102+
}
103+
104+
errorTxs, err := m.Int64Counter("solana_txm_tx_error")
105+
if err != nil {
106+
return nil, fmt.Errorf("failed to register solana error txs: %w", err)
107+
}
108+
109+
revertTxs, err := m.Int64Counter("solana_txm_tx_error_revert")
110+
if err != nil {
111+
return nil, fmt.Errorf("failed to register solana revert txs: %w", err)
112+
}
113+
114+
rejectTxs, err := m.Int64Counter("solana_txm_tx_error_reject")
115+
if err != nil {
116+
return nil, fmt.Errorf("failed to register solana reject txs: %w", err)
117+
}
118+
119+
dropTxs, err := m.Int64Counter("solana_txm_tx_error_drop")
120+
if err != nil {
121+
return nil, fmt.Errorf("failed to register solana drop txs: %w", err)
122+
}
123+
124+
simRevertTxs, err := m.Int64Counter("solana_txm_tx_error_sim_revert")
125+
if err != nil {
126+
return nil, fmt.Errorf("failed to register solana sim revert txs: %w", err)
127+
}
128+
129+
simOtherTxs, err := m.Int64Counter("solana_txm_tx_error_sim_other")
130+
if err != nil {
131+
return nil, fmt.Errorf("failed to register solana sim other txs: %w", err)
132+
}
133+
134+
dependencyFailTxs, err := m.Int64Counter("solana_txm_tx_error_dependency")
135+
if err != nil {
136+
return nil, fmt.Errorf("failed to register solana dependency fail txs: %w", err)
137+
}
138+
139+
return &solTxmMetrics{
140+
chainID: chainID,
141+
Labeler: metrics.NewLabeler().With("chainID", chainID),
142+
143+
successTxs: successTxs,
144+
finalizedTxs: finalizedTxs,
145+
pendingTxs: pendingTxs,
146+
errorTxs: errorTxs,
147+
revertTxs: revertTxs,
148+
rejectTxs: rejectTxs,
149+
dropTxs: dropTxs,
150+
simRevertTxs: simRevertTxs,
151+
simOtherTxs: simOtherTxs,
152+
dependencyFailTxs: dependencyFailTxs,
153+
}, nil
154+
}
155+
156+
func (m *solTxmMetrics) GetOtelAttributes() []attribute.KeyValue {
157+
return beholder.OtelAttributes(m.Labels).AsStringAttributes()
158+
}
159+
160+
func (m *solTxmMetrics) IncrementSuccessTxs(ctx context.Context) {
161+
promSolTxmSuccessTxs.WithLabelValues(m.chainID).Add(1)
162+
m.successTxs.Add(ctx, 1, metric.WithAttributes(m.GetOtelAttributes()...))
163+
}
164+
165+
func (m *solTxmMetrics) IncrementFinalizedTxs(ctx context.Context) {
166+
promSolTxmFinalizedTxs.WithLabelValues(m.chainID).Add(1)
167+
m.finalizedTxs.Add(ctx, 1, metric.WithAttributes(m.GetOtelAttributes()...))
168+
}
169+
170+
func (m *solTxmMetrics) SetPendingTxs(ctx context.Context, count int) {
171+
promSolTxmPendingTxs.WithLabelValues(m.chainID).Set(float64(count))
172+
m.pendingTxs.Record(ctx, int64(count), metric.WithAttributes(m.GetOtelAttributes()...))
173+
}
174+
175+
func (m *solTxmMetrics) IncrementErrorTxs(ctx context.Context) {
176+
promSolTxmErrorTxs.WithLabelValues(m.chainID).Add(1)
177+
m.errorTxs.Add(ctx, 1, metric.WithAttributes(m.GetOtelAttributes()...))
178+
}
179+
180+
func (m *solTxmMetrics) IncrementRevertTxs(ctx context.Context) {
181+
promSolTxmRevertTxs.WithLabelValues(m.chainID).Add(1)
182+
m.revertTxs.Add(ctx, 1, metric.WithAttributes(m.GetOtelAttributes()...))
183+
}
184+
185+
func (m *solTxmMetrics) IncrementRejectTxs(ctx context.Context) {
186+
promSolTxmRejectTxs.WithLabelValues(m.chainID).Add(1)
187+
m.rejectTxs.Add(ctx, 1, metric.WithAttributes(m.GetOtelAttributes()...))
188+
}
189+
190+
func (m *solTxmMetrics) IncrementDropTxs(ctx context.Context) {
191+
promSolTxmDropTxs.WithLabelValues(m.chainID).Add(1)
192+
m.dropTxs.Add(ctx, 1, metric.WithAttributes(m.GetOtelAttributes()...))
193+
}
194+
195+
func (m *solTxmMetrics) IncrementSimRevertTxs(ctx context.Context) {
196+
promSolTxmSimRevertTxs.WithLabelValues(m.chainID).Add(1)
197+
m.simRevertTxs.Add(ctx, 1, metric.WithAttributes(m.GetOtelAttributes()...))
198+
}
199+
200+
func (m *solTxmMetrics) IncrementSimOtherTxs(ctx context.Context) {
201+
promSolTxmSimOtherTxs.WithLabelValues(m.chainID).Add(1)
202+
m.simOtherTxs.Add(ctx, 1, metric.WithAttributes(m.GetOtelAttributes()...))
203+
}
204+
205+
func (m *solTxmMetrics) IncrementDependencyFailTxs(ctx context.Context) {
206+
promSolTxmDependencyFailTxs.WithLabelValues(m.chainID).Add(1)
207+
m.dependencyFailTxs.Add(ctx, 1, metric.WithAttributes(m.GetOtelAttributes()...))
208+
}

0 commit comments

Comments
 (0)