Skip to content
This repository has been archived by the owner on Apr 2, 2024. It is now read-only.

Commit

Permalink
feat(567): metrics collectors & trackers
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-4chain authored and jakubmkowalski committed Feb 12, 2024
1 parent a3f13d1 commit 470ab1e
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 7 deletions.
7 changes: 7 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/BuxOrg/bux/chainstate"
"github.com/BuxOrg/bux/cluster"
"github.com/BuxOrg/bux/logging"
"github.com/BuxOrg/bux/metrics"
"github.com/BuxOrg/bux/notifications"
"github.com/BuxOrg/bux/taskmanager"
"github.com/bitcoin-sv/go-paymail"
Expand Down Expand Up @@ -35,6 +36,7 @@ type (
httpClient HTTPInterface // HTTP interface to use
iuc bool // (Input UTXO Check) True will check input utxos when saving transactions
logger *zerolog.Logger // Internal logging
metrics *metrics.Metrics // Metrics with a collector interface
models *modelOptions // Configuration options for the loaded models
newRelic *newRelicOptions // Configuration options for NewRelic
notifications *notificationsOptions // Configuration options for Notifications
Expand Down Expand Up @@ -416,3 +418,8 @@ func (c *Client) UserAgent() string {
func (c *Client) Version() string {
return version
}

// Metrics will return the metrics client (if it's enabled)
func (c *Client) Metrics() (metrics *metrics.Metrics, enabled bool) {
return c.options.metrics, c.options.metrics != nil
}
1 change: 1 addition & 0 deletions client_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bux

import (
"context"

"github.com/BuxOrg/bux/chainstate"
"github.com/BuxOrg/bux/cluster"
"github.com/BuxOrg/bux/notifications"
Expand Down
14 changes: 14 additions & 0 deletions client_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/BuxOrg/bux/chainstate"
"github.com/BuxOrg/bux/cluster"
"github.com/BuxOrg/bux/logging"
"github.com/BuxOrg/bux/metrics"
"github.com/BuxOrg/bux/notifications"
"github.com/BuxOrg/bux/taskmanager"
"github.com/BuxOrg/bux/utils"
Expand Down Expand Up @@ -283,6 +284,19 @@ func WithLogger(customLogger *zerolog.Logger) ClientOps {
}
}

// -----------------------------------------------------------------
// METRICS
// -----------------------------------------------------------------

// WithMetrics will set the metrics with a collector interface
func WithMetrics(collector metrics.Collector) ClientOps {
return func(c *clientOptions) {
if collector != nil {
c.metrics = metrics.NewMetrics(collector)
}
}
}

// -----------------------------------------------------------------
// CACHESTORE
// -----------------------------------------------------------------
Expand Down
12 changes: 11 additions & 1 deletion cron_job_declarations.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const (
CronJobNameDraftTransactionCleanUp = "draft_transaction_clean_up"
CronJobNameSyncTransactionBroadcast = "sync_transaction_broadcast"
CronJobNameSyncTransactionSync = "sync_transaction_sync"
CronJobNameCalculateMetrics = "calculate_metrics"
)

type cronJobHandler func(ctx context.Context, client *Client) error
Expand All @@ -25,7 +26,7 @@ func (c *Client) cronJobs() taskmanager.CronJobs {
}
}

return taskmanager.CronJobs{
jobs := taskmanager.CronJobs{
CronJobNameDraftTransactionCleanUp: {
Period: 60 * time.Second,
Handler: handler(taskCleanupDraftTransactions),
Expand All @@ -39,4 +40,13 @@ func (c *Client) cronJobs() taskmanager.CronJobs {
Handler: handler(taskSyncTransactions),
},
}

if _, enabled := c.Metrics(); enabled {
jobs[CronJobNameCalculateMetrics] = taskmanager.CronJob{
Period: 15 * time.Second,
Handler: handler(taskCalculateMetrics),
}
}

return jobs
}
17 changes: 17 additions & 0 deletions cron_job_definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,20 @@ func taskSyncTransactions(ctx context.Context, client *Client) error {
}
return err
}

func taskCalculateMetrics(ctx context.Context, client *Client) error {
metrics, enabled := client.Metrics()
if !enabled {
return errors.New("metrics are not enabled")
}

modelOpts := client.DefaultModelOptions()

if xpubsCount, err := getXPubsCount(ctx, nil, nil, modelOpts...); err != nil {
client.options.logger.Error().Err(err).Msg("error getting xpubs count")
} else {
metrics.Stats.XPub.Set(float64(xpubsCount))
}

return nil
}
2 changes: 2 additions & 0 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/BuxOrg/bux/chainstate"
"github.com/BuxOrg/bux/cluster"
"github.com/BuxOrg/bux/metrics"
"github.com/BuxOrg/bux/notifications"
"github.com/BuxOrg/bux/taskmanager"
"github.com/bitcoin-sv/go-broadcast-client/broadcast"
Expand Down Expand Up @@ -186,4 +187,5 @@ type ClientInterface interface {
SetNotificationsClient(notifications.ClientInterface)
UserAgent() string
Version() string
Metrics() (metrics *metrics.Metrics, enabled bool)
}
22 changes: 22 additions & 0 deletions metrics/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package metrics

// Collector is an interface that is used to register metrics
type Collector interface {
RegisterGauge(name string) GaugeInterface
RegisterHistogramVec(name string, labels ...string) HistogramVecInterface
}

// GaugeInterface is an interface that is used to track gauges of values
type GaugeInterface interface {
Set(value float64)
}

// HistogramVecInterface is an interface that is used to register histograms with labels
type HistogramVecInterface interface {
WithLabelValues(lvs ...string) HistogramInterface
}

// HistogramInterface is an interface that is used to track histograms of values
type HistogramInterface interface {
Observe(value float64)
}
50 changes: 50 additions & 0 deletions metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Package metrics provides a way to track metrics in the application. Functionality is strictly tailored to the needs of the package and is not meant to be a general purpose metrics library.
*/
package metrics

import "time"

// Metrics is a struct that contains all the metrics that are used to track in the package
type Metrics struct {
collector Collector
Stats Stats
verifyMerkleRoots HistogramVecInterface
recordTransaction HistogramVecInterface
}

// NewMetrics is a constructor for the Metrics struct
func NewMetrics(collector Collector) *Metrics {
return &Metrics{
collector: collector,
Stats: registerStats(collector),
verifyMerkleRoots: collector.RegisterHistogramVec(verifyMerkleRootsHistogramName, "classification"),
recordTransaction: collector.RegisterHistogramVec(recordTransactionHistogramName, "classification", "strategy"),
}
}

// EndWithClassification is a function returned by Track* methods that should be called when the tracked operation is finished
type EndWithClassification func(success bool)

// TrackVerifyMerkleRoots is used to track the time it takes to verify merkle roots
func (m *Metrics) TrackVerifyMerkleRoots() EndWithClassification {
start := time.Now()
return func(success bool) {
m.verifyMerkleRoots.WithLabelValues(classify(success)).Observe(time.Since(start).Seconds())
}
}

// TrackRecordTransaction is used to track the time it takes to record a transaction
func (m *Metrics) TrackRecordTransaction(strategyName string) EndWithClassification {
start := time.Now()
return func(success bool) {
m.verifyMerkleRoots.WithLabelValues(classify(success), strategyName).Observe(time.Since(start).Seconds())
}
}

func classify(success bool) string {
if success {
return "success"
}
return "failure"
}
10 changes: 10 additions & 0 deletions metrics/naming.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package metrics

const domainPrefix = "bux_"

const (
verifyMerkleRootsHistogramName = domainPrefix + "verify_merkle_roots_histogram"
recordTransactionHistogramName = domainPrefix + "record_transaction_histogram"
)

const xpubGaugeName = domainPrefix + "xpub_gauge"
12 changes: 12 additions & 0 deletions metrics/stats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package metrics

// Stats is a struct that contains all the gauges that are used to track the calculated stats of the application
type Stats struct {
XPub GaugeInterface
}

func registerStats(collector Collector) Stats {
return Stats{
XPub: collector.RegisterGauge(xpubGaugeName),
}
}
18 changes: 14 additions & 4 deletions paymail_service_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"encoding/hex"
"fmt"
"reflect"

"github.com/BuxOrg/bux/chainstate"
"github.com/BuxOrg/bux/utils"
"github.com/bitcoin-sv/go-paymail"
Expand All @@ -12,7 +14,6 @@ import (
"github.com/bitcoin-sv/go-paymail/spv"
"github.com/bitcoinschema/go-bitcoin/v2"
"github.com/libsv/go-bk/bec"
"reflect"
)

// PaymailDefaultServiceProvider is an interface for overriding the paymail actions in go-paymail/server
Expand Down Expand Up @@ -190,15 +191,24 @@ func (p *PaymailDefaultServiceProvider) RecordTransaction(ctx context.Context,
func (p *PaymailDefaultServiceProvider) VerifyMerkleRoots(
ctx context.Context,
merkleRoots []*spv.MerkleRootConfirmationRequestItem,
) error {
request := make([]chainstate.MerkleRootConfirmationRequestItem, 0)
) (err error) {
if metrics, enabled := p.client.Metrics(); enabled {
end := metrics.TrackVerifyMerkleRoots()
defer func() {
success := err == nil
end(success)
}()
}

request := make([]chainstate.MerkleRootConfirmationRequestItem, 0, len(merkleRoots))
for _, m := range merkleRoots {
request = append(request, chainstate.MerkleRootConfirmationRequestItem{
MerkleRoot: m.MerkleRoot,
BlockHeight: m.BlockHeight,
})
}
return p.client.Chainstate().VerifyMerkleRoots(ctx, request)
err = p.client.Chainstate().VerifyMerkleRoots(ctx, request)
return
}

func (p *PaymailDefaultServiceProvider) createPaymailInformation(ctx context.Context, alias, domain string, opts ...ModelOps) (paymailAddress *PaymailAddress, pubKey *derivedPubKey, err error) {
Expand Down
14 changes: 12 additions & 2 deletions record_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,21 @@ type recordIncomingTxStrategy interface {
FailOnBroadcastError(forceFail bool)
}

func recordTransaction(ctx context.Context, c ClientInterface, strategy recordTxStrategy, opts ...ModelOps) (*Transaction, error) {
func recordTransaction(ctx context.Context, c ClientInterface, strategy recordTxStrategy, opts ...ModelOps) (transaction *Transaction, err error) {
if metrics, enabled := c.Metrics(); enabled {
strategyType := fmt.Sprintf("%T", strategy)
end := metrics.TrackRecordTransaction(strategyType)
defer func() {
success := err == nil
end(success)
}()
}

unlock := waitForRecordTxWriteLock(ctx, c, strategy.LockKey())
defer unlock()

return strategy.Execute(ctx, c, opts)
transaction, err = strategy.Execute(ctx, c, opts)
return
}

func getRecordTxStrategy(ctx context.Context, c ClientInterface, xPubKey, txHex, draftID string) (recordTxStrategy, error) {
Expand Down

0 comments on commit 470ab1e

Please sign in to comment.