Add estimate fee and average block period prometheus metrics

This commit is contained in:
Martin Boehm 2022-12-10 00:22:52 +01:00 committed by Martin
parent aebc1c3495
commit 76a19b7f10
6 changed files with 94 additions and 25 deletions

View File

@ -2244,10 +2244,10 @@ type bitcoinTypeEstimatedFee struct {
lock sync.Mutex
}
const bitcoinTypeEstimatedFeeCacheSize = 300
const estimatedFeeCacheSize = 300
var bitcoinTypeEstimatedFeeCache [bitcoinTypeEstimatedFeeCacheSize]bitcoinTypeEstimatedFee
var bitcoinTypeEstimatedFeeConservativeCache [bitcoinTypeEstimatedFeeCacheSize]bitcoinTypeEstimatedFee
var estimatedFeeCache [estimatedFeeCacheSize]bitcoinTypeEstimatedFee
var estimatedFeeConservativeCache [estimatedFeeCacheSize]bitcoinTypeEstimatedFee
func (w *Worker) cachedBitcoinTypeEstimateFee(blocks int, conservative bool, s *bitcoinTypeEstimatedFee) (big.Int, error) {
s.lock.Lock()
@ -2261,18 +2261,22 @@ func (w *Worker) cachedBitcoinTypeEstimateFee(blocks int, conservative bool, s *
if err == nil {
s.timestamp = time.Now().Unix()
s.fee = fee
w.metrics.EstimatedFee.With(common.Labels{
"blocks": strconv.Itoa(blocks),
"conservative": strconv.FormatBool(conservative),
}).Set(float64(fee.Int64()))
}
return fee, err
}
// BitcoinTypeEstimateFee returns a fee estimation for given number of blocks
// EstimateFee returns a fee estimation for given number of blocks
// it uses 10 second cache to reduce calls to the backend
func (w *Worker) BitcoinTypeEstimateFee(blocks int, conservative bool) (big.Int, error) {
if blocks >= bitcoinTypeEstimatedFeeCacheSize {
func (w *Worker) EstimateFee(blocks int, conservative bool) (big.Int, error) {
if blocks >= estimatedFeeCacheSize {
return w.chain.EstimateSmartFee(blocks, conservative)
}
if conservative {
return w.cachedBitcoinTypeEstimateFee(blocks, conservative, &bitcoinTypeEstimatedFeeConservativeCache[blocks])
return w.cachedBitcoinTypeEstimateFee(blocks, conservative, &estimatedFeeConservativeCache[blocks])
}
return w.cachedBitcoinTypeEstimateFee(blocks, conservative, &bitcoinTypeEstimatedFeeCache[blocks])
return w.cachedBitcoinTypeEstimateFee(blocks, conservative, &estimatedFeeCache[blocks])
}

View File

@ -6,6 +6,8 @@ import (
"sync"
"sync/atomic"
"time"
"github.com/golang/glog"
)
const (
@ -68,6 +70,7 @@ type InternalState struct {
BestHeight uint32 `json:"bestHeight"`
LastSync time.Time `json:"lastSync"`
BlockTimes []uint32 `json:"-"`
AvgBlockPeriod uint32 `json:"-"`
IsMempoolSynchronized bool `json:"isMempoolSynchronized"`
MempoolSize int `json:"mempoolSize"`
@ -77,7 +80,6 @@ type InternalState struct {
UtxoChecked bool `json:"utxoChecked"`
// store only the historical state, not the current state of the fiat rates in DB
HasFiatRates bool `json:"-"`
HasTokenFiatRates bool `json:"-"`
HistoricalFiatRatesTime time.Time `json:"historicalFiatRatesTime"`
@ -208,11 +210,23 @@ func (is *InternalState) GetBlockTime(height uint32) uint32 {
return 0
}
// AppendBlockTime appends block time to BlockTimes
func (is *InternalState) AppendBlockTime(time uint32) {
// SetBlockTimes initializes BlockTimes array, returns AvgBlockPeriod
func (is *InternalState) SetBlockTimes(blockTimes []uint32) uint32 {
is.mux.Lock()
defer is.mux.Unlock()
is.BlockTimes = blockTimes
is.computeAvgBlockPeriod()
glog.Info("set ", len(is.BlockTimes), " block times, average block period ", is.AvgBlockPeriod, "s")
return is.AvgBlockPeriod
}
// AppendBlockTime appends block time to BlockTimes, returns AvgBlockPeriod
func (is *InternalState) AppendBlockTime(time uint32) uint32 {
is.mux.Lock()
defer is.mux.Unlock()
is.BlockTimes = append(is.BlockTimes, time)
is.computeAvgBlockPeriod()
return is.AvgBlockPeriod
}
// RemoveLastBlockTimes removes last times from BlockTimes
@ -223,6 +237,7 @@ func (is *InternalState) RemoveLastBlockTimes(count int) {
count = len(is.BlockTimes)
}
is.BlockTimes = is.BlockTimes[:len(is.BlockTimes)-count]
is.computeAvgBlockPeriod()
}
// GetBlockHeightOfTime returns block height of the first block with time greater or equal to the given time or MaxUint32 if no such block
@ -246,6 +261,25 @@ func (is *InternalState) GetBlockHeightOfTime(time uint32) uint32 {
return uint32(height)
}
const avgBlockPeriodSample = 100
// Avg100BlocksPeriod returns average period of the last 100 blocks in seconds
func (is *InternalState) GetAvgBlockPeriod() uint32 {
is.mux.Lock()
defer is.mux.Unlock()
return is.AvgBlockPeriod
}
// computeAvgBlockPeriod returns computes average of the last 100 blocks in seconds
func (is *InternalState) computeAvgBlockPeriod() {
last := len(is.BlockTimes) - 1
first := last - avgBlockPeriodSample - 1
if first < 0 {
return
}
is.AvgBlockPeriod = (is.BlockTimes[last] - is.BlockTimes[first]) / avgBlockPeriodSample
}
// SetBackendInfo sets new BackendInfo
func (is *InternalState) SetBackendInfo(bi *BackendInfo) {
is.mux.Lock()

View File

@ -24,6 +24,8 @@ type Metrics struct {
IndexDBSize prometheus.Gauge
ExplorerViews *prometheus.CounterVec
MempoolSize prometheus.Gauge
EstimatedFee *prometheus.GaugeVec
AvgBlockPeriod prometheus.Gauge
DbColumnRows *prometheus.GaugeVec
DbColumnSize *prometheus.GaugeVec
BlockbookAppInfo *prometheus.GaugeVec
@ -169,6 +171,21 @@ func GetMetrics(coin string) (*Metrics, error) {
ConstLabels: Labels{"coin": coin},
},
)
metrics.EstimatedFee = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "blockbook_estimated_fee",
Help: "Estimated fee per byte (gas) for number of blocks",
ConstLabels: Labels{"coin": coin},
},
[]string{"blocks", "conservative"},
)
metrics.AvgBlockPeriod = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "blockbook_avg_block_period",
Help: "Average period of mining of last 100 blocks in seconds",
ConstLabels: Labels{"coin": coin},
},
)
metrics.DbColumnRows = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "blockbook_dbcolumn_rows",
@ -209,7 +226,7 @@ func GetMetrics(coin string) (*Metrics, error) {
)
metrics.ExplorerPendingRequests = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "blockbook_explorer_pending_reqests",
Name: "blockbook_explorer_pending_requests",
Help: "Number of unfinished requests in explorer interface",
ConstLabels: Labels{"coin": coin},
},
@ -217,7 +234,7 @@ func GetMetrics(coin string) (*Metrics, error) {
)
metrics.WebsocketPendingRequests = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "blockbook_websocket_pending_reqests",
Name: "blockbook_websocket_pending_requests",
Help: "Number of unfinished requests in websocket interface",
ConstLabels: Labels{"coin": coin},
},
@ -225,7 +242,7 @@ func GetMetrics(coin string) (*Metrics, error) {
)
metrics.SocketIOPendingRequests = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "blockbook_socketio_pending_reqests",
Name: "blockbook_socketio_pending_requests",
Help: "Number of unfinished requests in socketio interface",
ConstLabels: Labels{"coin": coin},
},

