Return state information for index request on internal http server

This commit is contained in:
Martin Boehm 2018-06-12 22:57:46 +02:00
parent d018164b37
commit d4cd2ad607
4 changed files with 79 additions and 33 deletions

View File

@ -234,7 +234,7 @@ func main() {
var httpServer *server.HTTPServer var httpServer *server.HTTPServer
if *httpServerBinding != "" { if *httpServerBinding != "" {
httpServer, err = server.NewHTTPServer(*httpServerBinding, *certFiles, index, chain, txCache) httpServer, err = server.NewHTTPServer(*httpServerBinding, *certFiles, index, chain, txCache, internalState)
if err != nil { if err != nil {
glog.Error("https: ", err) glog.Error("https: ", err)
return return
@ -424,7 +424,7 @@ func storeInternalStateLoop() {
lastCompute := time.Now() lastCompute := time.Now()
// randomize the duration between ComputeInternalStateColumnStats to avoid peaks after reboot of machine with multiple blockbooks // randomize the duration between ComputeInternalStateColumnStats to avoid peaks after reboot of machine with multiple blockbooks
computePeriod := 9*time.Hour + time.Duration(rand.Float64()*float64((2*time.Hour).Nanoseconds())) computePeriod := 9*time.Hour + time.Duration(rand.Float64()*float64((2*time.Hour).Nanoseconds()))
glog.Info("storeInternalStateLoop starting with internal state compute period ", computePeriod) glog.Info("storeInternalStateLoop starting with db stats recompute period ", computePeriod)
tickAndDebounce(storeInternalStatePeriodMs*time.Millisecond, (storeInternalStatePeriodMs-1)*time.Millisecond, chanStoreInternalState, func() { tickAndDebounce(storeInternalStatePeriodMs*time.Millisecond, (storeInternalStatePeriodMs-1)*time.Millisecond, chanStoreInternalState, func() {
if !computeRunning && lastCompute.Add(computePeriod).Before(time.Now()) { if !computeRunning && lastCompute.Add(computePeriod).Before(time.Now()) {
computeRunning = true computeRunning = true

View File

@ -15,11 +15,12 @@ const (
// InternalStateColumn contains the data of a db column // InternalStateColumn contains the data of a db column
type InternalStateColumn struct { type InternalStateColumn struct {
Name string `json:"name"` Name string `json:"name"`
Version uint32 `json:"version"` Version uint32 `json:"version"`
Rows int64 `json:"rows"` Rows int64 `json:"rows"`
KeyBytes int64 `json:"keysSum"` KeyBytes int64 `json:"keyBytes"`
ValueBytes int64 `json:"valuesSum"` ValueBytes int64 `json:"valueBytes"`
Updated time.Time `json:"updated"`
} }
// InternalState contains the data of the internal state // InternalState contains the data of the internal state
@ -91,29 +92,36 @@ func (is *InternalState) FinishedMempoolSync(mempoolSize int) {
} }
// GetMempoolSyncState gets the state of mempool synchronization // GetMempoolSyncState gets the state of mempool synchronization
func (is *InternalState) GetMempoolSyncState() (bool, time.Time) { func (is *InternalState) GetMempoolSyncState() (bool, time.Time, int) {
is.mux.Lock() is.mux.Lock()
defer is.mux.Unlock() defer is.mux.Unlock()
return is.IsMempoolSynchronized, is.LastMempoolSync return is.IsMempoolSynchronized, is.LastMempoolSync, is.MempoolSize
} }
// AddDBColumnStats adds differences in column statistics to column stats
func (is *InternalState) AddDBColumnStats(c int, rowsDiff int64, keyBytesDiff int64, valueBytesDiff int64) { func (is *InternalState) AddDBColumnStats(c int, rowsDiff int64, keyBytesDiff int64, valueBytesDiff int64) {
is.mux.Lock() is.mux.Lock()
defer is.mux.Unlock() defer is.mux.Unlock()
is.DbColumns[c].Rows += rowsDiff dc := &is.DbColumns[c]
is.DbColumns[c].KeyBytes += keyBytesDiff dc.Rows += rowsDiff
is.DbColumns[c].ValueBytes += valueBytesDiff dc.KeyBytes += keyBytesDiff
dc.ValueBytes += valueBytesDiff
dc.Updated = time.Now()
} }
// SetDBColumnStats sets new values of column stats
func (is *InternalState) SetDBColumnStats(c int, rows int64, keyBytes int64, valueBytes int64) { func (is *InternalState) SetDBColumnStats(c int, rows int64, keyBytes int64, valueBytes int64) {
is.mux.Lock() is.mux.Lock()
defer is.mux.Unlock() defer is.mux.Unlock()
is.DbColumns[c].Rows = rows dc := &is.DbColumns[c]
is.DbColumns[c].KeyBytes = keyBytes dc.Rows = rows
is.DbColumns[c].ValueBytes = valueBytes dc.KeyBytes = keyBytes
dc.ValueBytes = valueBytes
dc.Updated = time.Now()
} }
func (is *InternalState) GetDBColumnStats(c int) (int64, int64, int64) { // GetDBColumnStatValues gets stat values for given column
func (is *InternalState) GetDBColumnStatValues(c int) (int64, int64, int64) {
is.mux.Lock() is.mux.Lock()
defer is.mux.Unlock() defer is.mux.Unlock()
if c < len(is.DbColumns) { if c < len(is.DbColumns) {
@ -122,6 +130,16 @@ func (is *InternalState) GetDBColumnStats(c int) (int64, int64, int64) {
return 0, 0, 0 return 0, 0, 0
} }
// GetAllDBColumnStats returns stats for all columns
func (is *InternalState) GetAllDBColumnStats() []InternalStateColumn {
is.mux.Lock()
defer is.mux.Unlock()
rv := make([]InternalStateColumn, len(is.DbColumns))
copy(rv, is.DbColumns)
return rv
}
// DBSizeTotal sums the computed sizes of all columns
func (is *InternalState) DBSizeTotal() int64 { func (is *InternalState) DBSizeTotal() int64 {
is.mux.Lock() is.mux.Lock()
defer is.mux.Unlock() defer is.mux.Unlock()
@ -132,6 +150,7 @@ func (is *InternalState) DBSizeTotal() int64 {
return total return total
} }
// Pack marshals internal state to json
func (is *InternalState) Pack() ([]byte, error) { func (is *InternalState) Pack() ([]byte, error) {
is.mux.Lock() is.mux.Lock()
defer is.mux.Unlock() defer is.mux.Unlock()
@ -139,6 +158,7 @@ func (is *InternalState) Pack() ([]byte, error) {
return json.Marshal(is) return json.Marshal(is)
} }
// UnpackInternalState unmarshals internal state from json
func UnpackInternalState(buf []byte) (*InternalState, error) { func UnpackInternalState(buf []byte) (*InternalState, error) {
var is InternalState var is InternalState
if err := json.Unmarshal(buf, &is); err != nil { if err := json.Unmarshal(buf, &is); err != nil {

View File

@ -934,6 +934,7 @@ func (d *RocksDB) LoadInternalState(rpcCoin string) (*common.InternalState, erro
nc[i].Rows = sc[j].Rows nc[i].Rows = sc[j].Rows
nc[i].KeyBytes = sc[j].KeyBytes nc[i].KeyBytes = sc[j].KeyBytes
nc[i].ValueBytes = sc[j].ValueBytes nc[i].ValueBytes = sc[j].ValueBytes
nc[i].Updated = sc[j].Updated
break break
} }
} }
@ -950,7 +951,7 @@ func (d *RocksDB) SetInternalState(is *common.InternalState) {
// StoreInternalState stores the internal state to db // StoreInternalState stores the internal state to db
func (d *RocksDB) StoreInternalState(is *common.InternalState) error { func (d *RocksDB) StoreInternalState(is *common.InternalState) error {
for c := 0; c < len(cfNames); c++ { for c := 0; c < len(cfNames); c++ {
rows, keyBytes, valueBytes := d.is.GetDBColumnStats(c) rows, keyBytes, valueBytes := d.is.GetDBColumnStatValues(c)
d.metrics.DbColumnRows.With(common.Labels{"column": cfNames[c]}).Set(float64(rows)) d.metrics.DbColumnRows.With(common.Labels{"column": cfNames[c]}).Set(float64(rows))
d.metrics.DbColumnSize.With(common.Labels{"column": cfNames[c]}).Set(float64(keyBytes + valueBytes)) d.metrics.DbColumnSize.With(common.Labels{"column": cfNames[c]}).Set(float64(keyBytes + valueBytes))
} }

View File

@ -2,6 +2,7 @@ package server
import ( import (
"blockbook/bchain" "blockbook/bchain"
"blockbook/common"
"blockbook/db" "blockbook/db"
"context" "context"
"encoding/json" "encoding/json"
@ -9,6 +10,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
"time"
"github.com/golang/glog" "github.com/golang/glog"
@ -24,10 +26,26 @@ type HTTPServer struct {
txCache *db.TxCache txCache *db.TxCache
chain bchain.BlockChain chain bchain.BlockChain
chainParser bchain.BlockChainParser chainParser bchain.BlockChainParser
is *common.InternalState
}
type resAboutBlockbookInternal struct {
Coin string `json:"coin"`
Host string `json:"host"`
Version string `json:"version"`
GitCommit string `json:"gitcommit"`
BuildTime string `json:"buildtime"`
InSync bool `json:"inSync"`
BestHeight uint32 `json:"bestHeight"`
LastBlockTime time.Time `json:"lastBlockTime"`
InSyncMempool bool `json:"inSyncMempool"`
LastMempoolTime time.Time `json:"lastMempoolTime"`
MempoolSize int `json:"mempoolSize"`
DbColumns []common.InternalStateColumn `json:"dbColumns"`
} }
// NewHTTPServer creates new REST interface to blockbook and returns its handle // NewHTTPServer creates new REST interface to blockbook and returns its handle
func NewHTTPServer(httpServerBinding string, certFiles string, db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache) (*HTTPServer, error) { func NewHTTPServer(httpServerBinding string, certFiles string, db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is *common.InternalState) (*HTTPServer, error) {
r := mux.NewRouter() r := mux.NewRouter()
https := &http.Server{ https := &http.Server{
Addr: httpServerBinding, Addr: httpServerBinding,
@ -40,9 +58,10 @@ func NewHTTPServer(httpServerBinding string, certFiles string, db *db.RocksDB, c
txCache: txCache, txCache: txCache,
chain: chain, chain: chain,
chainParser: chain.GetChainParser(), chainParser: chain.GetChainParser(),
is: is,
} }
r.HandleFunc("/", s.info) r.HandleFunc("/", s.index)
r.HandleFunc("/bestBlockHash", s.bestBlockHash) r.HandleFunc("/bestBlockHash", s.bestBlockHash)
r.HandleFunc("/blockHash/{height}", s.blockHash) r.HandleFunc("/blockHash/{height}", s.blockHash)
r.HandleFunc("/transactions/{address}/{lower}/{higher}", s.transactions) r.HandleFunc("/transactions/{address}/{lower}/{higher}", s.transactions)
@ -89,23 +108,29 @@ func respondHashData(w http.ResponseWriter, hash string) {
}) })
} }
func (s *HTTPServer) info(w http.ResponseWriter, r *http.Request) { func (s *HTTPServer) index(w http.ResponseWriter, r *http.Request) {
type info struct { vi := common.GetVersionInfo()
Version string `json:"version"` ss, bh, st := s.is.GetSyncState()
BestBlockHeight uint32 `json:"bestBlockHeight"` ms, mt, msz := s.is.GetMempoolSyncState()
BestBlockHash string `json:"bestBlockHash"` a := resAboutBlockbookInternal{
Coin: s.is.Coin,
Host: s.is.Host,
Version: vi.Version,
GitCommit: vi.GitCommit,
BuildTime: vi.BuildTime,
InSync: ss,
BestHeight: bh,
LastBlockTime: st,
InSyncMempool: ms,
LastMempoolTime: mt,
MempoolSize: msz,
DbColumns: s.is.GetAllDBColumnStats(),
} }
buf, err := json.MarshalIndent(a, "", " ")
height, hash, err := s.db.GetBestBlock()
if err != nil { if err != nil {
glog.Errorf("https info: %v", err) glog.Error(err)
} }
w.Write(buf)
json.NewEncoder(w).Encode(info{
Version: "0.0.1",
BestBlockHeight: height,
BestBlockHash: hash,
})
} }
func (s *HTTPServer) bestBlockHash(w http.ResponseWriter, r *http.Request) { func (s *HTTPServer) bestBlockHash(w http.ResponseWriter, r *http.Request) {