Skip to content

Commit f0b66b9

Browse files
authored
Add prometheus metrics for monitoring (#110)
1 parent 072916a commit f0b66b9

37 files changed

+2280
-155
lines changed

go.mod

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ go 1.23.2
55
require (
66
github.com/alitto/pond v1.9.2
77
github.com/aws/aws-sdk-go v1.55.5
8+
github.com/dlmiddlecote/sqlstats v1.0.2
89
github.com/getsentry/sentry-go v0.29.1
910
github.com/go-chi/chi v4.1.2+incompatible
1011
github.com/go-playground/validator/v10 v10.22.1
1112
github.com/google/uuid v1.6.0
1213
github.com/jmoiron/sqlx v1.4.0
1314
github.com/lib/pq v1.10.9
15+
github.com/mattn/go-sqlite3 v1.14.22
16+
github.com/prometheus/client_golang v1.17.0
1417
github.com/rubenv/sql-migrate v1.7.0
1518
github.com/sirupsen/logrus v1.9.3
1619
github.com/spf13/cobra v1.8.1
@@ -49,7 +52,6 @@ require (
4952
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
5053
github.com/pkg/errors v0.9.1 // indirect
5154
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
52-
github.com/prometheus/client_golang v1.17.0 // indirect
5355
github.com/prometheus/client_model v0.5.0 // indirect
5456
github.com/prometheus/common v0.44.0 // indirect
5557
github.com/prometheus/procfs v0.12.0 // indirect

go.sum

+63
Large diffs are not rendered by default.

internal/data/accounts.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,41 @@ package data
33
import (
44
"context"
55
"fmt"
6+
"time"
67

78
"github.com/stellar/wallet-backend/internal/db"
9+
"github.com/stellar/wallet-backend/internal/metrics"
810
)
911

1012
type AccountModel struct {
11-
DB db.ConnectionPool
13+
DB db.ConnectionPool
14+
MetricsService metrics.MetricsService
1215
}
1316

1417
func (m *AccountModel) Insert(ctx context.Context, address string) error {
1518
const query = `INSERT INTO accounts (stellar_address) VALUES ($1) ON CONFLICT DO NOTHING`
19+
start := time.Now()
1620
_, err := m.DB.ExecContext(ctx, query, address)
21+
duration := time.Since(start).Seconds()
22+
m.MetricsService.ObserveDBQueryDuration("INSERT", "accounts", duration)
1723
if err != nil {
1824
return fmt.Errorf("inserting address %s: %w", address, err)
1925
}
26+
m.MetricsService.IncDBQuery("INSERT", "accounts")
2027

2128
return nil
2229
}
2330

2431
func (m *AccountModel) Delete(ctx context.Context, address string) error {
2532
const query = `DELETE FROM accounts WHERE stellar_address = $1`
33+
start := time.Now()
2634
_, err := m.DB.ExecContext(ctx, query, address)
35+
duration := time.Since(start).Seconds()
36+
m.MetricsService.ObserveDBQueryDuration("DELETE", "accounts", duration)
2737
if err != nil {
2838
return fmt.Errorf("deleting address %s: %w", address, err)
2939
}
30-
40+
m.MetricsService.IncDBQuery("DELETE", "accounts")
3141
return nil
3242
}
3343

@@ -43,10 +53,13 @@ func (m *AccountModel) IsAccountFeeBumpEligible(ctx context.Context, address str
4353
)
4454
`
4555
var exists bool
56+
start := time.Now()
4657
err := m.DB.GetContext(ctx, &exists, query, address)
58+
duration := time.Since(start).Seconds()
59+
m.MetricsService.ObserveDBQueryDuration("SELECT", "accounts", duration)
4760
if err != nil {
4861
return false, fmt.Errorf("checking if account %s is fee bump eligible: %w", address, err)
4962
}
50-
63+
m.MetricsService.IncDBQuery("SELECT", "accounts")
5164
return exists, nil
5265
}

internal/data/accounts_test.go

+23-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import (
88
"github.com/stellar/go/keypair"
99
"github.com/stellar/wallet-backend/internal/db"
1010
"github.com/stellar/wallet-backend/internal/db/dbtest"
11+
"github.com/stellar/wallet-backend/internal/metrics"
1112
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/mock"
1214
"github.com/stretchr/testify/require"
1315
)
1416

@@ -20,8 +22,14 @@ func TestAccountModelInsert(t *testing.T) {
2022
require.NoError(t, err)
2123
defer dbConnectionPool.Close()
2224

25+
mockMetricsService := metrics.NewMockMetricsService()
26+
mockMetricsService.On("ObserveDBQueryDuration", "INSERT", "accounts", mock.Anything).Return()
27+
mockMetricsService.On("IncDBQuery", "INSERT", "accounts").Return()
28+
defer mockMetricsService.AssertExpectations(t)
29+
2330
m := &AccountModel{
24-
DB: dbConnectionPool,
31+
DB: dbConnectionPool,
32+
MetricsService: mockMetricsService,
2533
}
2634

2735
ctx := context.Background()
@@ -45,8 +53,14 @@ func TestAccountModelDelete(t *testing.T) {
4553
require.NoError(t, err)
4654
defer dbConnectionPool.Close()
4755

56+
mockMetricsService := metrics.NewMockMetricsService()
57+
mockMetricsService.On("ObserveDBQueryDuration", "DELETE", "accounts", mock.Anything).Return()
58+
mockMetricsService.On("IncDBQuery", "DELETE", "accounts").Return()
59+
defer mockMetricsService.AssertExpectations(t)
60+
4861
m := &AccountModel{
49-
DB: dbConnectionPool,
62+
DB: dbConnectionPool,
63+
MetricsService: mockMetricsService,
5064
}
5165

5266
ctx := context.Background()
@@ -73,8 +87,14 @@ func TestAccountModelIsAccountFeeBumpEligible(t *testing.T) {
7387
require.NoError(t, err)
7488
defer dbConnectionPool.Close()
7589

90+
mockMetricsService := metrics.NewMockMetricsService()
91+
mockMetricsService.On("IncDBQuery", "SELECT", "accounts").Return()
92+
mockMetricsService.On("ObserveDBQueryDuration", "SELECT", "accounts", mock.Anything).Return()
93+
defer mockMetricsService.AssertExpectations(t)
94+
7695
m := &AccountModel{
77-
DB: dbConnectionPool,
96+
DB: dbConnectionPool,
97+
MetricsService: mockMetricsService,
7898
}
7999

80100
ctx := context.Background()

internal/data/models.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,21 @@ import (
44
"errors"
55

66
"github.com/stellar/wallet-backend/internal/db"
7+
"github.com/stellar/wallet-backend/internal/metrics"
78
)
89

910
type Models struct {
1011
Payments *PaymentModel
1112
Account *AccountModel
1213
}
1314

14-
func NewModels(db db.ConnectionPool) (*Models, error) {
15+
func NewModels(db db.ConnectionPool, metricsService metrics.MetricsService) (*Models, error) {
1516
if db == nil {
1617
return nil, errors.New("ConnectionPool must be initialized")
1718
}
1819

1920
return &Models{
20-
Payments: &PaymentModel{DB: db},
21-
Account: &AccountModel{DB: db},
21+
Payments: &PaymentModel{DB: db, MetricsService: metricsService},
22+
Account: &AccountModel{DB: db, MetricsService: metricsService},
2223
}, nil
2324
}

internal/data/payments.go

+23-4
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import (
88
"time"
99

1010
"github.com/stellar/wallet-backend/internal/db"
11+
"github.com/stellar/wallet-backend/internal/metrics"
1112
)
1213

1314
type PaymentModel struct {
14-
DB db.ConnectionPool
15+
DB db.ConnectionPool
16+
MetricsService metrics.MetricsService
1517
}
1618

1719
type Payment struct {
@@ -36,7 +38,11 @@ type Payment struct {
3638

3739
func (m *PaymentModel) GetLatestLedgerSynced(ctx context.Context, cursorName string) (uint32, error) {
3840
var lastSyncedLedger uint32
41+
start := time.Now()
3942
err := m.DB.GetContext(ctx, &lastSyncedLedger, `SELECT value FROM ingest_store WHERE key = $1`, cursorName)
43+
duration := time.Since(start).Seconds()
44+
m.MetricsService.ObserveDBQueryDuration("SELECT", "ingest_store", duration)
45+
m.MetricsService.IncDBQuery("SELECT", "ingest_store")
4046
// First run, key does not exist yet
4147
if err == sql.ErrNoRows {
4248
return 0, nil
@@ -53,10 +59,14 @@ func (m *PaymentModel) UpdateLatestLedgerSynced(ctx context.Context, cursorName
5359
INSERT INTO ingest_store (key, value) VALUES ($1, $2)
5460
ON CONFLICT (key) DO UPDATE SET value = excluded.value
5561
`
62+
start := time.Now()
5663
_, err := m.DB.ExecContext(ctx, query, cursorName, ledger)
64+
duration := time.Since(start).Seconds()
65+
m.MetricsService.ObserveDBQueryDuration("INSERT", "ingest_store", duration)
5766
if err != nil {
5867
return fmt.Errorf("updating last synced ledger to %d: %w", ledger, err)
5968
}
69+
m.MetricsService.IncDBQuery("INSERT", "ingest_store")
6070

6171
return nil
6272
}
@@ -90,12 +100,15 @@ func (m *PaymentModel) AddPayment(ctx context.Context, tx db.Transaction, paymen
90100
memo_type = EXCLUDED.memo_type
91101
;
92102
`
103+
start := time.Now()
93104
_, err := tx.ExecContext(ctx, query, payment.OperationID, payment.OperationType, payment.TransactionID, payment.TransactionHash, payment.FromAddress, payment.ToAddress, payment.SrcAssetCode, payment.SrcAssetIssuer, payment.SrcAssetType, payment.SrcAmount,
94105
payment.DestAssetCode, payment.DestAssetIssuer, payment.DestAssetType, payment.DestAmount, payment.CreatedAt, payment.Memo, payment.MemoType)
106+
duration := time.Since(start).Seconds()
107+
m.MetricsService.ObserveDBQueryDuration("INSERT", "ingest_payments", duration)
95108
if err != nil {
96109
return fmt.Errorf("inserting payment: %w", err)
97110
}
98-
111+
m.MetricsService.IncDBQuery("INSERT", "ingest_payments")
99112
return nil
100113
}
101114

@@ -142,16 +155,19 @@ func (m *PaymentModel) GetPaymentsPaginated(ctx context.Context, address string,
142155
if err != nil {
143156
return nil, false, false, fmt.Errorf("preparing named query: %w", err)
144157
}
158+
start := time.Now()
145159
err = m.DB.SelectContext(ctx, &payments, query, args...)
160+
duration := time.Since(start).Seconds()
161+
m.MetricsService.ObserveDBQueryDuration("SELECT", "ingest_payments", duration)
146162
if err != nil {
147163
return nil, false, false, fmt.Errorf("fetching payments: %w", err)
148164
}
165+
m.MetricsService.IncDBQuery("SELECT", "ingest_payments")
149166

150167
prevExists, nextExists, err := m.existsPrevNext(ctx, filteredSetCTE, address, sort, payments)
151168
if err != nil {
152169
return nil, false, false, fmt.Errorf("checking prev and next pages: %w", err)
153170
}
154-
155171
return payments, prevExists, nextExists, nil
156172
}
157173

@@ -187,11 +203,14 @@ func (m *PaymentModel) existsPrevNext(ctx context.Context, filteredSetCTE string
187203
}
188204

189205
var prevExists, nextExists bool
206+
start := time.Now()
190207
err = m.DB.QueryRowxContext(ctx, query, args...).Scan(&prevExists, &nextExists)
208+
duration := time.Since(start).Seconds()
209+
m.MetricsService.ObserveDBQueryDuration("SELECT", "ingest_payments", duration)
191210
if err != nil {
192211
return false, false, fmt.Errorf("fetching prev and next exists: %w", err)
193212
}
194-
213+
m.MetricsService.IncDBQuery("SELECT", "ingest_payments")
195214
return prevExists, nextExists, nil
196215
}
197216

0 commit comments

Comments
 (0)