2022-01-31 14:58:32 +00:00
|
|
|
package engine
|
|
|
|
|
|
|
|
import (
|
2023-03-23 14:59:14 +00:00
|
|
|
"context"
|
2022-01-31 14:58:32 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strconv"
|
|
|
|
"testing"
|
|
|
|
|
2023-03-07 13:38:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor"
|
2023-03-21 10:38:44 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/teststore"
|
2023-03-20 14:10:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil"
|
2023-03-07 13:38:26 +00:00
|
|
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/pilorama"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
|
|
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
|
|
|
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
|
|
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
2022-01-31 14:58:32 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.uber.org/zap/zaptest"
|
|
|
|
)
|
|
|
|
|
2022-02-22 07:20:33 +00:00
|
|
|
const errSmallSize = 256
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
type testEngine struct {
|
|
|
|
ng *StorageEngine
|
|
|
|
dir string
|
|
|
|
shards [2]*testShard
|
|
|
|
}
|
|
|
|
|
|
|
|
type testShard struct {
|
|
|
|
id *shard.ID
|
|
|
|
smallFileStorage *teststore.TestStore
|
|
|
|
largeFileStorage *teststore.TestStore
|
|
|
|
}
|
|
|
|
|
|
|
|
func newEngineWithErrorThreshold(t testing.TB, dir string, errThreshold uint32) *testEngine {
|
2022-02-22 07:20:33 +00:00
|
|
|
if dir == "" {
|
|
|
|
var err error
|
2022-01-31 14:58:32 +00:00
|
|
|
|
2022-02-22 07:20:33 +00:00
|
|
|
dir, err = os.MkdirTemp("", "*")
|
2022-01-31 14:58:32 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() { _ = os.RemoveAll(dir) })
|
2022-02-22 07:20:33 +00:00
|
|
|
}
|
2022-01-31 14:58:32 +00:00
|
|
|
|
2022-02-22 07:20:33 +00:00
|
|
|
e := New(
|
2022-09-28 07:41:01 +00:00
|
|
|
WithLogger(&logger.Logger{Logger: zaptest.NewLogger(t)}),
|
2022-02-22 07:20:33 +00:00
|
|
|
WithShardPoolSize(1),
|
|
|
|
WithErrorThreshold(errThreshold))
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
var testShards [2]*testShard
|
2022-02-22 07:20:33 +00:00
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
for i := range testShards {
|
|
|
|
storages, smallFileStorage, largeFileStorage := newTestStorages(filepath.Join(dir, strconv.Itoa(i)), errSmallSize)
|
|
|
|
id, err := e.AddShard(
|
2022-09-28 07:41:01 +00:00
|
|
|
shard.WithLogger(&logger.Logger{Logger: zaptest.NewLogger(t)}),
|
2023-03-21 10:38:44 +00:00
|
|
|
shard.WithBlobStorOptions(blobstor.WithStorages(storages)),
|
2022-02-22 07:20:33 +00:00
|
|
|
shard.WithMetaBaseOptions(
|
|
|
|
meta.WithPath(filepath.Join(dir, fmt.Sprintf("%d.metabase", i))),
|
2022-07-27 18:34:25 +00:00
|
|
|
meta.WithPermissions(0700),
|
|
|
|
meta.WithEpochState(epochState{}),
|
|
|
|
),
|
2022-06-09 08:09:18 +00:00
|
|
|
shard.WithPiloramaOptions(
|
|
|
|
pilorama.WithPath(filepath.Join(dir, fmt.Sprintf("%d.pilorama", i))),
|
|
|
|
pilorama.WithPerm(0700)))
|
2022-02-22 07:20:33 +00:00
|
|
|
require.NoError(t, err)
|
2023-03-21 10:38:44 +00:00
|
|
|
|
|
|
|
testShards[i] = &testShard{
|
|
|
|
id: id,
|
|
|
|
smallFileStorage: smallFileStorage,
|
|
|
|
largeFileStorage: largeFileStorage,
|
|
|
|
}
|
2022-01-31 14:58:32 +00:00
|
|
|
}
|
2022-02-22 07:20:33 +00:00
|
|
|
require.NoError(t, e.Open())
|
2023-03-23 14:59:14 +00:00
|
|
|
require.NoError(t, e.Init(context.Background()))
|
2022-02-22 07:20:33 +00:00
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
return &testEngine{
|
|
|
|
ng: e,
|
|
|
|
dir: dir,
|
|
|
|
shards: testShards,
|
|
|
|
}
|
2022-02-22 07:20:33 +00:00
|
|
|
}
|
2022-01-31 14:58:32 +00:00
|
|
|
|
2022-02-22 07:20:33 +00:00
|
|
|
func TestErrorReporting(t *testing.T) {
|
2022-01-31 14:58:32 +00:00
|
|
|
t.Run("ignore errors by default", func(t *testing.T) {
|
2023-03-21 10:38:44 +00:00
|
|
|
te := newEngineWithErrorThreshold(t, "", 0)
|
2022-01-31 14:58:32 +00:00
|
|
|
|
2023-03-20 14:10:26 +00:00
|
|
|
obj := testutil.GenerateObjectWithCID(cidtest.ID())
|
2022-02-22 07:20:33 +00:00
|
|
|
obj.SetPayload(make([]byte, errSmallSize))
|
2022-01-31 14:58:32 +00:00
|
|
|
|
2022-05-20 18:08:59 +00:00
|
|
|
var prm shard.PutPrm
|
2022-07-13 12:43:04 +00:00
|
|
|
prm.SetObject(obj)
|
2023-03-21 10:38:44 +00:00
|
|
|
te.ng.mtx.RLock()
|
|
|
|
_, err := te.ng.shards[te.shards[0].id.String()].Shard.Put(prm)
|
|
|
|
te.ng.mtx.RUnlock()
|
2022-01-31 14:58:32 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
_, err = te.ng.Get(GetPrm{addr: object.AddressOf(obj)})
|
2022-01-31 14:58:32 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
checkShardState(t, te.ng, te.shards[0].id, 0, mode.ReadWrite)
|
|
|
|
checkShardState(t, te.ng, te.shards[1].id, 0, mode.ReadWrite)
|
2022-01-31 14:58:32 +00:00
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
for _, shard := range te.shards {
|
|
|
|
shard.largeFileStorage.SetOption(teststore.WithGet(func(common.GetPrm) (common.GetRes, error) {
|
|
|
|
return common.GetRes{}, teststore.ErrDiskExploded
|
|
|
|
}))
|
|
|
|
}
|
2022-01-31 14:58:32 +00:00
|
|
|
|
|
|
|
for i := uint32(1); i < 3; i++ {
|
2023-03-21 10:38:44 +00:00
|
|
|
_, err = te.ng.Get(GetPrm{addr: object.AddressOf(obj)})
|
2022-01-31 14:58:32 +00:00
|
|
|
require.Error(t, err)
|
2023-03-21 10:38:44 +00:00
|
|
|
checkShardState(t, te.ng, te.shards[0].id, i, mode.ReadWrite)
|
|
|
|
checkShardState(t, te.ng, te.shards[1].id, 0, mode.ReadWrite)
|
2022-01-31 14:58:32 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("with error threshold", func(t *testing.T) {
|
|
|
|
const errThreshold = 3
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
te := newEngineWithErrorThreshold(t, "", errThreshold)
|
2022-01-31 14:58:32 +00:00
|
|
|
|
2023-03-20 14:10:26 +00:00
|
|
|
obj := testutil.GenerateObjectWithCID(cidtest.ID())
|
2022-02-22 07:20:33 +00:00
|
|
|
obj.SetPayload(make([]byte, errSmallSize))
|
2022-01-31 14:58:32 +00:00
|
|
|
|
2022-05-20 18:08:59 +00:00
|
|
|
var prm shard.PutPrm
|
2022-07-13 12:43:04 +00:00
|
|
|
prm.SetObject(obj)
|
2023-03-21 10:38:44 +00:00
|
|
|
te.ng.mtx.RLock()
|
|
|
|
_, err := te.ng.shards[te.shards[0].id.String()].Put(prm)
|
|
|
|
te.ng.mtx.RUnlock()
|
2022-01-31 14:58:32 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
_, err = te.ng.Get(GetPrm{addr: object.AddressOf(obj)})
|
2022-01-31 14:58:32 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
checkShardState(t, te.ng, te.shards[0].id, 0, mode.ReadWrite)
|
|
|
|
checkShardState(t, te.ng, te.shards[1].id, 0, mode.ReadWrite)
|
2022-01-31 14:58:32 +00:00
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
for _, shard := range te.shards {
|
|
|
|
shard.largeFileStorage.SetOption(teststore.WithGet(func(common.GetPrm) (common.GetRes, error) {
|
|
|
|
return common.GetRes{}, teststore.ErrDiskExploded
|
|
|
|
}))
|
|
|
|
}
|
2022-01-31 14:58:32 +00:00
|
|
|
|
|
|
|
for i := uint32(1); i < errThreshold; i++ {
|
2023-03-21 10:38:44 +00:00
|
|
|
_, err = te.ng.Get(GetPrm{addr: object.AddressOf(obj)})
|
2022-01-31 14:58:32 +00:00
|
|
|
require.Error(t, err)
|
2023-03-21 10:38:44 +00:00
|
|
|
checkShardState(t, te.ng, te.shards[0].id, i, mode.ReadWrite)
|
|
|
|
checkShardState(t, te.ng, te.shards[1].id, 0, mode.ReadWrite)
|
2022-01-31 14:58:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := uint32(0); i < 2; i++ {
|
2023-03-21 10:38:44 +00:00
|
|
|
_, err = te.ng.Get(GetPrm{addr: object.AddressOf(obj)})
|
2022-01-31 14:58:32 +00:00
|
|
|
require.Error(t, err)
|
2023-03-21 10:38:44 +00:00
|
|
|
checkShardState(t, te.ng, te.shards[0].id, errThreshold+i, mode.DegradedReadOnly)
|
|
|
|
checkShardState(t, te.ng, te.shards[1].id, 0, mode.ReadWrite)
|
2022-01-31 14:58:32 +00:00
|
|
|
}
|
2022-01-31 15:15:33 +00:00
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
require.NoError(t, te.ng.SetShardMode(te.shards[0].id, mode.ReadWrite, false))
|
|
|
|
checkShardState(t, te.ng, te.shards[0].id, errThreshold+1, mode.ReadWrite)
|
2022-01-31 15:15:33 +00:00
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
require.NoError(t, te.ng.SetShardMode(te.shards[0].id, mode.ReadWrite, true))
|
|
|
|
checkShardState(t, te.ng, te.shards[0].id, 0, mode.ReadWrite)
|
2022-01-31 14:58:32 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-02-22 07:20:33 +00:00
|
|
|
func TestBlobstorFailback(t *testing.T) {
|
|
|
|
dir, err := os.MkdirTemp("", "*")
|
|
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() { require.NoError(t, os.RemoveAll(dir)) })
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
te := newEngineWithErrorThreshold(t, dir, 1)
|
2022-02-22 07:20:33 +00:00
|
|
|
|
2022-03-03 14:19:05 +00:00
|
|
|
objs := make([]*objectSDK.Object, 0, 2)
|
2022-03-04 12:57:43 +00:00
|
|
|
for _, size := range []int{15, errSmallSize + 1} {
|
2023-03-20 14:10:26 +00:00
|
|
|
obj := testutil.GenerateObjectWithCID(cidtest.ID())
|
2022-02-22 07:20:33 +00:00
|
|
|
obj.SetPayload(make([]byte, size))
|
|
|
|
|
2022-05-20 18:08:59 +00:00
|
|
|
var prm shard.PutPrm
|
2022-07-13 12:43:04 +00:00
|
|
|
prm.SetObject(obj)
|
2023-03-21 10:38:44 +00:00
|
|
|
te.ng.mtx.RLock()
|
|
|
|
_, err = te.ng.shards[te.shards[0].id.String()].Shard.Put(prm)
|
|
|
|
te.ng.mtx.RUnlock()
|
2022-02-22 07:20:33 +00:00
|
|
|
require.NoError(t, err)
|
2022-03-05 08:49:19 +00:00
|
|
|
objs = append(objs, obj)
|
2022-02-22 07:20:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := range objs {
|
2022-03-03 14:19:05 +00:00
|
|
|
addr := object.AddressOf(objs[i])
|
2023-03-21 10:38:44 +00:00
|
|
|
_, err = te.ng.Get(GetPrm{addr: addr})
|
2022-02-22 07:20:33 +00:00
|
|
|
require.NoError(t, err)
|
2023-03-21 10:38:44 +00:00
|
|
|
_, err = te.ng.GetRange(RngPrm{addr: addr})
|
2022-03-04 12:57:43 +00:00
|
|
|
require.NoError(t, err)
|
2022-02-22 07:20:33 +00:00
|
|
|
}
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
checkShardState(t, te.ng, te.shards[0].id, 0, mode.ReadWrite)
|
|
|
|
require.NoError(t, te.ng.Close())
|
2022-02-22 07:20:33 +00:00
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
p1 := te.ng.shards[te.shards[0].id.String()].Shard.DumpInfo().BlobStorInfo.SubStorages[1].Path
|
|
|
|
p2 := te.ng.shards[te.shards[1].id.String()].Shard.DumpInfo().BlobStorInfo.SubStorages[1].Path
|
2022-02-22 07:20:33 +00:00
|
|
|
tmp := filepath.Join(dir, "tmp")
|
|
|
|
require.NoError(t, os.Rename(p1, tmp))
|
|
|
|
require.NoError(t, os.Rename(p2, p1))
|
|
|
|
require.NoError(t, os.Rename(tmp, p2))
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
te = newEngineWithErrorThreshold(t, dir, 1)
|
2022-02-22 07:20:33 +00:00
|
|
|
|
|
|
|
for i := range objs {
|
2022-03-03 14:19:05 +00:00
|
|
|
addr := object.AddressOf(objs[i])
|
2023-03-21 10:38:44 +00:00
|
|
|
getRes, err := te.ng.Get(GetPrm{addr: addr})
|
2022-03-04 12:57:43 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, objs[i], getRes.Object())
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
rngRes, err := te.ng.GetRange(RngPrm{addr: addr, off: 1, ln: 10})
|
2022-02-22 07:20:33 +00:00
|
|
|
require.NoError(t, err)
|
2022-03-04 12:57:43 +00:00
|
|
|
require.Equal(t, objs[i].Payload()[1:11], rngRes.Object().Payload())
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
_, err = te.ng.GetRange(RngPrm{addr: addr, off: errSmallSize + 10, ln: 1})
|
2022-06-29 17:33:48 +00:00
|
|
|
require.ErrorAs(t, err, &apistatus.ObjectOutOfRange{})
|
2022-02-22 07:20:33 +00:00
|
|
|
}
|
|
|
|
|
2023-03-21 10:38:44 +00:00
|
|
|
checkShardState(t, te.ng, te.shards[0].id, 1, mode.DegradedReadOnly)
|
|
|
|
checkShardState(t, te.ng, te.shards[1].id, 0, mode.ReadWrite)
|
2022-02-22 07:20:33 +00:00
|
|
|
}
|
|
|
|
|
2022-06-28 14:05:08 +00:00
|
|
|
func checkShardState(t *testing.T, e *StorageEngine, id *shard.ID, errCount uint32, mode mode.Mode) {
|
2022-01-31 14:58:32 +00:00
|
|
|
e.mtx.RLock()
|
|
|
|
sh := e.shards[id.String()]
|
|
|
|
e.mtx.RUnlock()
|
|
|
|
|
|
|
|
require.Equal(t, errCount, sh.errorCount.Load())
|
2022-11-10 10:58:46 +00:00
|
|
|
require.Equal(t, mode, sh.GetMode())
|
2022-01-31 14:58:32 +00:00
|
|
|
}
|