forked from TrueCloudLab/frostfs-node
pilorama: Support tree migration between forests
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
This commit is contained in:
parent
e4344fd5cc
commit
95061cc1ea
2 changed files with 124 additions and 0 deletions
33
pkg/local_object_storage/pilorama/migrate.go
Normal file
33
pkg/local_object_storage/pilorama/migrate.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package pilorama
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultMigrateBatchSize = 100
|
||||||
|
|
||||||
|
// Migrate migrates a single tree to another forest.
|
||||||
|
// If the migration is interrupted in the middle, some garbage may be left in the target forest.
|
||||||
|
func Migrate(ctx context.Context, from Forest, to Forest, cnr cidSDK.ID, treeID string) error {
|
||||||
|
eg, ctx := errgroup.WithContext(ctx)
|
||||||
|
eg.SetLimit(defaultMigrateBatchSize)
|
||||||
|
|
||||||
|
var height uint64
|
||||||
|
for {
|
||||||
|
op, err := from.TreeGetOpLog(ctx, cnr, treeID, height)
|
||||||
|
if err != nil {
|
||||||
|
_ = eg.Wait()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if op.Time == 0 {
|
||||||
|
return eg.Wait()
|
||||||
|
}
|
||||||
|
eg.Go(func() error {
|
||||||
|
return to.TreeApply(ctx, cnr, treeID, &op, false)
|
||||||
|
})
|
||||||
|
height = op.Time + 1
|
||||||
|
}
|
||||||
|
}
|
91
pkg/local_object_storage/pilorama/migrate_test.go
Normal file
91
pkg/local_object_storage/pilorama/migrate_test.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package pilorama
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func prepareTree(t testing.TB, f Forest, cnr cidSDK.ID, treeID string, count int) {
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
_, err := f.TreeMove(context.Background(), CIDDescriptor{CID: cnr, Size: 1}, treeID, &Move{
|
||||||
|
Parent: RootID,
|
||||||
|
Child: Node(i + 1),
|
||||||
|
Meta: Meta{
|
||||||
|
Items: []KeyValue{
|
||||||
|
{Key: AttributeFilename, Value: []byte(uuid.New().String())},
|
||||||
|
{Key: "Another key", Value: []byte(strconv.FormatInt(int64(i), 10))},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMigrate(b *testing.B) {
|
||||||
|
const (
|
||||||
|
count = 1000
|
||||||
|
batchSize = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := range providers {
|
||||||
|
if providers[i].name == "inmemory" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
from := providers[i].construct(b)
|
||||||
|
cnr := cidtest.ID()
|
||||||
|
treeID := "benchtree"
|
||||||
|
cons := providers[i].construct
|
||||||
|
|
||||||
|
// Do not use random tree here, to better interpret benchmark results.
|
||||||
|
// It is append-only log without deletions.
|
||||||
|
prepareTree(b, from, cnr, treeID, count)
|
||||||
|
|
||||||
|
b.Run(providers[i].name, func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
to := cons(b, WithMaxBatchSize(batchSize))
|
||||||
|
err := Migrate(context.Background(), from, to, cnr, treeID)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("migrate: %v", err)
|
||||||
|
}
|
||||||
|
if err := to.Close(); err != nil {
|
||||||
|
b.Fatalf("close: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMigrate(t *testing.T) {
|
||||||
|
for i := range providers {
|
||||||
|
t.Run(providers[i].name, func(t *testing.T) {
|
||||||
|
testMigrate(t, providers[i].construct)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMigrate(t *testing.T, cons func(testing.TB, ...Option) ForestStorage) {
|
||||||
|
const (
|
||||||
|
count = 100
|
||||||
|
treeID = "sometree"
|
||||||
|
)
|
||||||
|
cnr := cidtest.ID()
|
||||||
|
|
||||||
|
from := cons(t)
|
||||||
|
ops := prepareRandomTree(count, count)
|
||||||
|
for i := range ops {
|
||||||
|
err := from.TreeApply(context.Background(), cnr, treeID, &ops[i], true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
to := cons(t)
|
||||||
|
require.NoError(t, Migrate(context.Background(), from, to, cnr, treeID))
|
||||||
|
compareForests(t, from, to, cnr, treeID, count)
|
||||||
|
}
|
Loading…
Reference in a new issue