From d5cea25cd8572c9ccc1d959b4c8c5b9bdad6d586 Mon Sep 17 00:00:00 2001 From: Evgenii Baidakov Date: Thu, 22 Jun 2023 15:37:38 +0400 Subject: [PATCH] client: External statistic in containers Signed-off-by: Evgenii Baidakov --- client/container.go | 101 +++++++++++++++++++++++++++++++++--------- pool/container.go | 55 +++++------------------ pool/pool.go | 6 +++ pool/pool_aio_test.go | 19 +++++++- stat/stat.go | 6 +++ 5 files changed, 121 insertions(+), 66 deletions(-) diff --git a/client/container.go b/client/container.go index 8bd102ca..3834ed6b 100644 --- a/client/container.go +++ b/client/container.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "time" v2container "github.com/nspcc-dev/neofs-api-go/v2/container" "github.com/nspcc-dev/neofs-api-go/v2/refs" @@ -15,6 +16,7 @@ import ( neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" "github.com/nspcc-dev/neofs-sdk-go/eacl" "github.com/nspcc-dev/neofs-sdk-go/session" + "github.com/nspcc-dev/neofs-sdk-go/stat" "github.com/nspcc-dev/neofs-sdk-go/user" ) @@ -63,7 +65,12 @@ func (x *PrmContainerPut) WithinSession(s session.Container) { // // Return errors: // - [ErrMissingSigner] -func (c *Client) ContainerPut(ctx context.Context, cont container.Container, prm PrmContainerPut) (cid.ID, error) { +func (c *Client) ContainerPut(ctx context.Context, cont container.Container, prm PrmContainerPut) (_ cid.ID, err error) { + ts := time.Now() + defer func() { + c.sendStatistic(stat.MethodContainerPut, time.Since(ts), err) + }() + signer, err := c.getSigner(prm.signer) if err != nil { return cid.ID{}, err @@ -76,7 +83,8 @@ func (c *Client) ContainerPut(ctx context.Context, cont container.Container, prm var sig neofscrypto.Signature err = cont.CalculateSignature(&sig, signer) if err != nil { - return cid.ID{}, fmt.Errorf("calculate container signature: %w", err) + err = fmt.Errorf("calculate container signature: %w", err) + return cid.ID{}, err } var sigv2 refs.Signature @@ -136,6 +144,7 @@ func (c *Client) ContainerPut(ctx context.Context, cont container.Container, prm // process call if !cc.processCall() { + err = cc.err return cid.ID{}, cc.err } @@ -156,9 +165,15 @@ type PrmContainerGet struct { // // Return errors: // - [ErrMissingSigner] -func (c *Client) ContainerGet(ctx context.Context, id cid.ID, prm PrmContainerGet) (container.Container, error) { +func (c *Client) ContainerGet(ctx context.Context, id cid.ID, prm PrmContainerGet) (_ container.Container, err error) { + ts := time.Now() + defer func() { + c.sendStatistic(stat.MethodContainerGet, time.Since(ts), err) + }() + if c.prm.signer == nil { - return container.Container{}, ErrMissingSigner + err = ErrMissingSigner + return container.Container{}, err } var cidV2 refs.ContainerID @@ -203,6 +218,7 @@ func (c *Client) ContainerGet(ctx context.Context, id cid.ID, prm PrmContainerGe // process call if !cc.processCall() { + err = cc.err return container.Container{}, cc.err } @@ -223,9 +239,15 @@ type PrmContainerList struct { // // Return errors: // - [ErrMissingSigner] -func (c *Client) ContainerList(ctx context.Context, ownerID user.ID, prm PrmContainerList) ([]cid.ID, error) { +func (c *Client) ContainerList(ctx context.Context, ownerID user.ID, prm PrmContainerList) (_ []cid.ID, err error) { + ts := time.Now() + defer func() { + c.sendStatistic(stat.MethodContainerList, time.Since(ts), err) + }() + if c.prm.signer == nil { - return nil, ErrMissingSigner + err = ErrMissingSigner + return nil, err } // form request body @@ -269,6 +291,7 @@ func (c *Client) ContainerList(ctx context.Context, ownerID user.ID, prm PrmCont // process call if !cc.processCall() { + err = cc.err return nil, cc.err } @@ -319,14 +342,20 @@ func (x *PrmContainerDelete) WithinSession(tok session.Container) { // Return errors: // - [ErrMissingSigner] // - [neofscrypto.ErrIncorrectSigner] -func (c *Client) ContainerDelete(ctx context.Context, id cid.ID, prm PrmContainerDelete) error { +func (c *Client) ContainerDelete(ctx context.Context, id cid.ID, prm PrmContainerDelete) (err error) { + ts := time.Now() + defer func() { + c.sendStatistic(stat.MethodContainerDelete, time.Since(ts), err) + }() + signer, err := c.getSigner(prm.signer) if err != nil { return err } if signer.Scheme() != neofscrypto.ECDSA_DETERMINISTIC_SHA256 { - return errNonNeoSigner + err = errNonNeoSigner + return err } // sign container ID @@ -340,7 +369,8 @@ func (c *Client) ContainerDelete(ctx context.Context, id cid.ID, prm PrmContaine var sig neofscrypto.Signature err = sig.Calculate(signer, data) if err != nil { - return fmt.Errorf("calculate signature: %w", err) + err = fmt.Errorf("calculate signature: %w", err) + return err } var sigv2 refs.Signature @@ -383,6 +413,7 @@ func (c *Client) ContainerDelete(ctx context.Context, id cid.ID, prm PrmContaine // process call if !cc.processCall() { + err = cc.err return cc.err } @@ -403,9 +434,15 @@ type PrmContainerEACL struct { // // Return errors: // - [ErrMissingSigner] -func (c *Client) ContainerEACL(ctx context.Context, id cid.ID, prm PrmContainerEACL) (eacl.Table, error) { +func (c *Client) ContainerEACL(ctx context.Context, id cid.ID, prm PrmContainerEACL) (_ eacl.Table, err error) { + ts := time.Now() + defer func() { + c.sendStatistic(stat.MethodContainerEACL, time.Since(ts), err) + }() + if c.prm.signer == nil { - return eacl.Table{}, ErrMissingSigner + err = ErrMissingSigner + return eacl.Table{}, err } var cidV2 refs.ContainerID @@ -447,6 +484,7 @@ func (c *Client) ContainerEACL(ctx context.Context, id cid.ID, prm PrmContainerE // process call if !cc.processCall() { + err = cc.err return eacl.Table{}, cc.err } @@ -502,19 +540,26 @@ func (x *PrmContainerSetEACL) WithinSession(s session.Container) { // - [ErrMissingSigner] // // Context is required and must not be nil. It is used for network communication. -func (c *Client) ContainerSetEACL(ctx context.Context, table eacl.Table, prm PrmContainerSetEACL) error { +func (c *Client) ContainerSetEACL(ctx context.Context, table eacl.Table, prm PrmContainerSetEACL) (err error) { + ts := time.Now() + defer func() { + c.sendStatistic(stat.MethodContainerSetEACL, time.Since(ts), err) + }() + signer, err := c.getSigner(prm.signer) if err != nil { return err } if signer.Scheme() != neofscrypto.ECDSA_DETERMINISTIC_SHA256 { - return errNonNeoSigner + err = errNonNeoSigner + return err } _, isCIDSet := table.CID() if !isCIDSet { - return ErrMissingEACLContainer + err = ErrMissingEACLContainer + return err } // sign the eACL table @@ -523,7 +568,8 @@ func (c *Client) ContainerSetEACL(ctx context.Context, table eacl.Table, prm Prm var sig neofscrypto.Signature err = sig.CalculateMarshalled(signer, eaclV2) if err != nil { - return fmt.Errorf("calculate signature: %w", err) + err = fmt.Errorf("calculate signature: %w", err) + return err } var sigv2 refs.Signature @@ -566,6 +612,7 @@ func (c *Client) ContainerSetEACL(ctx context.Context, table eacl.Table, prm Prm // process call if !cc.processCall() { + err = cc.err return cc.err } @@ -594,15 +641,20 @@ type PrmAnnounceSpace struct { // Return errors: // - [ErrMissingAnnouncements] // - [ErrMissingSigner] -func (c *Client) ContainerAnnounceUsedSpace(ctx context.Context, announcements []container.SizeEstimation, prm PrmAnnounceSpace) error { - // check parameters +func (c *Client) ContainerAnnounceUsedSpace(ctx context.Context, announcements []container.SizeEstimation, prm PrmAnnounceSpace) (err error) { + ts := time.Now() + defer func() { + c.sendStatistic(stat.MethodContainerAnnounceUsedSpace, time.Since(ts), err) + }() if len(announcements) == 0 { - return ErrMissingAnnouncements + err = ErrMissingAnnouncements + return err } if c.prm.signer == nil { - return ErrMissingSigner + err = ErrMissingSigner + return err } // convert list of SDK announcement structures into NeoFS-API v2 list @@ -635,6 +687,7 @@ func (c *Client) ContainerAnnounceUsedSpace(ctx context.Context, announcements [ // process call if !cc.processCall() { + err = cc.err return cc.err } @@ -650,10 +703,16 @@ func (c *Client) ContainerAnnounceUsedSpace(ctx context.Context, announcements [ // Returns any network/parsing config errors. // // See also NetworkInfo, container.ApplyNetworkConfig. -func SyncContainerWithNetwork(ctx context.Context, cnr *container.Container, c *Client) error { +func SyncContainerWithNetwork(ctx context.Context, cnr *container.Container, c *Client) (err error) { + ts := time.Now() + defer func() { + c.sendStatistic(stat.MethodContainerAnnounceUsedSpace, time.Since(ts), err) + }() + res, err := c.NetworkInfo(ctx, PrmNetworkInfo{}) if err != nil { - return fmt.Errorf("network info call: %w", err) + err = fmt.Errorf("network info call: %w", err) + return err } cnr.ApplyNetworkConfig(res) diff --git a/pool/container.go b/pool/container.go index 9ec32e2f..d858a290 100644 --- a/pool/container.go +++ b/pool/container.go @@ -2,7 +2,6 @@ package pool import ( "context" - "time" "github.com/nspcc-dev/neofs-sdk-go/client" "github.com/nspcc-dev/neofs-sdk-go/container" @@ -15,100 +14,70 @@ import ( // // See details in [client.Client.ContainerPut]. func (p *Pool) ContainerPut(ctx context.Context, cont container.Container, prm client.PrmContainerPut) (cid.ID, error) { - c, statUpdater, err := p.sdkClient() + c, _, err := p.sdkClient() if err != nil { return cid.ID{}, err } - start := time.Now() - id, err := c.ContainerPut(ctx, cont, prm) - statUpdater.incRequests(time.Since(start), methodContainerPut) - statUpdater.updateErrorRate(err) - - return id, err + return c.ContainerPut(ctx, cont, prm) } // ContainerGet reads NeoFS container by ID. // // See details in [client.Client.ContainerGet]. func (p *Pool) ContainerGet(ctx context.Context, id cid.ID, prm client.PrmContainerGet) (container.Container, error) { - c, statUpdater, err := p.sdkClient() + c, _, err := p.sdkClient() if err != nil { return container.Container{}, err } - start := time.Now() - cnr, err := c.ContainerGet(ctx, id, prm) - statUpdater.incRequests(time.Since(start), methodContainerGet) - statUpdater.updateErrorRate(err) - - return cnr, err + return c.ContainerGet(ctx, id, prm) } // ContainerList requests identifiers of the account-owned containers. // // See details in [client.Client.ContainerList]. func (p *Pool) ContainerList(ctx context.Context, ownerID user.ID, prm client.PrmContainerList) ([]cid.ID, error) { - c, statUpdater, err := p.sdkClient() + c, _, err := p.sdkClient() if err != nil { return []cid.ID{}, err } - start := time.Now() - ids, err := c.ContainerList(ctx, ownerID, prm) - statUpdater.incRequests(time.Since(start), methodContainerList) - statUpdater.updateErrorRate(err) - - return ids, err + return c.ContainerList(ctx, ownerID, prm) } // ContainerDelete sends request to remove the NeoFS container. // // See details in [client.Client.ContainerDelete]. func (p *Pool) ContainerDelete(ctx context.Context, id cid.ID, prm client.PrmContainerDelete) error { - c, statUpdater, err := p.sdkClient() + c, _, err := p.sdkClient() if err != nil { return err } - start := time.Now() - err = c.ContainerDelete(ctx, id, prm) - statUpdater.incRequests(time.Since(start), methodContainerDelete) - statUpdater.updateErrorRate(err) - - return err + return c.ContainerDelete(ctx, id, prm) } // ContainerEACL reads eACL table of the NeoFS container. // // See details in [client.Client.ContainerEACL]. func (p *Pool) ContainerEACL(ctx context.Context, id cid.ID, prm client.PrmContainerEACL) (eacl.Table, error) { - c, statUpdater, err := p.sdkClient() + c, _, err := p.sdkClient() if err != nil { return eacl.Table{}, err } - start := time.Now() - table, err := c.ContainerEACL(ctx, id, prm) - statUpdater.incRequests(time.Since(start), methodContainerEACL) - statUpdater.updateErrorRate(err) - - return table, err + return c.ContainerEACL(ctx, id, prm) } // ContainerSetEACL sends request to update eACL table of the NeoFS container. // // See details in [client.Client.ContainerSetEACL]. func (p *Pool) ContainerSetEACL(ctx context.Context, table eacl.Table, prm client.PrmContainerSetEACL) error { - c, statUpdater, err := p.sdkClient() + c, _, err := p.sdkClient() if err != nil { return err } - start := time.Now() - err = c.ContainerSetEACL(ctx, table, prm) - statUpdater.incRequests(time.Since(start), methodContainerSetEACL) - statUpdater.updateErrorRate(err) - - return err + return c.ContainerSetEACL(ctx, table, prm) } diff --git a/pool/pool.go b/pool/pool.go index 6a814457..3ff1ca1e 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -154,6 +154,8 @@ const ( methodNetMapSnapshot methodObjectHash methodObjectSearch + methodContainerAnnounceUsedSpace + methodSyncContainerWithNetwork methodLast ) @@ -196,6 +198,10 @@ func (m MethodIndex) String() string { return "objectHash" case methodObjectSearch: return "objectSearch" + case methodContainerAnnounceUsedSpace: + return "containerAnnounceUsedSpace" + case methodSyncContainerWithNetwork: + return "syncContainerWithNetwork" case methodLast: return "it's a system name rather than a method" default: diff --git a/pool/pool_aio_test.go b/pool/pool_aio_test.go index 997e699a..66b3519c 100644 --- a/pool/pool_aio_test.go +++ b/pool/pool_aio_test.go @@ -160,8 +160,8 @@ func TestPoolInterfaceWithAIO(t *testing.T) { _, err = pool.BalanceGet(ctx, cmd) require.Error(t, err) - stat := pool.Statistic() - nodeStat, err := stat.Node(nodeAddr) + st := pool.Statistic() + nodeStat, err := st.Node(nodeAddr) require.NoError(t, err) require.Equal(t, uint32(1), nodeStat.currentErrors) @@ -180,6 +180,11 @@ func TestPoolInterfaceWithAIO(t *testing.T) { require.NoError(t, isBucketCreated(ctxTimeout, cl, containerID)) eaclTable = testEaclTable(containerID) + + st := pool.Statistic() + nodeStat, err := st.Node(nodeAddr) + require.Equal(t, uint64(1), nodeStat.methods[methodContainerPut].allRequests) + require.Greater(t, nodeStat.methods[methodContainerPut].allTime, uint64(0)) }) t.Run("set eacl", func(t *testing.T) { @@ -191,6 +196,11 @@ func TestPoolInterfaceWithAIO(t *testing.T) { require.NoError(t, err) require.NoError(t, isEACLCreated(ctxTimeout, cl, containerID, table)) + + st := pool.Statistic() + nodeStat, err := st.Node(nodeAddr) + require.Equal(t, uint64(1), nodeStat.methods[methodContainerSetEACL].allRequests) + require.Greater(t, nodeStat.methods[methodContainerSetEACL].allTime, uint64(0)) }) t.Run("upload object", func(t *testing.T) { @@ -245,6 +255,11 @@ func TestPoolInterfaceWithAIO(t *testing.T) { require.NoError(t, err) require.NoError(t, isBucketDeleted(ctxTimeout, cl, containerID)) + + st := pool.Statistic() + nodeStat, err := st.Node(nodeAddr) + require.Equal(t, uint64(1), nodeStat.methods[methodContainerDelete].allRequests) + require.Greater(t, nodeStat.methods[methodContainerDelete].allTime, uint64(0)) }) t.Run("container really deleted", func(t *testing.T) { diff --git a/stat/stat.go b/stat/stat.go index d5453d5a..5665068a 100644 --- a/stat/stat.go +++ b/stat/stat.go @@ -26,6 +26,8 @@ const ( MethodNetMapSnapshot MethodObjectHash MethodObjectSearch + MethodContainerAnnounceUsedSpace + MethodSyncContainerWithNetwork methodLast ) @@ -68,6 +70,10 @@ func (m Method) String() string { return "objectHash" case MethodObjectSearch: return "objectSearch" + case MethodContainerAnnounceUsedSpace: + return "containerAnnounceUsedSpace" + case MethodSyncContainerWithNetwork: + return "syncContainerWithNetwork" case methodLast: return "it's a system name rather than a method" default: