erigon-pulse/diagnostics/db_access.go
Mark Holt a4cfbe0d56
Heimdall metrics + Metrics HTTP server rationalization (#8094)
This is an update of:

https://github.com/ledgerwatch/erigon/pull/7846

which uses a local fork of victoria metrics to include the changes that
https://github.com/anshalshukla added to the original for we where
using.

It also includes code to address the duplicate metrics issue identified
here:

https://github.com/ledgerwatch/erigon/issues/8053

It has one more associated fix which is to correctly add a metadata
label to counters, these where previously labelled as gauges.

e.g. 

```
# TYPE p2p_peers counter
p2p_peers 0
```
rather than

```
# TYPE p2p_peers gauge
p2p_peers 0
```

---------

Co-authored-by: Anshal Shukla <53994948+anshalshukla@users.noreply.github.com>
Co-authored-by: Anshal Shukla <shukla.anshal85@gmail.com>
2023-08-31 09:04:27 +01:00

140 lines
3.5 KiB
Go

package diagnostics
import (
"context"
"encoding/hex"
"fmt"
"io"
"net/http"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon-lib/kv/mdbx"
"github.com/ledgerwatch/erigon/common/paths"
"github.com/urfave/cli/v2"
)
func SetupDbAccess(ctx *cli.Context, metricsMux *http.ServeMux) {
var dataDir string
if ctx.IsSet("datadir") {
dataDir = ctx.String("datadir")
} else {
dataDir = paths.DataDirForNetwork(paths.DefaultDataDir(), ctx.String("chain"))
}
metricsMux.HandleFunc("/debug/metrics/db/list", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
writeDbList(w, dataDir)
})
metricsMux.HandleFunc("/debug/metrics/db/tables", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
writeDbTables(w, r, dataDir)
})
metricsMux.HandleFunc("/debug/metrics/db/read", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
writeDbRead(w, r, dataDir)
})
}
func writeDbList(w io.Writer, dataDir string) {
fmt.Fprintf(w, "SUCCESS\n")
m := mdbx.PathDbMap()
for path := range m {
fmt.Fprintf(w, "%s\n", path)
}
}
func writeDbTables(w io.Writer, r *http.Request, dataDir string) {
if err := r.ParseForm(); err != nil {
fmt.Fprintf(w, "ERROR: parsing arguments: %v\n", err)
return
}
path := r.Form.Get("path")
if path == "" {
fmt.Fprintf(w, "ERROR: path argument is required - specify the relative path to an MDBX database directory")
return
}
m := mdbx.PathDbMap()
db, ok := m[path]
if !ok {
fmt.Fprintf(w, "ERROR: path %s is not in the list of allowed paths", path)
return
}
var tables []string
if err := db.View(context.Background(), func(tx kv.Tx) error {
var e error
tables, e = tx.ListBuckets()
if e != nil {
return e
}
return nil
}); err != nil {
fmt.Fprintf(w, "ERROR: listing tables in %s: %v\n", path, err)
return
}
fmt.Fprintf(w, "SUCCESS\n")
for _, table := range tables {
fmt.Fprintf(w, "%s\n", table)
}
}
func writeDbRead(w io.Writer, r *http.Request, dataDir string) {
if err := r.ParseForm(); err != nil {
fmt.Fprintf(w, "ERROR: parsing arguments: %v\n", err)
return
}
path := r.Form.Get("path")
if path == "" {
fmt.Fprintf(w, "ERROR: path argument is required - specify the relative path to an MDBX database directory")
return
}
m := mdbx.PathDbMap()
db, ok := m[path]
if !ok {
fmt.Fprintf(w, "ERROR: path %s is not in the list of allowed paths", path)
return
}
table := r.Form.Get("table")
if table == "" {
fmt.Fprintf(w, "ERROR: table argument is required - specify the table to read from")
return
}
var key []byte
var err error
keyHex := r.Form.Get("key")
if keyHex != "" {
if key, err = hex.DecodeString(keyHex); err != nil {
fmt.Fprintf(w, "ERROR: key [%s] argument may only contain hexadecimal digits: %v\n", keyHex, err)
return
}
}
var results []string
if err := db.View(context.Background(), func(tx kv.Tx) error {
c, e := tx.Cursor(table)
if e != nil {
return e
}
defer c.Close()
var k, v []byte
if key == nil {
if k, v, e = c.First(); err != nil {
return e
}
} else if k, v, e = c.Seek(key); e != nil {
return e
}
count := 0
for e == nil && k != nil && count < 256 {
results = append(results, fmt.Sprintf("%x | %x", k, v))
count++
k, v, e = c.Next()
}
return nil
}); err != nil {
fmt.Fprintf(w, "ERROR: reading table %s in %s: %v\n", table, path, err)
return
}
fmt.Fprintf(w, "SUCCESS\n")
for _, result := range results {
fmt.Fprintf(w, "%s\n", result)
}
}