forked from TrueCloudLab/frostfs-node
[#1527] engine: Add tests for handling expired objects on inhume and lock
Currently, it's allowed to inhume or lock an expired object. Consider the following scenario: 1) An user inhumes or locks an object 2) The object expires 3) GC hasn't yet deleted the object 4) The node loses the associated tombstone or lock 5) Another node replicates tombstone or lock to the first node In this case, the second node succeeds, which is the desired behavior. Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
This commit is contained in:
parent
9cabca9dfe
commit
432042c534
2 changed files with 106 additions and 0 deletions
|
@ -2,13 +2,17 @@ package engine
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||
"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/shard"
|
||||
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
|
||||
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -84,3 +88,52 @@ func TestStorageEngine_Inhume(t *testing.T) {
|
|||
require.Empty(t, addrs)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInhumeExpiredRegularObject(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const currEpoch = 42
|
||||
const objectExpiresAfter = currEpoch - 1
|
||||
|
||||
engine := testNewEngine(t).setShardsNumAdditionalOpts(t, 1, func(_ int) []shard.Option {
|
||||
return []shard.Option{
|
||||
shard.WithDisabledGC(),
|
||||
shard.WithMetaBaseOptions(append(
|
||||
testGetDefaultMetabaseOptions(t),
|
||||
meta.WithEpochState(epochState{currEpoch}),
|
||||
)...),
|
||||
}
|
||||
}).prepare(t).engine
|
||||
|
||||
cnr := cidtest.ID()
|
||||
|
||||
generateAndPutObject := func() *objectSDK.Object {
|
||||
obj := testutil.GenerateObjectWithCID(cnr)
|
||||
testutil.AddAttribute(obj, objectV2.SysAttributeExpEpoch, strconv.Itoa(objectExpiresAfter))
|
||||
|
||||
var putPrm PutPrm
|
||||
putPrm.Object = obj
|
||||
require.NoError(t, engine.Put(context.Background(), putPrm))
|
||||
return obj
|
||||
}
|
||||
|
||||
t.Run("inhume with tombstone", func(t *testing.T) {
|
||||
obj := generateAndPutObject()
|
||||
ts := oidtest.Address()
|
||||
ts.SetContainer(cnr)
|
||||
|
||||
var prm InhumePrm
|
||||
prm.WithTarget(ts, object.AddressOf(obj))
|
||||
_, err := engine.Inhume(context.Background(), prm)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("inhume without tombstone", func(t *testing.T) {
|
||||
obj := generateAndPutObject()
|
||||
|
||||
var prm InhumePrm
|
||||
prm.MarkAsGarbage(object.AddressOf(obj))
|
||||
_, err := engine.Inhume(context.Background(), prm)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -292,3 +292,56 @@ func TestLockForceRemoval(t *testing.T) {
|
|||
_, err = e.Inhume(context.Background(), inhumePrm)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestLockExpiredRegularObject(t *testing.T) {
|
||||
const currEpoch = 42
|
||||
const objectExpiresAfter = currEpoch - 1
|
||||
|
||||
engine := testNewEngine(t).setShardsNumAdditionalOpts(t, 1, func(_ int) []shard.Option {
|
||||
return []shard.Option{
|
||||
shard.WithDisabledGC(),
|
||||
shard.WithMetaBaseOptions(append(
|
||||
testGetDefaultMetabaseOptions(t),
|
||||
meta.WithEpochState(epochState{currEpoch}),
|
||||
)...),
|
||||
}
|
||||
}).prepare(t).engine
|
||||
|
||||
cnr := cidtest.ID()
|
||||
|
||||
object := testutil.GenerateObjectWithCID(cnr)
|
||||
testutil.AddAttribute(object, objectV2.SysAttributeExpEpoch, strconv.Itoa(objectExpiresAfter))
|
||||
|
||||
address := objectcore.AddressOf(object)
|
||||
|
||||
var putPrm PutPrm
|
||||
putPrm.Object = object
|
||||
require.NoError(t, engine.Put(context.Background(), putPrm))
|
||||
|
||||
var getPrm GetPrm
|
||||
var errNotFound *apistatus.ObjectNotFound
|
||||
|
||||
getPrm.WithAddress(address)
|
||||
_, err := engine.Get(context.Background(), getPrm)
|
||||
require.ErrorAs(t, err, &errNotFound)
|
||||
|
||||
t.Run("lock expired regular object", func(t *testing.T) {
|
||||
engine.Lock(context.Background(),
|
||||
address.Container(),
|
||||
oidtest.ID(),
|
||||
[]oid.ID{address.Object()},
|
||||
)
|
||||
|
||||
res, err := engine.IsLocked(context.Background(), objectcore.AddressOf(object))
|
||||
require.NoError(t, err)
|
||||
require.True(t, res)
|
||||
})
|
||||
|
||||
t.Run("get expired and locked regular object", func(t *testing.T) {
|
||||
getPrm.WithAddress(objectcore.AddressOf(object))
|
||||
|
||||
res, err := engine.Get(context.Background(), getPrm)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, res.Object(), object)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue