frostfs-node/pkg/local_object_storage/metabase/iterators_test.go
Aleksey Savchuk 8ad8e2783a
[#1445] metabase: Add IterateLocked method
Added a new method for iterating locked objects. It's indended
to be used in the garbage collector for handling expired locks.

Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
2024-12-17 10:40:30 +03:00

154 lines
4.1 KiB
Go

package meta_test
import (
"context"
"math/rand"
"strconv"
"testing"
object2 "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"
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"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"
)
func TestDB_IterateExpired(t *testing.T) {
db := newDB(t)
defer func() { require.NoError(t, db.Close(context.Background())) }()
const epoch = 13
const lockExpiresAfter = 1000
mAlive := map[objectSDK.Type]oid.Address{}
mExpired := map[objectSDK.Type]oid.Address{}
for _, typ := range []objectSDK.Type{
objectSDK.TypeRegular,
objectSDK.TypeTombstone,
objectSDK.TypeLock,
} {
mAlive[typ] = putWithExpiration(t, db, typ, epoch)
mExpired[typ] = putWithExpiration(t, db, typ, epoch-1)
}
expiredLocked := putWithExpiration(t, db, objectSDK.TypeRegular, epoch-1)
require.NoError(t, db.Lock(context.Background(), expiredLocked.Container(), oidtest.ID(), []oid.ID{expiredLocked.Object()}, lockExpiresAfter))
err := db.IterateExpired(context.Background(), epoch, func(exp *meta.ExpiredObject) error {
if addr, ok := mAlive[exp.Type()]; ok {
require.NotEqual(t, addr, exp.Address())
}
require.NotEqual(t, expiredLocked, exp.Address())
addr, ok := mExpired[exp.Type()]
require.True(t, ok)
require.Equal(t, addr, exp.Address())
delete(mExpired, exp.Type())
return nil
})
require.NoError(t, err)
require.Empty(t, mExpired)
}
func putWithExpiration(t *testing.T, db *meta.DB, typ objectSDK.Type, expiresAt uint64) oid.Address {
obj := testutil.GenerateObject()
obj.SetType(typ)
testutil.AddAttribute(obj, objectV2.SysAttributeExpEpoch, strconv.FormatUint(expiresAt, 10))
require.NoError(t, putBig(db, obj))
return object2.AddressOf(obj)
}
func TestIterateLocked(t *testing.T) {
t.Parallel()
const (
numObjects = 10
numLocksPerObject = 10
)
db := newDB(t)
defer func() { require.NoError(t, db.Close(context.Background())) }()
m := make(map[oid.Address][]meta.Lock)
// every two objects have same container
for range numObjects / 2 {
container := cidtest.ID()
object := testutil.GenerateObjectWithCID(container)
require.NoError(t, metaPut(db, object, nil))
addr := object2.AddressOf(object)
m[addr] = []meta.Lock{}
object = testutil.GenerateObjectWithCID(container)
require.NoError(t, metaPut(db, object, nil))
addr = object2.AddressOf(object)
m[addr] = []meta.Lock{}
}
for addr := range m {
// every object has locks of both old and new formats
for range numLocksPerObject / 2 {
m[addr] = append(m[addr],
meta.Lock{
ID: oidtest.ID(),
ExpEpoch: rand.Uint64(),
}, meta.Lock{
ID: oidtest.ID(),
ExpEpoch: meta.NoExpirationEpoch,
},
)
}
for _, lock := range m[addr] {
require.NoError(t, db.Lock(
context.Background(),
addr.Container(),
lock.ID,
[]oid.ID{addr.Object()},
lock.ExpEpoch,
))
}
}
t.Run("just iterate", func(t *testing.T) {
err := db.IterateLocked(context.Background(), nil, func(lo *meta.LockedObject) error {
locks, ok := m[lo.Address()]
require.True(t, ok)
require.ElementsMatch(t, locks, lo.Locks())
return nil
})
require.NoError(t, err)
})
t.Run("iterate with offset", func(t *testing.T) {
var foundObjects []oid.Address
var offset *oid.Address
for didStep := true; didStep; {
didStep = false
err := db.IterateLocked(context.Background(), offset, func(lo *meta.LockedObject) error {
addr := lo.Address()
foundObjects = append(foundObjects, addr)
offset = &addr
didStep = true
return meta.ErrInterruptIterator
})
require.NoError(t, err)
}
require.ElementsMatch(t, foundObjects, maps.Keys(m))
})
}