[#645] metrics: Add badgerstore metrics

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
Dmitrii Stepanov 2023-10-26 14:31:15 +03:00
parent 6cbea91c06
commit 588113b7d6
14 changed files with 300 additions and 8 deletions

View file

@ -889,6 +889,11 @@ func (c *cfg) getSubstorageOpts(shCfg shardCfg) []blobstor.SubStorage {
badgerstore.WithMemTablesCount(sRead.memTablesCount), badgerstore.WithMemTablesCount(sRead.memTablesCount),
badgerstore.WithValueLogSize(sRead.valueLogFileSize), badgerstore.WithValueLogSize(sRead.valueLogFileSize),
} }
if c.metricsCollector != nil {
badgerStoreOpts = append(badgerStoreOpts,
badgerstore.WithMetrics(
lsmetrics.NewBadgerStoreMetrics(sRead.path, c.metricsCollector.BadgerStoreMetrics())))
}
ss = append(ss, blobstor.SubStorage{ ss = append(ss, blobstor.SubStorage{
Storage: badgerstore.New(badgerStoreOpts...), Storage: badgerstore.New(badgerStoreOpts...),
Policy: func(_ *objectSDK.Object, data []byte) bool { Policy: func(_ *objectSDK.Object, data []byte) bool {

View file

@ -16,6 +16,7 @@ type cfg struct {
db badger.Options db badger.Options
gcTimeout time.Duration gcTimeout time.Duration
gcDiscardRatio float64 gcDiscardRatio float64
metrics Metrics
} }
type Option func(*cfg) type Option func(*cfg)
@ -55,6 +56,7 @@ func defaultCfg() *cfg {
db: opts, db: opts,
gcTimeout: 10 * time.Minute, gcTimeout: 10 * time.Minute,
gcDiscardRatio: 0.2, // for 1GB vLog file GC will perform only if around 200MB could be free gcDiscardRatio: 0.2, // for 1GB vLog file GC will perform only if around 200MB could be free
metrics: &noopMetrics{},
} }
} }
@ -114,3 +116,10 @@ func WithValueLogSize(sz int64) Option {
c.db.ValueLogFileSize = sz c.db.ValueLogFileSize = sz
} }
} }
// WithMetrics sets metrics.
func WithMetrics(m Metrics) Option {
return func(c *cfg) {
c.metrics = m
}
}

View file

@ -27,6 +27,7 @@ func (s *Store) Close() error {
return err return err
} }
s.opened = false s.opened = false
s.cfg.metrics.Close()
return nil return nil
} }
@ -83,6 +84,7 @@ func (s *Store) Open(readOnly bool) error {
return err return err
} }
s.opened = true s.opened = true
s.cfg.metrics.SetMode(readOnly)
return nil return nil
} }

View file

