[#1585] metabase: Add missing expiration epoch for object stored with EC
All checks were successful
DCO action / DCO (pull_request) Successful in 17m11s
Pre-commit hooks / Pre-commit (pull_request) Successful in 18m42s
Vulncheck / Vulncheck (pull_request) Successful in 19m49s
Tests and linters / Run gofumpt (pull_request) Successful in 20m25s
Tests and linters / Tests (pull_request) Successful in 20m34s
Build / Build Components (pull_request) Successful in 21m8s
Tests and linters / Lint (pull_request) Successful in 21m11s
Tests and linters / Tests with -race (pull_request) Successful in 21m22s
Tests and linters / Staticcheck (pull_request) Successful in 21m31s
Tests and linters / gopls check (pull_request) Successful in 22m18s
All checks were successful
DCO action / DCO (pull_request) Successful in 17m11s
Pre-commit hooks / Pre-commit (pull_request) Successful in 18m42s
Vulncheck / Vulncheck (pull_request) Successful in 19m49s
Tests and linters / Run gofumpt (pull_request) Successful in 20m25s
Tests and linters / Tests (pull_request) Successful in 20m34s
Build / Build Components (pull_request) Successful in 21m8s
Tests and linters / Lint (pull_request) Successful in 21m11s
Tests and linters / Tests with -race (pull_request) Successful in 21m22s
Tests and linters / Staticcheck (pull_request) Successful in 21m31s
Tests and linters / gopls check (pull_request) Successful in 22m18s
Suppose there's a complex object stored with EC. It's divided into parts, and these parts are further divided into chunks, except for the linking object. The chunks and linking object are stored in the metabase. An expiration epoch of the original object should be stored in the expired objects index. In the described scenario, there's no way to determine the expiration epoch of the object from its chunks because a chunk's parent is a part of the original object, not the original object itself. However, the epoch can be determined from the linking object. Previously, whether the epoch was stored or not depended on the order in which the chunks and linking object were written. Now it's fixed. The absense of the expiration epoch prevented the GC from deleting this object upon its expiration. Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
This commit is contained in:
parent
fa08bfa553
commit
d4ff44ef83
2 changed files with 70 additions and 1 deletions
|
@ -2,12 +2,18 @@ package meta_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil"
|
||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||
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"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/erasurecode"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/transformer"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -92,3 +98,59 @@ func getAddressSafe(t *testing.T, o *objectSDK.Object) oid.Address {
|
|||
addr.SetObject(id)
|
||||
return addr
|
||||
}
|
||||
|
||||
func TestPutExpiredComplexObjectWithEC(t *testing.T) {
|
||||
const (
|
||||
currEpoch = 100
|
||||
expEpoch = currEpoch - 1
|
||||
partSize = 1 << 10
|
||||
partCount = 5
|
||||
dataCount = 2
|
||||
parityCount = 1
|
||||
)
|
||||
|
||||
db := newDB(t, meta.WithEpochState(epochState{currEpoch}))
|
||||
defer func() { require.NoError(t, db.Close(context.Background())) }()
|
||||
|
||||
cntr := cidtest.ID()
|
||||
|
||||
parent := testutil.GenerateObjectWithCID(cntr)
|
||||
testutil.AddAttribute(parent, objectV2.SysAttributeExpEpoch, strconv.Itoa(expEpoch))
|
||||
|
||||
key, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
var target testTarget
|
||||
ids := cutObject(t, transformer.NewPayloadSizeLimiter(transformer.Params{
|
||||
Key: &key.PrivateKey,
|
||||
NextTargetInit: func() transformer.ObjectWriter { return &target },
|
||||
NetworkState: epochState{currEpoch},
|
||||
MaxSize: partSize,
|
||||
}), parent, partCount*partSize)
|
||||
|
||||
n := len(target.objects)
|
||||
objects, link := target.objects[:n-1], target.objects[n-1]
|
||||
|
||||
ecc, err := erasurecode.NewConstructor(dataCount, parityCount)
|
||||
require.NoError(t, err)
|
||||
|
||||
var chunks []*objectSDK.Object
|
||||
for _, object := range objects {
|
||||
objectChunks, err := ecc.Split(object, &key.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
chunks = append(chunks, objectChunks...)
|
||||
}
|
||||
|
||||
// link mustn't be first, it's needed to reproduces the bug
|
||||
for _, chunk := range chunks {
|
||||
require.NoError(t, metaPut(db, chunk, nil))
|
||||
}
|
||||
require.NoError(t, metaPut(db, link, nil))
|
||||
|
||||
var addr oid.Address
|
||||
addr.SetContainer(cntr)
|
||||
addr.SetObject(*ids.ParentID)
|
||||
|
||||
_, err = metaGet(db, addr, false)
|
||||
require.ErrorIs(t, err, meta.ErrObjectIsExpired)
|
||||
}
|
||||
|
|
|
@ -159,7 +159,14 @@ func (db *DB) updateObj(tx *bbolt.Tx, obj *objectSDK.Object, id []byte, si *obje
|
|||
// a linking object to put (or vice versa), we should update split info
|
||||
// with object ids of these objects
|
||||
if isParent {
|
||||
return updateSplitInfo(tx, objectCore.AddressOf(obj), si)
|
||||
addr := objectCore.AddressOf(obj)
|
||||
|
||||
objKey := objectKey(addr.Object(), make([]byte, objectKeySize))
|
||||
if err := putExpirationEpoch(tx, obj, addr, objKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return updateSplitInfo(tx, addr, si)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
Loading…
Reference in a new issue