[#17] Add morph client metrics

Signed-off-by: Alejandro Lopez <a.lopez@yadro.com>
This commit is contained in:
Alejandro Lopez 2023-06-08 16:37:46 +03:00
parent 90e9247b69
commit 4887f489a1
11 changed files with 182 additions and 15 deletions

View file

@ -11,6 +11,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/metrics"
morphmetrics "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/metrics"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
lru "github.com/hashicorp/golang-lru/v2"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
@ -48,7 +49,8 @@ import (
type Client struct {
cache cache
logger *logger.Logger // logging component
logger *logger.Logger // logging component
metrics morphmetrics.Register
client *rpcclient.WSClient // neo-go websocket client
rpcActor *actor.Actor // neo-go RPC actor
@ -172,6 +174,12 @@ func wrapFrostFSError(err error) error {
// Invoke invokes contract method by sending transaction into blockchain.
// Supported args types: int64, string, util.Uint160, []byte and bool.
func (c *Client) Invoke(contract util.Uint160, fee fixedn.Fixed8, method string, args ...any) error {
start := time.Now()
success := false
defer func() {
c.metrics.ObserveInvoke("Invoke", contract.String(), method, success, time.Since(start))
}()
c.switchLock.RLock()
defer c.switchLock.RUnlock()
@ -189,6 +197,7 @@ func (c *Client) Invoke(contract util.Uint160, fee fixedn.Fixed8, method string,
zap.Uint32("vub", vub),
zap.Stringer("tx_hash", txHash.Reverse()))
success = true
return nil
}
@ -196,6 +205,12 @@ func (c *Client) Invoke(contract util.Uint160, fee fixedn.Fixed8, method string,
// If cb returns an error, the session is closed and this error is returned as-is.
// If the remove neo-go node does not support sessions, `unwrap.ErrNoSessionID` is returned.
func (c *Client) TestInvokeIterator(cb func(stackitem.Item) error, contract util.Uint160, method string, args ...interface{}) error {
start := time.Now()
success := false
defer func() {
c.metrics.ObserveInvoke("TestInvokeIterator", contract.String(), method, success, time.Since(start))
}()
c.switchLock.RLock()
defer c.switchLock.RUnlock()
@ -228,12 +243,20 @@ func (c *Client) TestInvokeIterator(cb func(stackitem.Item) error, contract util
}
items, err = c.rpcActor.TraverseIterator(sid, &r, 0)
}
success = err == nil
return err
}
// TestInvoke invokes contract method locally in neo-go node. This method should
// be used to read data from smart-contract.
func (c *Client) TestInvoke(contract util.Uint160, method string, args ...any) (res []stackitem.Item, err error) {
start := time.Now()
success := false
defer func() {
c.metrics.ObserveInvoke("TestInvoke", contract.String(), method, success, time.Since(start))
}()
c.switchLock.RLock()
defer c.switchLock.RUnlock()
@ -250,6 +273,7 @@ func (c *Client) TestInvoke(contract util.Uint160, method string, args ...any) (
return nil, wrapFrostFSError(&notHaltStateError{state: val.State, exception: val.FaultException})
}
success = true
return val.Stack, nil
}
@ -512,6 +536,10 @@ func (c *Client) NotificationChannel() <-chan rpcclient.Notification {
return c.client.Notifications //lint:ignore SA1019 waits for neo-go v0.102.0 https://github.com/nspcc-dev/neo-go/pull/2980
}
func (c *Client) Metrics() morphmetrics.Register {
return c.metrics
}
func (c *Client) setActor(act *actor.Actor) {
c.rpcActor = act
c.gasToken = nep17.New(act, gas.Hash)

View file

@ -8,6 +8,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/metrics"
morphmetrics "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/metrics"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
lru "github.com/hashicorp/golang-lru/v2"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
@ -32,6 +33,8 @@ type cfg struct {
logger *logger.Logger // logging component
metrics morphmetrics.Register
waitInterval time.Duration
signer *transaction.Signer
@ -60,6 +63,7 @@ func defaultConfig() *cfg {
return &cfg{
dialTimeout: defaultDialTimeout,
logger: &logger.Logger{Logger: zap.L()},
metrics: morphmetrics.NoopRegister{},
waitInterval: defaultWaitInterval,
signer: &transaction.Signer{
Scopes: transaction.Global,
@ -80,6 +84,7 @@ func defaultConfig() *cfg {
// - signer with the global scope;
// - wait interval: 500ms;
// - logger: &logger.Logger{Logger: zap.L()}.
// - metrics: metrics.NoopRegister
//
// If desired option satisfies the default value, it can be omitted.
// If multiple options of the same config value are supplied,
@ -109,6 +114,7 @@ func New(ctx context.Context, key *keys.PrivateKey, opts ...Option) (*Client, er
cli := &Client{
cache: newClientCache(cfg.morphCacheMetrics),
logger: cfg.logger,
metrics: cfg.metrics,
acc: acc,
accAddr: accAddr,
cfg: *cfg,
@ -235,6 +241,20 @@ func WithLogger(logger *logger.Logger) Option {
}
}
// WithMetrics returns a client constructor option
// that specifies the component for reporting metrics.
//
// Ignores nil value.
//
// If option not provided, NoopMetrics is used.
func WithMetrics(metrics morphmetrics.Register) Option {
return func(c *cfg) {
if metrics != nil {
c.metrics = metrics
}
}
}
// WithSigner returns a client constructor option
// that specifies the signer and the scope of the transaction.
//

View file

@ -444,6 +444,12 @@ func (c *Client) notaryInvokeAsCommittee(method string, nonce, vub uint32, args
}
func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint160, nonce uint32, vub *uint32, method string, args ...any) error {
start := time.Now()
success := false
defer func() {
c.metrics.ObserveInvoke("notaryInvoke", contract.String(), method, success, time.Since(start))
}()
alphabetList, err := c.notary.alphabetSource()
if err != nil {
return err
@ -485,6 +491,7 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint
zap.String("tx_hash", mainH.StringLE()),
zap.String("fallback_hash", fbH.StringLE()))
success = true
return nil
}

View file

@ -0,0 +1,17 @@
package metrics
import "time"
type Register interface {
IncSwitchCount()
SetLastBlock(uint32)
IncNotificationCount(notificationType string)
ObserveInvoke(typ string, contract string, method string, success bool, d time.Duration)
}
type NoopRegister struct{}
func (NoopRegister) IncSwitchCount() {}
func (NoopRegister) SetLastBlock(uint32) {}
func (NoopRegister) IncNotificationCount(string) {}
func (NoopRegister) ObserveInvoke(string, string, string, bool, time.Duration) {}

View file

@ -200,18 +200,22 @@ routeloop:
break routeloop
case ev, ok := <-curr.NotifyChan:
if ok {
s.client.Metrics().IncNotificationCount("notify")
s.notifyChan <- ev
} else {
connLost = true
}
case ev, ok := <-curr.BlockChan:
if ok {
s.client.Metrics().IncNotificationCount("block")
s.client.Metrics().SetLastBlock(ev.Index)
s.blockChan <- ev
} else {
connLost = true
}
case ev, ok := <-curr.NotaryChan:
if ok {
s.client.Metrics().IncNotificationCount("notary")
s.notaryChan <- ev
} else {
connLost = true
@ -262,6 +266,7 @@ func (s *subscriber) switchEndpoint(ctx context.Context, finishCh chan<- bool) (
s.current = chs
s.Unlock()
s.client.Metrics().IncSwitchCount()
return true, cliCh
}