@ -2,6 +2,7 @@ package badgerstore
import ( import (
"context" "context"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
@ -14,6 +15,13 @@ import (
// Delete implements common.Storage. // Delete implements common.Storage.
func (s *Store) Delete(ctx context.Context, prm common.DeletePrm) (common.DeleteRes, error) { func (s *Store) Delete(ctx context.Context, prm common.DeletePrm) (common.DeleteRes, error) {
success := false
startedAt := time.Now()
defer func() {
s.cfg.metrics.Delete(time.Since(startedAt), success)
}()
_, span := tracing.StartSpanFromContext(ctx, "BadgerStore.Delete", _, span := tracing.StartSpanFromContext(ctx, "BadgerStore.Delete",
trace.WithAttributes( trace.WithAttributes(
attribute.String("path", s.cfg.db.Dir), attribute.String("path", s.cfg.db.Dir),
@ -36,9 +44,12 @@ func (s *Store) Delete(ctx context.Context, prm common.DeletePrm) (common.Delete
return common.DeleteRes{}, err return common.DeleteRes{}, err
} }
err = tx.Delete(key(prm.Address)) if err = tx.Delete(key(prm.Address)); err != nil {
if err != nil {
return common.DeleteRes{}, err return common.DeleteRes{}, err
} }
return common.DeleteRes{}, tx.Commit() if err = tx.Commit(); err != nil {
return common.DeleteRes{}, err
}
success = true
return common.DeleteRes{}, nil
} }

View file

@ -3,6 +3,7 @@ package badgerstore
import ( import (
"context" "context"
"encoding/hex" "encoding/hex"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing" "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
@ -13,6 +14,13 @@ import (
// Exists implements common.Storage. // Exists implements common.Storage.
func (s *Store) Exists(ctx context.Context, prm common.ExistsPrm) (common.ExistsRes, error) { func (s *Store) Exists(ctx context.Context, prm common.ExistsPrm) (common.ExistsRes, error) {
success := false
startedAt := time.Now()
defer func() {
s.cfg.metrics.Exists(time.Since(startedAt), success)
}()
_, span := tracing.StartSpanFromContext(ctx, "BadgerStore.Exists", _, span := tracing.StartSpanFromContext(ctx, "BadgerStore.Exists",
trace.WithAttributes( trace.WithAttributes(
attribute.String("path", s.cfg.db.Dir), attribute.String("path", s.cfg.db.Dir),
@ -27,10 +35,12 @@ func (s *Store) Exists(ctx context.Context, prm common.ExistsPrm) (common.Exists
_, err := tx.Get(key(prm.Address)) _, err := tx.Get(key(prm.Address))
if err != nil { if err != nil {
if err == badger.ErrKeyNotFound { if err == badger.ErrKeyNotFound {
success = true
return common.ExistsRes{Exists: false}, nil return common.ExistsRes{Exists: false}, nil
} }
return common.ExistsRes{}, err return common.ExistsRes{}, err
} }
success = true
return common.ExistsRes{Exists: true}, nil return common.ExistsRes{Exists: true}, nil
} }

View file

@ -5,6 +5,7 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"strconv" "strconv"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
@ -19,6 +20,14 @@ import (
// Get implements common.Storage. // Get implements common.Storage.
func (s *Store) Get(ctx context.Context, prm common.GetPrm) (common.GetRes, error) { func (s *Store) Get(ctx context.Context, prm common.GetPrm) (common.GetRes, error) {
success := false
size := 0
startedAt := time.Now()
defer func() {
s.cfg.metrics.Get(time.Since(startedAt), size, success)
}()
_, span := tracing.StartSpanFromContext(ctx, "BadgerStore.Get", _, span := tracing.StartSpanFromContext(ctx, "BadgerStore.Get",
trace.WithAttributes( trace.WithAttributes(
attribute.String("path", s.cfg.db.Dir), attribute.String("path", s.cfg.db.Dir),
@ -43,11 +52,21 @@ func (s *Store) Get(ctx context.Context, prm common.GetPrm) (common.GetRes, erro
return common.GetRes{}, fmt.Errorf("could not unmarshal the object: %w", err) return common.GetRes{}, fmt.Errorf("could not unmarshal the object: %w", err)
} }
success = true
size = len(data)
return common.GetRes{Object: obj, RawData: data}, nil return common.GetRes{Object: obj, RawData: data}, nil
} }
// GetRange implements common.Storage. // GetRange implements common.Storage.
func (s *Store) GetRange(ctx context.Context, prm common.GetRangePrm) (common.GetRangeRes, error) { func (s *Store) GetRange(ctx context.Context, prm common.GetRangePrm) (common.GetRangeRes, error) {
success := false
size := 0
startedAt := time.Now()
defer func() {
s.cfg.metrics.GetRange(time.Since(startedAt), size, success)
}()
_, span := tracing.StartSpanFromContext(ctx, "BadgerStore.GetRange", _, span := tracing.StartSpanFromContext(ctx, "BadgerStore.GetRange",
trace.WithAttributes( trace.WithAttributes(
attribute.String("path", s.cfg.db.Dir), attribute.String("path", s.cfg.db.Dir),
@ -81,9 +100,10 @@ func (s *Store) GetRange(ctx context.Context, prm common.GetRangePrm) (common.Ge
return common.GetRangeRes{}, logicerr.Wrap(new(apistatus.ObjectOutOfRange)) return common.GetRangeRes{}, logicerr.Wrap(new(apistatus.ObjectOutOfRange))
} }
return common.GetRangeRes{ res := common.GetRangeRes{Data: payload[from:to]}
Data: payload[from:to], success = true
}, nil size = len(res.Data)
return res, nil
} }
func (s *Store) getObjectData(addr oid.Address) ([]byte, error) { func (s *Store) getObjectData(addr oid.Address) ([]byte, error) {

View file

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing" "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
@ -14,6 +15,13 @@ import (
// Iterate implements common.Storage. // Iterate implements common.Storage.
func (s *Store) Iterate(ctx context.Context, prm common.IteratePrm) (common.IterateRes, error) { func (s *Store) Iterate(ctx context.Context, prm common.IteratePrm) (common.IterateRes, error) {
success := false
startedAt := time.Now()
defer func() {
s.cfg.metrics.Iterate(time.Since(startedAt), success)
}()
_, span := tracing.StartSpanFromContext(ctx, "BadgerStore.Iterate", _, span := tracing.StartSpanFromContext(ctx, "BadgerStore.Iterate",
trace.WithAttributes( trace.WithAttributes(
attribute.String("path", s.cfg.db.Dir), attribute.String("path", s.cfg.db.Dir),
@ -78,6 +86,7 @@ func (s *Store) Iterate(ctx context.Context, prm common.IteratePrm) (common.Iter
} }
} }
success = true
return common.IterateRes{}, nil return common.IterateRes{}, nil
} }

View file

@ -0,0 +1,31 @@
package badgerstore
import "time"
type Metrics interface {
SetParentID(parentID string)
SetMode(readOnly bool)
Close()
Delete(d time.Duration, success bool)
Exists(d time.Duration, success bool)
GetRange(d time.Duration, size int, success bool)
Get(d time.Duration, size int, success bool)
Iterate(d time.Duration, success bool)
Put(d time.Duration, size int, success bool)
}
var _ Metrics = (*noopMetrics)(nil)
type noopMetrics struct{}
func (*noopMetrics) Close() {}
func (*noopMetrics) Delete(time.Duration, bool) {}
func (*noopMetrics) Exists(time.Duration, bool) {}
func (*noopMetrics) Get(time.Duration, int, bool) {}
func (*noopMetrics) GetRange(time.Duration, int, bool) {}
func (*noopMetrics) Iterate(time.Duration, bool) {}
func (*noopMetrics) Put(time.Duration, int, bool) {}
func (*noopMetrics) SetMode(bool) {}
func (*noopMetrics) SetParentID(string) {}

View file

@ -2,6 +2,7 @@ package badgerstore
import ( import (
"context" "context"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing" "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
@ -11,6 +12,14 @@ import (
// Put implements common.Storage. // Put implements common.Storage.
func (s *Store) Put(ctx context.Context, prm common.PutPrm) (common.PutRes, error) { func (s *Store) Put(ctx context.Context, prm common.PutPrm) (common.PutRes, error) {
success := false
size := 0
startedAt := time.Now()
defer func() {
s.cfg.metrics.Put(time.Since(startedAt), size, success)
}()
_, span := tracing.StartSpanFromContext(ctx, "BadgerStore.Put", _, span := tracing.StartSpanFromContext(ctx, "BadgerStore.Put",
trace.WithAttributes( trace.WithAttributes(
attribute.String("path", s.cfg.db.Dir), attribute.String("path", s.cfg.db.Dir),
@ -34,5 +43,10 @@ func (s *Store) Put(ctx context.Context, prm common.PutPrm) (common.PutRes, erro
if err != nil { if err != nil {
return common.PutRes{}, err return common.PutRes{}, err
} }
return common.PutRes{}, b.Flush() if err = b.Flush(); err != nil {
return common.PutRes{}, err
}
success = true
size = len(prm.RawData)
return common.PutRes{}, nil
} }

View file

@ -55,7 +55,9 @@ func (s *Store) SetCompressor(cc *compression.Config) {
} }
// SetParentID implements common.Storage. // SetParentID implements common.Storage.
func (*Store) SetParentID(parentID string) {} func (s *Store) SetParentID(parentID string) {
s.cfg.metrics.SetParentID(parentID)
}
// SetReportErrorFunc implements common.Storage. // SetReportErrorFunc implements common.Storage.
func (*Store) SetReportErrorFunc(func(string, error)) {} func (*Store) SetReportErrorFunc(func(string, error)) {}

View file

@ -0,0 +1,74 @@
package metrics
import (
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/badgerstore"
metrics_impl "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/metrics"
)
func NewBadgerStoreMetrics(path string, m metrics_impl.BadgerStoreMetrics) badgerstore.Metrics {
return &badgerStoreMetrics{
path: path,
m: m,
}
}
type badgerStoreMetrics struct {
path, shardID string
m metrics_impl.BadgerStoreMetrics
}
// Close implements badgerstore.Metrics.
func (m *badgerStoreMetrics) Close() {
m.m.Close(m.shardID, m.path)
}
// Delete implements badgerstore.Metrics.
func (m *badgerStoreMetrics) Delete(d time.Duration, success bool) {
m.m.MethodDuration(m.shardID, m.path, "Delete", d, success)
}
// Exists implements badgerstore.Metrics.
func (m *badgerStoreMetrics) Exists(d time.Duration, success bool) {
m.m.MethodDuration(m.shardID, m.path, "Exists", d, success)
}
// Get implements badgerstore.Metrics.
func (m *badgerStoreMetrics) Get(d time.Duration, size int, success bool) {
m.m.MethodDuration(m.shardID, m.path, "Get", d, success)
if success {
m.m.AddGet(m.shardID, m.path, size)
}
}
// GetRange implements badgerstore.Metrics.
func (m *badgerStoreMetrics) GetRange(d time.Duration, size int, success bool) {
m.m.MethodDuration(m.shardID, m.path, "GetRange", d, success)
if success {
m.m.AddGet(m.shardID, m.path, size)
}
}
// Iterate implements badgerstore.Metrics.
func (m *badgerStoreMetrics) Iterate(d time.Duration, success bool) {
m.m.MethodDuration(m.shardID, m.path, "Iterate", d, success)
}
// Put implements badgerstore.Metrics.
func (m *badgerStoreMetrics) Put(d time.Duration, size int, success bool) {
m.m.MethodDuration(m.shardID, m.path, "Put", d, success)
if success {
m.m.AddPut(m.shardID, m.path, size)
}
}
// SetMode implements badgerstore.Metrics.
func (m *badgerStoreMetrics) SetMode(readOnly bool) {
m.m.SetMode(m.shardID, m.path, readOnly)
}
// SetParentID implements badgerstore.Metrics.
func (m *badgerStoreMetrics) SetParentID(parentID string) {
m.shardID = parentID
}

View file

@ -0,0 +1,98 @@
package metrics
import (
"strconv"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-observability/metrics"
"github.com/prometheus/client_golang/prometheus"
)
type BadgerStoreMetrics interface {
SetMode(shardID, path string, readOnly bool)
Close(shardID, path string)
MethodDuration(shardID, path string, method string, d time.Duration, success bool)
AddPut(shardID, path string, size int)
AddGet(shardID, path string, size int)
}
var _ BadgerStoreMetrics = (*badgerStoreMetrics)(nil)
type badgerStoreMetrics struct {
mode *shardIDPathModeValue
reqDuration *prometheus.HistogramVec
put *prometheus.CounterVec
get *prometheus.CounterVec
}
func newbadgerStoreMetrics() *badgerStoreMetrics {
return &badgerStoreMetrics{
mode: newShardIDPathMode(badgerStoreSubSystem, "mode", "BadgerStore mode"),
reqDuration: metrics.NewHistogramVec(prometheus.HistogramOpts{
Namespace: namespace,
Subsystem: badgerStoreSubSystem,
Name: "request_duration_seconds",
Help: "Accumulated BadgerStore request process duration",
}, []string{shardIDLabel, pathLabel, successLabel, methodLabel}),
put: metrics.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: badgerStoreSubSystem,
Name: "put_bytes",
Help: "Accumulated payload size written to BadgerStore",
}, []string{shardIDLabel, pathLabel}),
get: metrics.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: badgerStoreSubSystem,
Name: "get_bytes",
Help: "Accumulated payload size read from BadgerStore",
}, []string{shardIDLabel, pathLabel}),
}
}
// AddGet implements BadgerStoreMetrics.
func (b *badgerStoreMetrics) AddGet(shardID string, path string, size int) {
b.get.With(prometheus.Labels{
shardIDLabel: shardID,
pathLabel: path,
}).Add(float64(size))
}
// AddPut implements BadgerStoreMetrics.
func (b *badgerStoreMetrics) AddPut(shardID string, path string, size int) {
b.put.With(prometheus.Labels{
shardIDLabel: shardID,
pathLabel: path,
}).Add(float64(size))
}
// Close implements BadgerStoreMetrics.
func (b *badgerStoreMetrics) Close(shardID string, path string) {
b.mode.SetMode(shardID, path, closedMode)
b.reqDuration.DeletePartialMatch(prometheus.Labels{
shardIDLabel: shardID,
pathLabel: path,
})
b.get.DeletePartialMatch(prometheus.Labels{
shardIDLabel: shardID,
pathLabel: path,
})
b.put.DeletePartialMatch(prometheus.Labels{
shardIDLabel: shardID,
pathLabel: path,
})
}
// MethodDuration implements BadgerStoreMetrics.
func (b *badgerStoreMetrics) MethodDuration(shardID string, path string, method string, d time.Duration, success bool) {
b.reqDuration.With(prometheus.Labels{
shardIDLabel: shardID,
pathLabel: path,
successLabel: strconv.FormatBool(success),
methodLabel: method,
}).Observe(d.Seconds())
}
// SetMode implements BadgerStoreMetrics.
func (b *badgerStoreMetrics) SetMode(shardID string, path string, readOnly bool) {
b.mode.SetMode(shardID, path, modeFromBool(readOnly))
}

View file

@ -22,6 +22,7 @@ const (
writeCacheSubsystem = "writecache" writeCacheSubsystem = "writecache"
grpcServerSubsystem = "grpc_server" grpcServerSubsystem = "grpc_server"
policerSubsystem = "policer" policerSubsystem = "policer"
badgerStoreSubSystem = "badgerstore"
successLabel = "success" successLabel = "success"
shardIDLabel = "shard_id" shardIDLabel = "shard_id"

View file

@ -21,6 +21,7 @@ type NodeMetrics struct {
pilorama *piloramaMetrics pilorama *piloramaMetrics
grpc *grpcServerMetrics grpc *grpcServerMetrics
blobTree *blobTreeMetrics blobTree *blobTreeMetrics
badgerStore *badgerStoreMetrics
policer *policerMetrics policer *policerMetrics
morphClient *morphClientMetrics morphClient *morphClientMetrics
morphCache *morphCacheMetrics morphCache *morphCacheMetrics
@ -51,6 +52,7 @@ func NewNodeMetrics() *NodeMetrics {
morphCache: newMorphCacheMetrics(namespace), morphCache: newMorphCacheMetrics(namespace),
log: logger.NewLogMetrics(namespace), log: logger.NewLogMetrics(namespace),
blobTree: newBlobTreeMetrics(), blobTree: newBlobTreeMetrics(),
badgerStore: newbadgerStoreMetrics(),
} }
} }
@ -122,3 +124,7 @@ func (m *NodeMetrics) LogMetrics() logger.LogMetrics {
func (m *NodeMetrics) BlobTreeMetrics() BlobTreeMetrics { func (m *NodeMetrics) BlobTreeMetrics() BlobTreeMetrics {
return m.blobTree return m.blobTree
} }
func (m *NodeMetrics) BadgerStoreMetrics() BadgerStoreMetrics {
return m.badgerStore
}