Skip to content

Commit 7be6fc1

Browse files
authored
new prometheus metrics for client-stats metrics (#8834)
1 parent 2fdfda2 commit 7be6fc1

File tree

18 files changed

+624
-214
lines changed

18 files changed

+624
-214
lines changed

beacon-chain/db/db.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ func NewDB(ctx context.Context, dirPath string, config *kv.Config) (Database, er
1414
return kv.NewKVStore(ctx, dirPath, config)
1515
}
1616

17+
// NewDBFilename uses the KVStoreDatafilePath so that if this layer of
18+
// indirection between db.NewDB->kv.NewKVStore ever changes, it will be easy to remember
19+
// to also change this filename indirection at the same time.
20+
func NewDBFilename(dirPath string) string {
21+
return kv.KVStoreDatafilePath(dirPath)
22+
}
23+
1724
// NewSlasherDB initializes a new DB for slasher.
1825
func NewSlasherDB(ctx context.Context, dirPath string, config *slasherkv.Config) (SlasherDatabase, error) {
1926
return slasherkv.NewKVStore(ctx, dirPath, config)

beacon-chain/db/kv/kv.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ type Store struct {
6464
ctx context.Context
6565
}
6666

67+
// KVStoreDatafilePath is the canonical construction of a full
68+
// database file path from the directory path, so that code outside
69+
// this package can find the full path in a consistent way.
70+
func KVStoreDatafilePath(dirPath string) string {
71+
return path.Join(dirPath, DatabaseFileName)
72+
}
73+
6774
// NewKVStore initializes a new boltDB key-value store at the directory
6875
// path specified, creates the kv-buckets based on the schema, and stores
6976
// an open connection db object as a property of the Store struct.
@@ -77,7 +84,7 @@ func NewKVStore(ctx context.Context, dirPath string, config *Config) (*Store, er
7784
return nil, err
7885
}
7986
}
80-
datafile := path.Join(dirPath, DatabaseFileName)
87+
datafile := KVStoreDatafilePath(dirPath)
8188
boltDB, err := bolt.Open(
8289
datafile,
8390
params.BeaconIoConfig().ReadWritePermissions,

beacon-chain/node/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ go_library(
77
"config.go",
88
"log.go",
99
"node.go",
10+
"prometheus.go",
1011
],
1112
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/node",
1213
visibility = [
@@ -48,6 +49,7 @@ go_library(
4849
"//shared/version:go_default_library",
4950
"@com_github_ethereum_go_ethereum//common:go_default_library",
5051
"@com_github_pkg_errors//:go_default_library",
52+
"@com_github_prometheus_client_golang//prometheus:go_default_library",
5153
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
5254
"@com_github_sirupsen_logrus//:go_default_library",
5355
"@com_github_urfave_cli_v2//:go_default_library",

beacon-chain/node/node.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ type BeaconNode struct {
7373
opFeed *event.Feed
7474
forkChoiceStore forkchoice.ForkChoicer
7575
stateGen *stategen.State
76+
collector *bcnodeCollector
7677
}
7778

7879
// New creates a new node instance, sets up configuration options, and registers
@@ -162,6 +163,15 @@ func New(cliCtx *cli.Context) (*BeaconNode, error) {
162163
}
163164
}
164165

166+
// db.DatabasePath is the path to the containing directory
167+
// db.NewDBFilename expands that to the canonical full path using
168+
// the same constuction as NewDB()
169+
c, err := newBeaconNodePromCollector(db.NewDBFilename(beacon.db.DatabasePath()))
170+
if err != nil {
171+
return nil, err
172+
}
173+
beacon.collector = c
174+
165175
return beacon, nil
166176
}
167177

@@ -224,6 +234,7 @@ func (b *BeaconNode) Close() {
224234
if err := b.db.Close(); err != nil {
225235
log.Errorf("Failed to close database: %v", err)
226236
}
237+
b.collector.unregister()
227238
b.cancel()
228239
close(b.stop)
229240
}
@@ -435,15 +446,22 @@ func (b *BeaconNode) registerPOWChainService() error {
435446
return err
436447
}
437448

449+
bs, err := powchain.NewPowchainCollector(b.ctx)
450+
if err != nil {
451+
return err
452+
}
453+
438454
cfg := &powchain.Web3ServiceConfig{
439-
HttpEndpoints: endpoints,
440-
DepositContract: common.HexToAddress(depAddress),
441-
BeaconDB: b.db,
442-
DepositCache: b.depositCache,
443-
StateNotifier: b,
444-
StateGen: b.stateGen,
445-
Eth1HeaderReqLimit: b.cliCtx.Uint64(flags.Eth1HeaderReqLimit.Name),
455+
HttpEndpoints: endpoints,
456+
DepositContract: common.HexToAddress(depAddress),
457+
BeaconDB: b.db,
458+
DepositCache: b.depositCache,
459+
StateNotifier: b,
460+
StateGen: b.stateGen,
461+
Eth1HeaderReqLimit: b.cliCtx.Uint64(flags.Eth1HeaderReqLimit.Name),
462+
BeaconNodeStatsUpdater: bs,
446463
}
464+
447465
web3Service, err := powchain.NewService(b.ctx, cfg)
448466
if err != nil {
449467
return errors.Wrap(err, "could not register proof-of-work chain web3Service")

beacon-chain/node/prometheus.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package node
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/prometheus/client_golang/prometheus"
8+
)
9+
10+
type bcnodeCollector struct {
11+
DiskBeaconchainBytesTotal *prometheus.Desc
12+
dbPath string
13+
}
14+
15+
func newBeaconNodePromCollector(dbPath string) (*bcnodeCollector, error) {
16+
namespace := "bcnode"
17+
c := &bcnodeCollector{
18+
DiskBeaconchainBytesTotal: prometheus.NewDesc(
19+
prometheus.BuildFQName(namespace, "", "disk_beaconchain_bytes_total"),
20+
"Total hard disk space used by the beaconchain database, in bytes.",
21+
nil,
22+
nil,
23+
),
24+
dbPath: dbPath,
25+
}
26+
_, err := c.getCurrentDbBytes()
27+
if err != nil {
28+
return nil, err
29+
}
30+
return c, prometheus.Register(c)
31+
}
32+
33+
func (bc *bcnodeCollector) Describe(ch chan<- *prometheus.Desc) {
34+
ch <- bc.DiskBeaconchainBytesTotal
35+
}
36+
37+
func (bc *bcnodeCollector) Collect(ch chan<- prometheus.Metric) {
38+
dbBytes, err := bc.getCurrentDbBytes()
39+
if err != nil {
40+
log.Warn(err)
41+
return
42+
}
43+
44+
ch <- prometheus.MustNewConstMetric(
45+
bc.DiskBeaconchainBytesTotal,
46+
prometheus.GaugeValue,
47+
dbBytes,
48+
)
49+
}
50+
51+
func (bc *bcnodeCollector) getCurrentDbBytes() (float64, error) {
52+
fs, err := os.Stat(bc.dbPath)
53+
if err != nil {
54+
return 0, fmt.Errorf("could not collect database file size for prometheus, path=%s, err=%s", bc.dbPath, err)
55+
}
56+
return float64(fs.Size()), nil
57+
}
58+
59+
func (bc *bcnodeCollector) unregister() {
60+
prometheus.Unregister(bc)
61+
}

beacon-chain/powchain/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ go_library(
99
"deposit.go",
1010
"log.go",
1111
"log_processing.go",
12+
"prometheus.go",
1213
"provider.go",
1314
"service.go",
1415
],
@@ -32,6 +33,7 @@ go_library(
3233
"//contracts/deposit-contract:go_default_library",
3334
"//proto/beacon/db:go_default_library",
3435
"//shared/bytesutil:go_default_library",
36+
"//shared/clientstats:go_default_library",
3537
"//shared/hashutil:go_default_library",
3638
"//shared/httputils:go_default_library",
3739
"//shared/httputils/authorizationmethod:go_default_library",
@@ -67,6 +69,7 @@ go_test(
6769
"init_test.go",
6870
"log_processing_test.go",
6971
"powchain_test.go",
72+
"prometheus_test.go",
7073
"provider_test.go",
7174
"service_test.go",
7275
],
@@ -84,6 +87,7 @@ go_test(
8487
"//proto/beacon/db:go_default_library",
8588
"//shared/bls:go_default_library",
8689
"//shared/bytesutil:go_default_library",
90+
"//shared/clientstats:go_default_library",
8791
"//shared/event:go_default_library",
8892
"//shared/httputils:go_default_library",
8993
"//shared/httputils/authorizationmethod:go_default_library",
@@ -98,6 +102,7 @@ go_test(
98102
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
99103
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
100104
"@com_github_ethereum_go_ethereum//trie:go_default_library",
105+
"@com_github_prometheus_client_golang//prometheus:go_default_library",
101106
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
102107
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
103108
"@com_github_sirupsen_logrus//:go_default_library",
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package powchain
2+
3+
import (
4+
"context"
5+
"sync"
6+
7+
"github.com/prometheus/client_golang/prometheus"
8+
"github.com/prysmaticlabs/prysm/shared/clientstats"
9+
)
10+
11+
type BeaconNodeStatsUpdater interface {
12+
Update(stats clientstats.BeaconNodeStats)
13+
}
14+
15+
type PowchainCollector struct {
16+
SyncEth1Connected *prometheus.Desc
17+
SyncEth1FallbackConnected *prometheus.Desc
18+
SyncEth1FallbackConfigured *prometheus.Desc // true if flag specified: --fallback-web3provider
19+
updateChan chan clientstats.BeaconNodeStats
20+
latestStats clientstats.BeaconNodeStats
21+
sync.Mutex
22+
ctx context.Context
23+
finishChan chan struct{}
24+
}
25+
26+
var _ BeaconNodeStatsUpdater = &PowchainCollector{}
27+
var _ prometheus.Collector = &PowchainCollector{}
28+
29+
// Update satisfies the BeaconNodeStatsUpdater
30+
func (pc *PowchainCollector) Update(update clientstats.BeaconNodeStats) {
31+
pc.updateChan <- update
32+
}
33+
34+
// Describe is invoked by the prometheus collection loop.
35+
// It returns a set of metric Descriptor references which
36+
// are also used in Collect to group collected metrics into
37+
// a family. Describe and Collect together satisfy the
38+
// prometheus.Collector interface.
39+
func (pc *PowchainCollector) Describe(ch chan<- *prometheus.Desc) {
40+
ch <- pc.SyncEth1Connected
41+
ch <- pc.SyncEth1FallbackConfigured
42+
ch <- pc.SyncEth1FallbackConnected
43+
}
44+
45+
// Collect is invoked by the prometheus collection loop.
46+
// It returns a set of Metrics representing the observation
47+
// for the current collection period. In the case of this
48+
// collector, we use values from the latest BeaconNodeStats
49+
// value sent by the powchain Service, which updates this value
50+
// whenever an internal event could change the state of one of
51+
// the metrics.
52+
// Describe and Collect together satisfy the
53+
// prometheus.Collector interface.
54+
func (pc *PowchainCollector) Collect(ch chan<- prometheus.Metric) {
55+
bs := pc.getLatestStats()
56+
57+
var syncEth1FallbackConfigured float64 = 0
58+
if bs.SyncEth1FallbackConfigured {
59+
syncEth1FallbackConfigured = 1
60+
}
61+
ch <- prometheus.MustNewConstMetric(
62+
pc.SyncEth1FallbackConfigured,
63+
prometheus.GaugeValue,
64+
syncEth1FallbackConfigured,
65+
)
66+
67+
var syncEth1FallbackConnected float64 = 0
68+
if bs.SyncEth1FallbackConnected {
69+
syncEth1FallbackConnected = 1
70+
}
71+
ch <- prometheus.MustNewConstMetric(
72+
pc.SyncEth1FallbackConnected,
73+
prometheus.GaugeValue,
74+
syncEth1FallbackConnected,
75+
)
76+
77+
var syncEth1Connected float64 = 0
78+
if bs.SyncEth1Connected {
79+
syncEth1Connected = 1
80+
}
81+
ch <- prometheus.MustNewConstMetric(
82+
pc.SyncEth1Connected,
83+
prometheus.GaugeValue,
84+
syncEth1Connected,
85+
)
86+
}
87+
88+
func (pc *PowchainCollector) getLatestStats() clientstats.BeaconNodeStats {
89+
pc.Lock()
90+
defer pc.Unlock()
91+
return pc.latestStats
92+
}
93+
94+
func (pc *PowchainCollector) setLatestStats(bs clientstats.BeaconNodeStats) {
95+
pc.Lock()
96+
pc.latestStats = bs
97+
pc.Unlock()
98+
}
99+
100+
// unregister returns true if the prometheus DefaultRegistry
101+
// confirms that it was removed.
102+
func (pc *PowchainCollector) unregister() bool {
103+
return prometheus.Unregister(pc)
104+
}
105+
106+
func (pc *PowchainCollector) latestStatsUpdateLoop() {
107+
for {
108+
select {
109+
case <-pc.ctx.Done():
110+
pc.unregister()
111+
pc.finishChan <- struct{}{}
112+
return
113+
case bs := <-pc.updateChan:
114+
pc.setLatestStats(bs)
115+
}
116+
}
117+
}
118+
119+
func NewPowchainCollector(ctx context.Context) (*PowchainCollector, error) {
120+
namespace := "powchain"
121+
updateChan := make(chan clientstats.BeaconNodeStats, 2)
122+
c := &PowchainCollector{
123+
SyncEth1FallbackConfigured: prometheus.NewDesc(
124+
prometheus.BuildFQName(namespace, "", "sync_eth1_fallback_configured"),
125+
"Boolean recording whether a fallback eth1 endpoint was configured: 0=false, 1=true.",
126+
nil,
127+
nil,
128+
),
129+
SyncEth1FallbackConnected: prometheus.NewDesc(
130+
prometheus.BuildFQName(namespace, "", "sync_eth1_fallback_connected"),
131+
"Boolean indicating whether a fallback eth1 endpoint is currently connected: 0=false, 1=true.",
132+
nil,
133+
nil,
134+
),
135+
SyncEth1Connected: prometheus.NewDesc(
136+
prometheus.BuildFQName(namespace, "", "sync_eth1_connected"),
137+
"Boolean indicating whether a fallback eth1 endpoint is currently connected: 0=false, 1=true.",
138+
nil,
139+
nil,
140+
),
141+
updateChan: updateChan,
142+
ctx: ctx,
143+
finishChan: make(chan struct{}, 1),
144+
}
145+
go c.latestStatsUpdateLoop()
146+
return c, prometheus.Register(c)
147+
}
148+
149+
type NopBeaconNodeStatsUpdater struct{}
150+
151+
func (nop *NopBeaconNodeStatsUpdater) Update(stats clientstats.BeaconNodeStats) {}
152+
153+
var _ BeaconNodeStatsUpdater = &NopBeaconNodeStatsUpdater{}

0 commit comments

Comments
 (0)