Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 6 additions & 21 deletions pkg/local_object_storage/metabase/counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"

"github.com/nspcc-dev/bbolt"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
)
Expand Down Expand Up @@ -135,31 +134,17 @@ func syncCounter(tx *bbolt.Tx, force bool) error {
return nil
}

var addr oid.Address
var phyCounter uint64
var logicCounter uint64

err = iteratePhyObjects(tx, func(cnr cid.ID, obj oid.ID) error {
err = iteratePhyObjects(tx, func(c *bbolt.Cursor, obj oid.ID) error {
phyCounter++

addr.SetContainer(cnr)
addr.SetObject(obj)

metaBucket := tx.Bucket(metaBucketKey(cnr))
if metaBucket != nil {
var (
metaCursor = metaBucket.Cursor()
typPrefix = make([]byte, metaIDTypePrefixSize)
)

fillIDTypePrefix(typPrefix)

typ, err := fetchTypeForID(metaCursor, typPrefix, obj)
// check if an object is available: not with GCMark
// and not covered with a tombstone
if inGarbage(metaCursor, obj) == statusAvailable && err == nil && typ == object.TypeRegular {
logicCounter++
}
typ, err := fetchTypeForID(c, obj)
// check if an object is available: not with GCMark
// and not covered with a tombstone
if inGarbage(c, obj) == statusAvailable && err == nil && typ == object.TypeRegular {
logicCounter++
}

return nil
Expand Down
27 changes: 7 additions & 20 deletions pkg/local_object_storage/metabase/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (

"github.com/nspcc-dev/bbolt"
iec "github.com/nspcc-dev/neofs-node/internal/ec"
islices "github.com/nspcc-dev/neofs-node/internal/slices"
objectcore "github.com/nspcc-dev/neofs-node/pkg/core/object"
storagelog "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/internal/log"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/object"
Expand Down Expand Up @@ -216,40 +214,29 @@ func supplementRemovedObjects(tx *bbolt.Tx, addrs []oid.Address) ([]RemovedObjec
func supplementRemovedECParts(res []RemovedObject, cnrMetaCrs *bbolt.Cursor, addrs []oid.Address, addr oid.Address) ([]RemovedObject, error) {
cnr := addr.Container()
parent := addr.Object()
pref := slices.Concat([]byte{metaPrefixAttrIDPlain}, []byte(object.FilterParentID), objectcore.MetaAttributeDelimiter,
parent[:], objectcore.MetaAttributeDelimiter,
)

var partCrs *bbolt.Cursor
var ecPref []byte
for k, _ := cnrMetaCrs.Seek(pref); ; k, _ = cnrMetaCrs.Next() {
partID, ok := bytes.CutPrefix(k, pref)
if !ok {
break
}
if len(partID) != oid.Size {
return nil, invalidMetaBucketKeyErr(k, fmt.Errorf("wrong OID len %d", len(partID)))
}
if islices.AllZeros(partID) {
return nil, invalidMetaBucketKeyErr(k, oid.ErrZero)
for id := range iterAttrVal(cnrMetaCrs, object.FilterParentID, parent[:]) {
if id.IsZero() {
return nil, fmt.Errorf("invalid child of %s parent: %w", parent, oid.ErrZero)
}

if partCrs == nil {
partCrs = cnrMetaCrs.Bucket().Cursor()
}

if ecPref == nil {
ecPref = slices.Concat([]byte{metaPrefixIDAttr}, partID, []byte(iec.AttributePrefix)) // any of EC attributes
ecPref = slices.Concat([]byte{metaPrefixIDAttr}, id[:], []byte(iec.AttributePrefix)) // any of EC attributes
} else {
copy(ecPref[1:], partID)
copy(ecPref[1:], id[:])
}

if k, _ = partCrs.Seek(ecPref); !bytes.HasPrefix(k, ecPref) {
k, _ := partCrs.Seek(ecPref)
if !bytes.HasPrefix(k, ecPref) {
continue
}

id := oid.ID(partID)

if !slices.ContainsFunc(addrs, func(addr oid.Address) bool { return addr.Container() == cnr && addr.Object() == id }) {
res = append(res, RemovedObject{
Address: oid.NewAddress(cnr, id),
Expand Down
102 changes: 44 additions & 58 deletions pkg/local_object_storage/metabase/ec.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/nspcc-dev/bbolt"
iec "github.com/nspcc-dev/neofs-node/internal/ec"
ierrors "github.com/nspcc-dev/neofs-node/internal/errors"
islices "github.com/nspcc-dev/neofs-node/internal/slices"
objectcore "github.com/nspcc-dev/neofs-node/pkg/core/object"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
Expand Down Expand Up @@ -54,7 +53,13 @@ func (db *DB) ResolveECPart(cnr cid.ID, parent oid.ID, pi iec.PartInfo) (oid.ID,
var res oid.ID
err := db.boltDB.View(func(tx *bbolt.Tx) error {
var err error
res, err = db.resolveECPartTx(tx, cnr, parent, pi)

metaBkt := tx.Bucket(metaBucketKey(cnr))
if metaBkt == nil {
return apistatus.ErrObjectNotFound
}

res, err = db.resolveECPartInMetaBucket(metaBkt.Cursor(), parent, pi)
return err
})
return res, err
Expand All @@ -74,21 +79,20 @@ func (db *DB) ResolveECPartWithPayloadLen(cnr cid.ID, parent oid.ID, pi iec.Part

err := db.boltDB.View(func(tx *bbolt.Tx) error {
var err error
id, ln, err = db.resolveECPartWithPayloadLenTx(tx, cnr, parent, pi)

metaBkt := tx.Bucket(metaBucketKey(cnr))
if metaBkt == nil {
return apistatus.ErrObjectNotFound
}

id, ln, err = db.resolveECPartWithPayloadLen(metaBkt.Cursor(), parent, pi)
return err
})

return id, ln, err
}

func (db *DB) resolveECPartWithPayloadLenTx(tx *bbolt.Tx, cnr cid.ID, parent oid.ID, pi iec.PartInfo) (oid.ID, uint64, error) {
metaBkt := tx.Bucket(metaBucketKey(cnr))
if metaBkt == nil {
return oid.ID{}, 0, apistatus.ErrObjectNotFound
}

metaBktCrs := metaBkt.Cursor()

func (db *DB) resolveECPartWithPayloadLen(metaBktCrs *bbolt.Cursor, parent oid.ID, pi iec.PartInfo) (oid.ID, uint64, error) {
id, err := db.resolveECPartInMetaBucket(metaBktCrs, parent, pi)
if err != nil {
return oid.ID{}, 0, err
Expand All @@ -111,15 +115,6 @@ func (db *DB) resolveECPartWithPayloadLenTx(tx *bbolt.Tx, cnr cid.ID, parent oid
return id, ln, nil
}

func (db *DB) resolveECPartTx(tx *bbolt.Tx, cnr cid.ID, parent oid.ID, pi iec.PartInfo) (oid.ID, error) {
metaBkt := tx.Bucket(metaBucketKey(cnr))
if metaBkt == nil {
return oid.ID{}, apistatus.ErrObjectNotFound
}

return db.resolveECPartInMetaBucket(metaBkt.Cursor(), parent, pi)
}

func (db *DB) resolveECPartInMetaBucket(crs *bbolt.Cursor, parent oid.ID, pi iec.PartInfo) (oid.ID, error) {
metaBkt := crs.Bucket()

Expand All @@ -132,38 +127,13 @@ func (db *DB) resolveECPartInMetaBucket(crs *bbolt.Cursor, parent oid.ID, pi iec
return oid.ID{}, ErrObjectIsExpired
}

pref := slices.Concat([]byte{metaPrefixAttrIDPlain}, []byte(object.FilterParentID), objectcore.MetaAttributeDelimiter,
parent[:], objectcore.MetaAttributeDelimiter,
)

var partCrs *bbolt.Cursor
var rulePref, partPref, typePref []byte
var sizeSplitInfo *object.SplitInfo
isParent := false
for k, _ := crs.Seek(pref); ; k, _ = crs.Next() {
partID, ok := bytes.CutPrefix(k, pref)
if !ok {
if !isParent { // neither tombstone nor lock can be a parent
if typePref == nil {
typePref = make([]byte, metaIDTypePrefixSize)
fillIDTypePrefix(typePref)
}
if typ, err := fetchTypeForID(crs, typePref, parent); err == nil && (typ == object.TypeTombstone || typ == object.TypeLock || typ == object.TypeLink) {
return parent, nil
}
}

if sizeSplitInfo != nil {
return oid.ID{}, object.NewSplitInfoError(sizeSplitInfo)
}

return oid.ID{}, apistatus.ErrObjectNotFound
}
if len(partID) != oid.Size {
return oid.ID{}, invalidMetaBucketKeyErr(k, fmt.Errorf("wrong OID len %d", len(partID)))
}
if islices.AllZeros(partID) {
return oid.ID{}, invalidMetaBucketKeyErr(k, oid.ErrZero)
for id := range iterAttrVal(crs, object.FilterParentID, parent[:]) {
if id.IsZero() {
return oid.ID{}, fmt.Errorf("invalid child of %s parent: %w", parent, oid.ErrZero)
}

isParent = true
Expand All @@ -174,44 +144,60 @@ func (db *DB) resolveECPartInMetaBucket(crs *bbolt.Cursor, parent oid.ID, pi iec

if rulePref == nil {
// TODO: make and reuse one buffer for all keys
rulePref = slices.Concat([]byte{metaPrefixIDAttr}, partID, []byte(iec.AttributeRuleIdx), objectcore.MetaAttributeDelimiter, []byte(strconv.Itoa(pi.RuleIndex)))
rulePref = slices.Concat([]byte{metaPrefixIDAttr}, id[:], []byte(iec.AttributeRuleIdx), objectcore.MetaAttributeDelimiter, []byte(strconv.Itoa(pi.RuleIndex)))
} else {
copy(rulePref[1:], partID)
copy(rulePref[1:], id[:])
}
if k, _ = partCrs.Seek(rulePref); !bytes.Equal(k, rulePref) { // Cursor.Seek is more lightweight than Bucket.Get making cursor inside
k, _ := partCrs.Seek(rulePref)
if !bytes.Equal(k, rulePref) { // Cursor.Seek is more lightweight than Bucket.Get making cursor inside
if typePref == nil {
typePref = make([]byte, metaIDTypePrefixSize)
fillIDTypePrefix(typePref)
}
if typ, err := fetchTypeForID(partCrs, typePref, oid.ID(partID)); err == nil && typ == object.TypeLink {
if typ, err := fetchTypeForIDWBuf(partCrs, typePref, id); err == nil && typ == object.TypeLink {
if sizeSplitInfo == nil {
sizeSplitInfo = new(object.SplitInfo)
}

sizeSplitInfo.SetLink(oid.ID(partID))
sizeSplitInfo.SetLink(id)

return oid.ID{}, object.NewSplitInfoError(sizeSplitInfo)
}

if (sizeSplitInfo == nil || sizeSplitInfo.GetLastPart().IsZero()) && getObjAttribute(partCrs, oid.ID(partID), object.FilterFirstSplitObject) != nil {
if (sizeSplitInfo == nil || sizeSplitInfo.GetLastPart().IsZero()) && getObjAttribute(partCrs, id, object.FilterFirstSplitObject) != nil {
if sizeSplitInfo == nil {
sizeSplitInfo = new(object.SplitInfo)
}

sizeSplitInfo.SetLastPart(oid.ID(partID))
sizeSplitInfo.SetLastPart(id)
// continue because next item may be a linker. If so, it's the most informative
}

continue
}

if partPref == nil {
partPref = slices.Concat([]byte{metaPrefixIDAttr}, partID, []byte(iec.AttributePartIdx), objectcore.MetaAttributeDelimiter, []byte(strconv.Itoa(pi.Index)))
partPref = slices.Concat([]byte{metaPrefixIDAttr}, id[:], []byte(iec.AttributePartIdx), objectcore.MetaAttributeDelimiter, []byte(strconv.Itoa(pi.Index)))
} else {
copy(partPref[1:], partID)
copy(partPref[1:], id[:])
}
if k, _ = partCrs.Seek(partPref); bytes.Equal(k, partPref) {
return oid.ID(partID), nil
return id, nil
}
}
if !isParent { // neither tombstone nor lock can be a parent
if typePref == nil {
typePref = make([]byte, metaIDTypePrefixSize)
fillIDTypePrefix(typePref)
}
if typ, err := fetchTypeForIDWBuf(crs, typePref, parent); err == nil && (typ == object.TypeTombstone || typ == object.TypeLock || typ == object.TypeLink) {
return parent, nil
}
}

if sizeSplitInfo != nil {
return oid.ID{}, object.NewSplitInfoError(sizeSplitInfo)
}

return oid.ID{}, apistatus.ErrObjectNotFound
}
4 changes: 2 additions & 2 deletions pkg/local_object_storage/metabase/ec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ func TestDB_ResolveECPart(t *testing.T) {
// broken data
tcs = append(tcs, testcase{
name: "wrong OID len in parent index", assertErr: func(t *testing.T, err error) {
require.EqualError(t, err, "invalid meta bucket key (prefix 0x2): wrong OID len 31")
require.ErrorIs(t, err, apistatus.ErrObjectNotFound)
}, preset: func(t *testing.T) *meta.DB {
return presetBoltDB(t, func(tx *bbolt.Tx) error {
b, err := tx.CreateBucketIfNotExists(slices.Concat([]byte{0xFF}, cnr[:]))
Expand All @@ -213,7 +213,7 @@ func TestDB_ResolveECPart(t *testing.T) {
})
}}, testcase{
name: "zero OID in parent index", assertErr: func(t *testing.T, err error) {
require.EqualError(t, err, "invalid meta bucket key (prefix 0x2): zero object ID")
require.ErrorIs(t, err, oid.ErrZero)
}, preset: func(t *testing.T) *meta.DB {
return presetBoltDB(t, func(tx *bbolt.Tx) error {
b, err := tx.CreateBucketIfNotExists(slices.Concat([]byte{0xFF}, cnr[:]))
Expand Down
44 changes: 9 additions & 35 deletions pkg/local_object_storage/metabase/exists.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ func (db *DB) exists(tx *bbolt.Tx, addr oid.Address, currEpoch uint64, checkPare
return false, ErrObjectIsExpired
}

var objKeyBuf = make([]byte, metaIDTypePrefixSize)

if checkParent {
err := getParentInfo(metaCursor, cnr, id)
if err != nil {
Expand All @@ -102,8 +100,7 @@ func (db *DB) exists(tx *bbolt.Tx, addr oid.Address, currEpoch uint64, checkPare
}
}

fillIDTypePrefix(objKeyBuf)
_, err := fetchTypeForID(metaCursor, objKeyBuf, id)
_, err := fetchTypeForID(metaCursor, id)
return err == nil, nil
}

Expand Down Expand Up @@ -170,17 +167,9 @@ func getParentID(metaCursor *bbolt.Cursor, objID oid.ID) oid.ID {
// the same attribute and value. Obviously this only makes sense if the parent
// is the same, that is attribute is either a first object ID or a split ID.
func seekForParentViaAttribute(metaCursor *bbolt.Cursor, attr string, val []byte) oid.ID {
var (
idCursor = metaCursor.Bucket().Cursor()
pref = slices.Concat([]byte{metaPrefixAttrIDPlain}, []byte(attr),
objectcore.MetaAttributeDelimiter, val, objectcore.MetaAttributeDelimiter)
)
var idCursor = metaCursor.Bucket().Cursor()

for k, _ := metaCursor.Seek(pref); bytes.HasPrefix(k, pref); k, _ = metaCursor.Next() {
child, err := oid.DecodeBytes(k[len(pref):])
if err != nil {
continue
}
for child := range iterAttrVal(metaCursor, attr, val) {
parent := getParentID(idCursor, child)
if !parent.IsZero() {
return parent
Expand Down Expand Up @@ -254,33 +243,18 @@ func inGarbage(metaCursor *bbolt.Cursor, id oid.ID) uint8 {
// - [ErrParts] if object is EC.
func getParentInfo(metaCursor *bbolt.Cursor, cnr cid.ID, parentID oid.ID) error {
var (
splitInfo *object.SplitInfo
ecParts []oid.ID
parentPrefix = getParentMetaOwnersPrefix(parentID)
splitInfo *object.SplitInfo
ecParts []oid.ID
)

loop:
for k, _ := metaCursor.Seek(parentPrefix); bytes.HasPrefix(k, parentPrefix); k, _ = metaCursor.Next() {
objID, err := oid.DecodeBytes(k[len(parentPrefix):])
if err != nil {
return fmt.Errorf("invalid oid with %s parent in %s container: %w", parentID, cnr, err)
}
var (
objCur = metaCursor.Bucket().Cursor()
objPrefix = slices.Concat([]byte{metaPrefixIDAttr}, objID[:])
isLink bool
isV1 bool
isEmpty bool
)
for objID := range iterAttrVal(metaCursor, object.FilterParentID, parentID[:]) {
var isEmpty, isLink, isV1 bool

if splitInfo == nil {
splitInfo = object.NewSplitInfo()
}
for ak, _ := objCur.Seek(objPrefix); bytes.HasPrefix(ak, objPrefix); ak, _ = objCur.Next() {
attrKey, attrVal, ok := bytes.Cut(ak[len(objPrefix):], objectcore.MetaAttributeDelimiter)
if !ok {
return fmt.Errorf("invalid attribute in meta of %s/%s: missing delimiter", cnr, objID)
}

for attrKey, attrVal := range iterIDAttrs(metaCursor.Bucket().Cursor(), objID) {
if strings.HasPrefix(string(attrKey), iec.AttributePrefix) {
ecParts = append(ecParts, objID)
continue loop
Expand Down
Loading
Loading