frostfs-node/pkg/local_object_storage/shard/range_test.go

134 lines
4.1 KiB
Go
Raw Normal View History

package shard
import (
"context"
"math"
"path/filepath"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/blobovniczatree"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/fstree"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil"
writecacheconfig "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/writecache/config"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/writecache/writecachebbolt"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/stretchr/testify/require"
)
func TestShard_GetRange(t *testing.T) {
t.Parallel()
t.Run("without write cache", func(t *testing.T) {
t.Parallel()
testShardGetRange(t, false)
})
t.Run("with write cache", func(t *testing.T) {
t.Parallel()
testShardGetRange(t, true)
})
}
func testShardGetRange(t *testing.T, hasWriteCache bool) {
type testCase struct {
hasErr bool
name string
payloadSize int
rng *objectSDK.Range
}
const (
writeCacheMaxSize = 1024
smallObjectSize = 2048
)
newRange := func(off, ln uint64) *objectSDK.Range {
rng := objectSDK.NewRange()
rng.SetOffset(off)
rng.SetLength(ln)
return rng
}
testCases := []testCase{
{false, "small object, good", 1024, newRange(11, 123)},
{true, "small object, out of range, big len", 1024, newRange(10, 1020)},
{true, "small object, out of range, big offset", 1024, newRange(1025, math.MaxUint64-10)},
{false, "big object, good", 2048, newRange(11, 123)},
{true, "big object, out of range, big len", 2048, newRange(100, 2000)},
{true, "big object, out of range, big offset", 2048, newRange(2048, math.MaxUint64-10)},
}
if hasWriteCache {
testCases = append(testCases,
testCase{false, "object in write-cache, good", 100, newRange(2, 18)},
testCase{true, "object in write-cache, out of range, big len", 100, newRange(4, 99)},
testCase{true, "object in write-cache, out of range, big offset", 100, newRange(101, math.MaxUint64-10)})
}
wcOpts := writecacheconfig.Options{
Type: writecacheconfig.TypeBBolt,
BBoltOptions: []writecachebbolt.Option{
writecachebbolt.WithMaxObjectSize(writeCacheMaxSize),
},
}
sh := newCustomShard(t, hasWriteCache, shardOptions{
rootPath: t.TempDir(),
wcOpts: wcOpts,
bsOpts: []blobstor.Option{
blobstor.WithStorages([]blobstor.SubStorage{
{
Storage: blobovniczatree.NewBlobovniczaTree(
blobovniczatree.WithLogger(test.NewLogger(t, true)),
blobovniczatree.WithRootPath(filepath.Join(t.TempDir(), "blob", "blobovnicza")),
blobovniczatree.WithBlobovniczaShallowDepth(1),
blobovniczatree.WithBlobovniczaShallowWidth(1)),
Policy: func(_ *objectSDK.Object, data []byte) bool {
return len(data) <= smallObjectSize
},
},
{
Storage: fstree.New(
fstree.WithPath(filepath.Join(t.TempDir(), "blob"))),
},
}),
},
})
defer releaseShard(sh, t)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
obj := testutil.GenerateObject()
testutil.AddAttribute(obj, "foo", "bar")
testutil.AddPayload(obj, tc.payloadSize)
addr := object.AddressOf(obj)
payload := slice.Copy(obj.Payload())
var putPrm PutPrm
putPrm.SetObject(obj)
_, err := sh.Put(context.Background(), putPrm)
require.NoError(t, err)
var rngPrm RngPrm
rngPrm.SetAddress(addr)
rngPrm.SetRange(tc.rng.GetOffset(), tc.rng.GetLength())
res, err := sh.GetRange(context.Background(), rngPrm)
if tc.hasErr {
var target *apistatus.ObjectOutOfRange
require.ErrorAs(t, err, &target)
} else {
require.Equal(t,
payload[tc.rng.GetOffset():tc.rng.GetOffset()+tc.rng.GetLength()],
res.Object().Payload())
}
})
}
}