[#947] evacuate: Refactor evacuate parameters

Drop methods to make it easier to extend.

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
Dmitrii Stepanov 2024-02-05 15:42:30 +03:00
parent 8e2a0611f4
commit a6eb66bf9c
4 changed files with 43 additions and 61 deletions

View file

@ -30,10 +30,10 @@ var (
// EvacuateShardPrm represents parameters for the EvacuateShard operation. // EvacuateShardPrm represents parameters for the EvacuateShard operation.
type EvacuateShardPrm struct { type EvacuateShardPrm struct {
shardID []*shard.ID ShardID []*shard.ID
handler func(context.Context, oid.Address, *objectSDK.Object) error Handler func(context.Context, oid.Address, *objectSDK.Object) error
ignoreErrors bool IgnoreErrors bool
async bool Async bool
} }
// EvacuateShardRes represents result of the EvacuateShard operation. // EvacuateShardRes represents result of the EvacuateShard operation.
@ -54,26 +54,6 @@ func NewEvacuateShardRes() *EvacuateShardRes {
} }
} }
// WithShardIDList sets shard ID.
func (p *EvacuateShardPrm) WithShardIDList(id []*shard.ID) {
p.shardID = id
}
// WithIgnoreErrors sets flag to ignore errors.
func (p *EvacuateShardPrm) WithIgnoreErrors(ignore bool) {
p.ignoreErrors = ignore
}
// WithFaultHandler sets handler to call for objects which cannot be saved on other shards.
func (p *EvacuateShardPrm) WithFaultHandler(f func(context.Context, oid.Address, *objectSDK.Object) error) {
p.handler = f
}
// WithAsync sets flag to run evacuate async.
func (p *EvacuateShardPrm) WithAsync(async bool) {
p.async = async
}
// Evacuated returns amount of evacuated objects. // Evacuated returns amount of evacuated objects.
// Objects for which handler returned no error are also assumed evacuated. // Objects for which handler returned no error are also assumed evacuated.
func (p *EvacuateShardRes) Evacuated() uint64 { func (p *EvacuateShardRes) Evacuated() uint64 {
@ -145,20 +125,20 @@ func (e *StorageEngine) Evacuate(ctx context.Context, prm EvacuateShardPrm) (*Ev
default: default:
} }
shardIDs := make([]string, len(prm.shardID)) shardIDs := make([]string, len(prm.ShardID))
for i := range prm.shardID { for i := range prm.ShardID {
shardIDs[i] = prm.shardID[i].String() shardIDs[i] = prm.ShardID[i].String()
} }
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.Evacuate", ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.Evacuate",
trace.WithAttributes( trace.WithAttributes(
attribute.StringSlice("shardIDs", shardIDs), attribute.StringSlice("shardIDs", shardIDs),
attribute.Bool("async", prm.async), attribute.Bool("async", prm.Async),
attribute.Bool("ignoreErrors", prm.ignoreErrors), attribute.Bool("ignoreErrors", prm.IgnoreErrors),
)) ))
defer span.End() defer span.End()
shards, weights, err := e.getActualShards(shardIDs, prm.handler != nil) shards, weights, err := e.getActualShards(shardIDs, prm.Handler != nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -173,7 +153,7 @@ func (e *StorageEngine) Evacuate(ctx context.Context, prm EvacuateShardPrm) (*Ev
} }
res := NewEvacuateShardRes() res := NewEvacuateShardRes()
ctx = ctxOrBackground(ctx, prm.async) ctx = ctxOrBackground(ctx, prm.Async)
eg, egCtx, err := e.evacuateLimiter.TryStart(ctx, shardIDs, res) eg, egCtx, err := e.evacuateLimiter.TryStart(ctx, shardIDs, res)
if err != nil { if err != nil {
return nil, err return nil, err
@ -183,7 +163,7 @@ func (e *StorageEngine) Evacuate(ctx context.Context, prm EvacuateShardPrm) (*Ev
return e.evacuateShards(egCtx, shardIDs, prm, res, shards, weights, shardsToEvacuate) return e.evacuateShards(egCtx, shardIDs, prm, res, shards, weights, shardsToEvacuate)
}) })
if prm.async { if prm.Async {
return nil, nil return nil, nil
} }
@ -204,8 +184,8 @@ func (e *StorageEngine) evacuateShards(ctx context.Context, shardIDs []string, p
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.evacuateShards", ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.evacuateShards",
trace.WithAttributes( trace.WithAttributes(
attribute.StringSlice("shardIDs", shardIDs), attribute.StringSlice("shardIDs", shardIDs),
attribute.Bool("async", prm.async), attribute.Bool("async", prm.Async),
attribute.Bool("ignoreErrors", prm.ignoreErrors), attribute.Bool("ignoreErrors", prm.IgnoreErrors),
)) ))
defer func() { defer func() {
@ -357,7 +337,7 @@ func (e *StorageEngine) evacuateObjects(ctx context.Context, sh *shard.Shard, to
getRes, err := sh.Get(ctx, getPrm) getRes, err := sh.Get(ctx, getPrm)
if err != nil { if err != nil {
if prm.ignoreErrors { if prm.IgnoreErrors {
res.failed.Add(1) res.failed.Add(1)
continue continue
} }
@ -375,13 +355,13 @@ func (e *StorageEngine) evacuateObjects(ctx context.Context, sh *shard.Shard, to
continue continue
} }
if prm.handler == nil { if prm.Handler == nil {
// Do not check ignoreErrors flag here because // Do not check ignoreErrors flag here because
// ignoring errors on put make this command kinda useless. // ignoring errors on put make this command kinda useless.
return fmt.Errorf("%w: %s", errPutShard, toEvacuate[i]) return fmt.Errorf("%w: %s", errPutShard, toEvacuate[i])
} }
err = prm.handler(ctx, addr, getRes.Object()) err = prm.Handler(ctx, addr, getRes.Object())
if err != nil { if err != nil {
e.log.Error(logs.EngineShardsEvacuationFailedToMoveObject, zap.String("address", addr.EncodeToString()), zap.Error(err), evacuationOperationLogField, e.log.Error(logs.EngineShardsEvacuationFailedToMoveObject, zap.String("address", addr.EncodeToString()), zap.Error(err), evacuationOperationLogField,
zap.String("trace_id", tracingPkg.GetTraceID(ctx))) zap.String("trace_id", tracingPkg.GetTraceID(ctx)))

View file

@ -102,7 +102,7 @@ func TestEvacuateShard(t *testing.T) {
checkHasObjects(t) checkHasObjects(t)
var prm EvacuateShardPrm var prm EvacuateShardPrm
prm.WithShardIDList(ids[2:3]) prm.ShardID = ids[2:3]
t.Run("must be read-only", func(t *testing.T) { t.Run("must be read-only", func(t *testing.T) {
res, err := e.Evacuate(context.Background(), prm) res, err := e.Evacuate(context.Background(), prm)
@ -173,13 +173,13 @@ func TestEvacuateNetwork(t *testing.T) {
require.NoError(t, e.shards[evacuateShardID].SetMode(mode.ReadOnly)) require.NoError(t, e.shards[evacuateShardID].SetMode(mode.ReadOnly))
var prm EvacuateShardPrm var prm EvacuateShardPrm
prm.shardID = ids[0:1] prm.ShardID = ids[0:1]
res, err := e.Evacuate(context.Background(), prm) res, err := e.Evacuate(context.Background(), prm)
require.ErrorIs(t, err, errMustHaveTwoShards) require.ErrorIs(t, err, errMustHaveTwoShards)
require.Equal(t, uint64(0), res.Evacuated()) require.Equal(t, uint64(0), res.Evacuated())
prm.handler = acceptOneOf(objects, 2) prm.Handler = acceptOneOf(objects, 2)
res, err = e.Evacuate(context.Background(), prm) res, err = e.Evacuate(context.Background(), prm)
require.ErrorIs(t, err, errReplication) require.ErrorIs(t, err, errReplication)
@ -196,15 +196,15 @@ func TestEvacuateNetwork(t *testing.T) {
require.NoError(t, e.shards[ids[1].String()].SetMode(mode.ReadOnly)) require.NoError(t, e.shards[ids[1].String()].SetMode(mode.ReadOnly))
var prm EvacuateShardPrm var prm EvacuateShardPrm
prm.shardID = ids[1:2] prm.ShardID = ids[1:2]
prm.handler = acceptOneOf(objects, 2) prm.Handler = acceptOneOf(objects, 2)
res, err := e.Evacuate(context.Background(), prm) res, err := e.Evacuate(context.Background(), prm)
require.ErrorIs(t, err, errReplication) require.ErrorIs(t, err, errReplication)
require.Equal(t, uint64(2), res.Evacuated()) require.Equal(t, uint64(2), res.Evacuated())
t.Run("no errors", func(t *testing.T) { t.Run("no errors", func(t *testing.T) {
prm.handler = acceptOneOf(objects, 3) prm.Handler = acceptOneOf(objects, 3)
res, err := e.Evacuate(context.Background(), prm) res, err := e.Evacuate(context.Background(), prm)
require.NoError(t, err) require.NoError(t, err)
@ -233,15 +233,15 @@ func TestEvacuateNetwork(t *testing.T) {
} }
var prm EvacuateShardPrm var prm EvacuateShardPrm
prm.shardID = evacuateIDs prm.ShardID = evacuateIDs
prm.handler = acceptOneOf(objects, totalCount-1) prm.Handler = acceptOneOf(objects, totalCount-1)
res, err := e.Evacuate(context.Background(), prm) res, err := e.Evacuate(context.Background(), prm)
require.ErrorIs(t, err, errReplication) require.ErrorIs(t, err, errReplication)
require.Equal(t, totalCount-1, res.Evacuated()) require.Equal(t, totalCount-1, res.Evacuated())
t.Run("no errors", func(t *testing.T) { t.Run("no errors", func(t *testing.T) {
prm.handler = acceptOneOf(objects, totalCount) prm.Handler = acceptOneOf(objects, totalCount)
res, err := e.Evacuate(context.Background(), prm) res, err := e.Evacuate(context.Background(), prm)
require.NoError(t, err) require.NoError(t, err)
@ -261,8 +261,8 @@ func TestEvacuateCancellation(t *testing.T) {
require.NoError(t, e.shards[ids[1].String()].SetMode(mode.ReadOnly)) require.NoError(t, e.shards[ids[1].String()].SetMode(mode.ReadOnly))
var prm EvacuateShardPrm var prm EvacuateShardPrm
prm.shardID = ids[1:2] prm.ShardID = ids[1:2]
prm.handler = func(ctx context.Context, a oid.Address, o *objectSDK.Object) error { prm.Handler = func(ctx context.Context, a oid.Address, o *objectSDK.Object) error {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return ctx.Err() return ctx.Err()
@ -292,8 +292,8 @@ func TestEvacuateSingleProcess(t *testing.T) {
running := make(chan interface{}) running := make(chan interface{})
var prm EvacuateShardPrm var prm EvacuateShardPrm
prm.shardID = ids[1:2] prm.ShardID = ids[1:2]
prm.handler = func(ctx context.Context, a oid.Address, o *objectSDK.Object) error { prm.Handler = func(ctx context.Context, a oid.Address, o *objectSDK.Object) error {
select { select {
case <-running: case <-running:
default: default:
@ -334,8 +334,8 @@ func TestEvacuateAsync(t *testing.T) {
running := make(chan interface{}) running := make(chan interface{})
var prm EvacuateShardPrm var prm EvacuateShardPrm
prm.shardID = ids[1:2] prm.ShardID = ids[1:2]
prm.handler = func(ctx context.Context, a oid.Address, o *objectSDK.Object) error { prm.Handler = func(ctx context.Context, a oid.Address, o *objectSDK.Object) error {
select { select {
case <-running: case <-running:
default: default:

View file

@ -25,10 +25,11 @@ func (s *Server) EvacuateShard(ctx context.Context, req *control.EvacuateShardRe
return nil, status.Error(codes.PermissionDenied, err.Error()) return nil, status.Error(codes.PermissionDenied, err.Error())
} }
var prm engine.EvacuateShardPrm prm := engine.EvacuateShardPrm{
prm.WithShardIDList(s.getShardIDList(req.GetBody().GetShard_ID())) ShardID: s.getShardIDList(req.GetBody().GetShard_ID()),
prm.WithIgnoreErrors(req.GetBody().GetIgnoreErrors()) IgnoreErrors: req.GetBody().GetIgnoreErrors(),
prm.WithFaultHandler(s.replicate) Handler: s.replicate,
}
res, err := s.s.Evacuate(ctx, prm) res, err := s.s.Evacuate(ctx, prm)
if err != nil { if err != nil {

View file

@ -17,11 +17,12 @@ func (s *Server) StartShardEvacuation(ctx context.Context, req *control.StartSha
return nil, status.Error(codes.PermissionDenied, err.Error()) return nil, status.Error(codes.PermissionDenied, err.Error())
} }
var prm engine.EvacuateShardPrm prm := engine.EvacuateShardPrm{
prm.WithShardIDList(s.getShardIDList(req.GetBody().GetShard_ID())) ShardID: s.getShardIDList(req.GetBody().GetShard_ID()),
prm.WithIgnoreErrors(req.GetBody().GetIgnoreErrors()) IgnoreErrors: req.GetBody().GetIgnoreErrors(),
prm.WithFaultHandler(s.replicate) Handler: s.replicate,
prm.WithAsync(true) Async: true,
}
_, err = s.s.Evacuate(ctx, prm) _, err = s.s.Evacuate(ctx, prm)
if err != nil { if err != nil {