View File

@ -403,11 +403,14 @@ func (b *BulkConnect) Close() error {
return err
}
}
var err error
b.d.is.BlockTimes, err = b.d.loadBlockTimes()
bt, err := b.d.loadBlockTimes()
if err != nil {
return err
}
avg := b.d.is.SetBlockTimes(bt)
if b.d.metrics != nil {
b.d.metrics.AvgBlockPeriod.Set(float64(avg))
}
if err := b.d.SetInconsistentState(false); err != nil {
return err

View File

@ -380,7 +380,10 @@ func (d *RocksDB) ConnectBlock(block *bchain.Block) error {
if err := d.WriteBatch(wb); err != nil {
return err
}
d.is.AppendBlockTime(uint32(block.Time))
avg := d.is.AppendBlockTime(uint32(block.Time))
if d.metrics != nil {
d.metrics.AvgBlockPeriod.Set(float64(avg))
}
return nil
}
@ -1631,7 +1634,6 @@ func (d *RocksDB) loadBlockTimes() ([]uint32, error) {
}
times = append(times, time)
}
glog.Info("loaded ", len(times), " block times")
return times, nil
}
@ -1699,10 +1701,15 @@ func (d *RocksDB) LoadInternalState(rpcCoin string) (*common.InternalState, erro
return nil, err
}
is.DbColumns = nc
is.BlockTimes, err = d.loadBlockTimes()
bt, err := d.loadBlockTimes()
if err != nil {
return nil, err
}
avg := is.SetBlockTimes(bt)
if d.metrics != nil {
d.metrics.AvgBlockPeriod.Set(float64(avg))
}
// after load, reset the synchronization data
is.IsSynchronized = false
is.IsMempoolSynchronized = false

View File

@ -638,11 +638,15 @@ func (s *WebsocketServer) estimateFee(c *websocketChannel, params []byte) (inter
return nil, err
}
sg := strconv.FormatUint(gas, 10)
for i, b := range r.Blocks {
fee, err := s.chain.EstimateSmartFee(b, true)
if err != nil {
return nil, err
}
b := 1
if len(r.Blocks) > 0 {
b = r.Blocks[0]
}
fee, err := s.api.EstimateFee(b, true)
if err != nil {
return nil, err
}
for i := range r.Blocks {
res[i].FeePerUnit = fee.String()
res[i].FeeLimit = sg
fee.Mul(&fee, new(big.Int).SetUint64(gas))
@ -666,7 +670,7 @@ func (s *WebsocketServer) estimateFee(c *websocketChannel, params []byte) (inter
}
}
for i, b := range r.Blocks {
fee, err := s.api.BitcoinTypeEstimateFee(b, conservative)
fee, err := s.api.EstimateFee(b, conservative)
if err != nil {
return nil, err
}