frostfs-node/pkg/services/metrics/metrics_test.go

276 lines
5.6 KiB
Go

package metrics
import (
"context"
"encoding/binary"
"sync"
"testing"
"time"
"github.com/nspcc-dev/neofs-api-go/object"
"github.com/nspcc-dev/neofs-api-go/refs"
meta2 "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/meta"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
type (
fakeCounter int
fakeIterator string
fakeMetaStore []*object.Object
)
var (
_ ObjectCounter = (*fakeCounter)(nil)
_ meta2.Iterator = (*fakeIterator)(nil)
)
func (f fakeCounter) ObjectsCount() (uint64, error) {
return uint64(f), nil
}
func (f fakeIterator) Iterate(_ meta2.IterateFunc) error {
if f == "" {
return nil
}
return errors.New(string(f))
}
func (f fakeMetaStore) Iterate(cb meta2.IterateFunc) error {
if cb == nil {
return nil
}
for i := range f {
if err := cb(f[i]); err != nil {
return err
}
}
return nil
}
func TestCollector(t *testing.T) {
buck := &fakeBucket{items: make(map[uint64]int)}
t.Run("check errors", func(t *testing.T) {
t.Run("empty logger", func(t *testing.T) {
svc, err := New(Params{MetricsStore: buck})
require.Nil(t, svc)
require.EqualError(t, err, errEmptyLogger.Error())
})
t.Run("empty metrics store", func(t *testing.T) {
svc, err := New(Params{Logger: zap.L()})
require.Nil(t, svc)
require.EqualError(t, err, errEmptyMetricsStore.Error())
})
})
svc, err := New(Params{
Logger: zap.L(),
MetricsStore: buck,
Options: []string{
"/Location:Europe/Country:Russia/City:Moscow",
"/Some:Another/Key:Value",
},
})
require.NoError(t, err)
require.NotNil(t, svc)
coll, ok := svc.(*collector)
require.True(t, ok)
require.NotNil(t, coll)
t.Run("check start", func(t *testing.T) {
coll.interval = time.Second
t.Run("stop by context", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
wg := new(sync.WaitGroup)
wg.Add(1)
counter.Store(-1)
go func() {
svc.Start(ctx)
wg.Done()
}()
cancel()
wg.Wait()
require.Equal(t, float64(-1), counter.Load())
})
t.Run("should fail on empty counter", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
wg := new(sync.WaitGroup)
wg.Add(1)
counter.Store(0)
go func() {
svc.Start(ctx)
wg.Done()
}()
time.Sleep(2 * time.Second)
cancel()
wg.Wait()
require.Equal(t, float64(0), counter.Load())
})
t.Run("should success on fakeCounter", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
wg := new(sync.WaitGroup)
wg.Add(1)
coll.SetCounter(fakeCounter(8))
counter.Store(0)
go func() {
svc.Start(ctx)
wg.Done()
}()
time.Sleep(2 * time.Second)
cancel()
wg.Wait()
require.Equal(t, float64(8), counter.Load())
})
})
t.Run("iterator", func(t *testing.T) {
{
coll.SetIterator(nil)
require.Nil(t, coll.metas.iter)
require.EqualError(t, coll.metas.Iterate(nil), errEmptyMetaStore.Error())
}
{
iter := fakeIterator("")
coll.SetIterator(iter)
require.Equal(t, iter, coll.metas.iter)
require.NoError(t, coll.metas.Iterate(nil))
}
{
iter := fakeIterator("test")
coll.SetIterator(iter)
require.Equal(t, iter, coll.metas.iter)
require.EqualError(t, coll.metas.Iterate(nil), string(iter))
}
})
t.Run("add-rem space", func(t *testing.T) {
cid := refs.CID{1, 2, 3, 4, 5}
buf := make([]byte, 8)
key := keyFromBytes(cid.Bytes())
zero := make([]byte, 8)
size := uint64(100)
binary.BigEndian.PutUint64(buf, size)
{
coll.UpdateContainer(cid, size, AddSpace)
require.Len(t, coll.sizes.items, 1)
require.Len(t, buck.items, 1)
require.Contains(t, buck.items, key)
require.Contains(t, buck.kv, fakeKV{key: cid.Bytes(), val: buf})
}
{
coll.UpdateContainer(cid, size, RemSpace)
require.Len(t, coll.sizes.items, 1)
require.Len(t, buck.items, 1)
require.Contains(t, buck.items, key)
require.Contains(t, buck.kv, fakeKV{key: cid.Bytes(), val: zero})
}
{
coll.UpdateContainer(cid, size, RemSpace)
require.Len(t, coll.sizes.items, 1)
require.Len(t, buck.items, 1)
require.Contains(t, buck.kv, fakeKV{key: cid.Bytes(), val: zero})
}
})
t.Run("add-rem multi thread", func(t *testing.T) {
wg := new(sync.WaitGroup)
wg.Add(10)
size := uint64(100)
zero := make([]byte, 8)
// reset
coll.UpdateSpaceUsage()
for i := 0; i < 10; i++ {
cid := refs.CID{1, 2, 3, 4, byte(i)}
coll.UpdateContainer(cid, size, AddSpace)
go func() {
coll.UpdateContainer(cid, size, RemSpace)
wg.Done()
}()
}
wg.Wait()
require.Len(t, coll.sizes.items, 10)
require.Len(t, buck.items, 10)
for i := 0; i < 10; i++ {
cid := refs.CID{1, 2, 3, 4, byte(i)}
require.Contains(t, buck.kv, fakeKV{key: cid.Bytes(), val: zero})
}
})
t.Run("reset buckets", func(t *testing.T) {
coll.UpdateSpaceUsage()
require.Len(t, coll.sizes.items, 0)
require.Len(t, buck.items, 0)
})
t.Run("reset from metaStore", func(t *testing.T) {
cid := refs.CID{1, 2, 3, 4, 5}
buf := make([]byte, 8)
key := keyFromBytes(cid.Bytes())
size := uint64(100)
binary.BigEndian.PutUint64(buf, size)
iter := fakeMetaStore{
{
SystemHeader: object.SystemHeader{
PayloadLength: size,
CID: cid,
},
},
{
Headers: []object.Header{
{
Value: &object.Header_Tombstone{Tombstone: &object.Tombstone{}},
},
},
},
}
coll.SetIterator(iter)
coll.UpdateSpaceUsage()
require.Len(t, coll.sizes.items, 1)
require.Len(t, buck.items, 1)
require.Contains(t, buck.items, key)
require.Contains(t, buck.kv, fakeKV{key: cid.Bytes(), val: buf})
})
}