package engine import ( "fmt" "os" "path/filepath" "testing" "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/blobstor/teststore" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil" 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/util/logger" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" "git.frostfs.info/TrueCloudLab/hrw" "github.com/panjf2000/ants/v2" "github.com/stretchr/testify/require" "go.uber.org/atomic" "go.uber.org/zap" ) type epochState struct{} func (s epochState) CurrentEpoch() uint64 { return 0 } func BenchmarkExists(b *testing.B) { b.Run("2 shards", func(b *testing.B) { benchmarkExists(b, 2) }) b.Run("4 shards", func(b *testing.B) { benchmarkExists(b, 4) }) b.Run("8 shards", func(b *testing.B) { benchmarkExists(b, 8) }) } func benchmarkExists(b *testing.B, shardNum int) { shards := make([]*shard.Shard, shardNum) for i := 0; i < shardNum; i++ { shards[i] = testNewShard(b, i) } e := testNewEngineWithShards(b, shards...) b.Cleanup(func() { _ = e.Close() _ = os.RemoveAll(b.Name()) }) addr := oidtest.Address() for i := 0; i < 100; i++ { obj := testutil.GenerateObjectWithCID(cidtest.ID()) err := Put(e, obj) if err != nil { b.Fatal(err) } } b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { ok, err := e.exists(addr) if err != nil || ok { b.Fatalf("%t %v", ok, err) } } } func testNewEngineWithShards(t testing.TB, shards ...*shard.Shard) *StorageEngine { engine := New() for _, s := range shards { pool, err := ants.NewPool(10, ants.WithNonblocking(true)) require.NoError(t, err) engine.shards[s.ID().String()] = hashedShard{ shardWrapper: shardWrapper{ errorCount: atomic.NewUint32(0), Shard: s, }, hash: hrw.Hash([]byte(s.ID().String())), } engine.shardPools[s.ID().String()] = pool } return engine } func newStorages(root string, smallSize uint64) []blobstor.SubStorage { return []blobstor.SubStorage{ { Storage: blobovniczatree.NewBlobovniczaTree( blobovniczatree.WithRootPath(filepath.Join(root, "blobovnicza")), blobovniczatree.WithBlobovniczaShallowDepth(1), blobovniczatree.WithBlobovniczaShallowWidth(1), blobovniczatree.WithPermissions(0700)), Policy: func(_ *object.Object, data []byte) bool { return uint64(len(data)) < smallSize }, }, { Storage: fstree.New( fstree.WithPath(root), fstree.WithDepth(1)), }, } } func newTestStorages(root string, smallSize uint64) ([]blobstor.SubStorage, *teststore.TestStore, *teststore.TestStore) { smallFileStorage := teststore.New( teststore.WithSubstorage(blobovniczatree.NewBlobovniczaTree( blobovniczatree.WithRootPath(filepath.Join(root, "blobovnicza")), blobovniczatree.WithBlobovniczaShallowDepth(1), blobovniczatree.WithBlobovniczaShallowWidth(1), blobovniczatree.WithPermissions(0700)), )) largeFileStorage := teststore.New( teststore.WithSubstorage(fstree.New( fstree.WithPath(root), fstree.WithDepth(1)), )) return []blobstor.SubStorage{ { Storage: smallFileStorage, Policy: func(_ *object.Object, data []byte) bool { return uint64(len(data)) < smallSize }, }, { Storage: largeFileStorage, }, }, smallFileStorage, largeFileStorage } func testNewShard(t testing.TB, id int) *shard.Shard { sid, err := generateShardID() require.NoError(t, err) s := shard.New( shard.WithID(sid), shard.WithLogger(&logger.Logger{Logger: zap.L()}), shard.WithBlobStorOptions( blobstor.WithStorages( newStorages(filepath.Join(t.Name(), fmt.Sprintf("%d.blobstor", id)), 1<<20))), shard.WithPiloramaOptions(pilorama.WithPath(filepath.Join(t.Name(), fmt.Sprintf("%d.pilorama", id)))), shard.WithMetaBaseOptions( meta.WithPath(filepath.Join(t.Name(), fmt.Sprintf("%d.metabase", id))), meta.WithPermissions(0700), meta.WithEpochState(epochState{}), )) require.NoError(t, s.Open()) require.NoError(t, s.Init()) return s } func testEngineFromShardOpts(t *testing.T, num int, extraOpts []shard.Option) *StorageEngine { engine := New() for i := 0; i < num; i++ { _, err := engine.AddShard(append([]shard.Option{ shard.WithBlobStorOptions( blobstor.WithStorages( newStorages(filepath.Join(t.Name(), fmt.Sprintf("blobstor%d", i)), 1<<20)), ), shard.WithMetaBaseOptions( meta.WithPath(filepath.Join(t.Name(), fmt.Sprintf("metabase%d", i))), meta.WithPermissions(0700), meta.WithEpochState(epochState{}), ), shard.WithPiloramaOptions( pilorama.WithPath(filepath.Join(t.Name(), fmt.Sprintf("pilorama%d", i)))), }, extraOpts...)...) require.NoError(t, err) } require.NoError(t, engine.Open()) require.NoError(t, engine.Init()) return engine } func testNewEngineWithShardNum(t *testing.T, num int) *StorageEngine { shards := make([]*shard.Shard, 0, num) for i := 0; i < num; i++ { shards = append(shards, testNewShard(t, i)) } return testNewEngineWithShards(t, shards...) }