diff --git a/pkg/local_object_storage/metabase/counter.go b/pkg/local_object_storage/metabase/counter.go index 933387e22b..8543636aec 100644 --- a/pkg/local_object_storage/metabase/counter.go +++ b/pkg/local_object_storage/metabase/counter.go @@ -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" ) @@ -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 diff --git a/pkg/local_object_storage/metabase/delete.go b/pkg/local_object_storage/metabase/delete.go index 650090dbdb..f3afc8c2b7 100644 --- a/pkg/local_object_storage/metabase/delete.go +++ b/pkg/local_object_storage/metabase/delete.go @@ -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" @@ -216,22 +214,12 @@ 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 { @@ -239,17 +227,16 @@ func supplementRemovedECParts(res []RemovedObject, cnrMetaCrs *bbolt.Cursor, add } 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), diff --git a/pkg/local_object_storage/metabase/ec.go b/pkg/local_object_storage/metabase/ec.go index 4095b6d732..58db22e845 100644 --- a/pkg/local_object_storage/metabase/ec.go +++ b/pkg/local_object_storage/metabase/ec.go @@ -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" @@ -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 @@ -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 @@ -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() @@ -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 @@ -174,31 +144,32 @@ 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 } @@ -206,12 +177,27 @@ func (db *DB) resolveECPartInMetaBucket(crs *bbolt.Cursor, parent oid.ID, pi iec } 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 } diff --git a/pkg/local_object_storage/metabase/ec_test.go b/pkg/local_object_storage/metabase/ec_test.go index 460ad9e65e..60c9a9cc3c 100644 --- a/pkg/local_object_storage/metabase/ec_test.go +++ b/pkg/local_object_storage/metabase/ec_test.go @@ -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[:])) @@ -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[:])) diff --git a/pkg/local_object_storage/metabase/exists.go b/pkg/local_object_storage/metabase/exists.go index 514678980b..9ef8440758 100644 --- a/pkg/local_object_storage/metabase/exists.go +++ b/pkg/local_object_storage/metabase/exists.go @@ -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 { @@ -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 } @@ -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 @@ -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 diff --git a/pkg/local_object_storage/metabase/get.go b/pkg/local_object_storage/metabase/get.go index 93396d4014..df4a3254ed 100644 --- a/pkg/local_object_storage/metabase/get.go +++ b/pkg/local_object_storage/metabase/get.go @@ -51,7 +51,13 @@ func (db *DB) Get(addr oid.Address, raw bool) (*object.Object, error) { ) err = db.boltDB.View(func(tx *bbolt.Tx) error { - hdr, err = get(tx, addr, true, raw, currEpoch) + var metaBucket = tx.Bucket(metaBucketKey(addr.Container())) + + if metaBucket == nil { + return logicerr.Wrap(apistatus.ObjectNotFound{}) + } + + hdr, err = get(metaBucket.Cursor(), addr, true, raw, currEpoch) return err }) @@ -62,19 +68,12 @@ func (db *DB) Get(addr oid.Address, raw bool) (*object.Object, error) { // If raw and the object is a parent of some stored objects, get returns: // - [object.SplitInfoError] wrapping [object.SplitInfo] collected from parts if object is split; // - [iec.ErrPartitionedObject] if object is EC. -func get(tx *bbolt.Tx, addr oid.Address, checkStatus, raw bool, currEpoch uint64) (*object.Object, error) { +func get(metaCursor *bbolt.Cursor, addr oid.Address, checkStatus, raw bool, currEpoch uint64) (*object.Object, error) { var ( - cnr = addr.Container() - metaBucket = tx.Bucket(metaBucketKey(cnr)) - objID = addr.Object() + cnr = addr.Container() + objID = addr.Object() ) - if metaBucket == nil { - return nil, logicerr.Wrap(apistatus.ObjectNotFound{}) - } - - var metaCursor = metaBucket.Cursor() - if checkStatus { switch objectStatus(metaCursor, objID, currEpoch) { case statusGCMarked: @@ -96,19 +95,10 @@ func get(tx *bbolt.Tx, addr oid.Address, checkStatus, raw bool, currEpoch uint64 // Reconstruct header from available data. var ( - attrs []object.Attribute - obj = object.New() - objPrefix = slices.Concat([]byte{metaPrefixIDAttr}, objID[:]) + attrs []object.Attribute + obj = object.New() ) - for ak, _ := metaCursor.Seek(objPrefix); bytes.HasPrefix(ak, objPrefix); ak, _ = metaCursor.Next() { - attrKey, attrVal, ok := bytes.Cut(ak[len(objPrefix):], objectcore.MetaAttributeDelimiter) - if !ok { - return nil, fmt.Errorf("invalid attribute in meta of %s/%s: missing delimiter", cnr, objID) - } - // Attribute must non-zero key and value. - if len(attrKey) == 0 || len(attrVal) == 0 { - return nil, fmt.Errorf("empty attribute or value in meta of %s/%s", cnr, objID) - } + for attrKey, attrVal := range iterIDAttrs(metaCursor, objID) { switch string(attrKey) { case object.FilterVersion: var v version.Version @@ -189,12 +179,19 @@ func get(tx *bbolt.Tx, addr oid.Address, checkStatus, raw bool, currEpoch uint64 return obj, nil } -func getParentMetaOwnersPrefix(parentID oid.ID) []byte { - var parentPrefix = make([]byte, 1+len(object.FilterParentID)+attributeDelimiterLen+len(parentID)+attributeDelimiterLen) - parentPrefix[0] = metaPrefixAttrIDPlain - off := 1 + copy(parentPrefix[1:], object.FilterParentID) - off += copy(parentPrefix[off:], objectcore.MetaAttributeDelimiter) - copy(parentPrefix[off:], parentID[:]) +func iterIDAttrs(cur *bbolt.Cursor, obj oid.ID) func(yield func(k, v []byte) bool) { + var pref = slices.Concat([]byte{metaPrefixIDAttr}, obj[:]) - return parentPrefix + return func(yield func(k, v []byte) bool) { + for dbKey, _ := cur.Seek(pref); bytes.HasPrefix(dbKey, pref); dbKey, _ = cur.Next() { + kv := dbKey[len(pref):] + k, v, found := bytes.Cut(kv, objectcore.MetaAttributeDelimiter) + if !found || len(k) == 0 || len(v) == 0 { + continue + } + if !yield(k, v) { + break + } + } + } } diff --git a/pkg/local_object_storage/metabase/graveyard.go b/pkg/local_object_storage/metabase/graveyard.go index 2c338aaa4e..a2e7e972ab 100644 --- a/pkg/local_object_storage/metabase/graveyard.go +++ b/pkg/local_object_storage/metabase/graveyard.go @@ -1,7 +1,6 @@ package meta import ( - "bytes" "errors" "fmt" @@ -45,27 +44,10 @@ func (db *DB) IterateOverGarbage(h func(oid.ID) error, cnr cid.ID, offset oid.ID } func (db *DB) iterateIDs(c *bbolt.Cursor, h func(oid.ID) error, offset oid.ID) error { - var ( - k []byte - pref = []byte{metaPrefixGarbage} - ) - - if offset.IsZero() { - k, _ = c.Seek(pref) - } else { - pref = append(pref, offset[:]...) - k, _ = c.Seek(pref) - if bytes.Equal(k, pref) { - k, _ = c.Next() - } - } + var pref = []byte{metaPrefixGarbage} - for ; len(k) > 0 && k[0] == metaPrefixGarbage; k, _ = c.Next() { - obj, err := oid.DecodeBytes(k[1:]) - if err != nil { - return fmt.Errorf("garbage key of length %d: %w", len(k), err) - } - err = h(obj) + for obj := range iterPrefixedIDs(c, pref, offset) { + err := h(obj) if err != nil { if errors.Is(err, ErrInterruptIterator) { return nil @@ -143,11 +125,9 @@ func (db *DB) GetGarbage(limit int) ([]oid.Address, []cid.ID, error) { } func listGarbageObjects(cur *bbolt.Cursor, prefix byte, cnr cid.ID, objs []oid.Address, limit int) ([]oid.Address, error) { - k, _ := cur.Seek([]byte{prefix}) - for ; len(k) > 0 && len(objs) < limit && k[0] == prefix; k, _ = cur.Next() { - obj, err := oid.DecodeBytes(k[1:]) - if err != nil { - return objs, fmt.Errorf("bad key of length %d with prefix %d for container %s: %w", len(k), prefix, cnr, err) + for obj := range iterPrefixedIDs(cur, []byte{prefix}, oid.ID{}) { + if len(objs) >= limit { + break } objs = append(objs, oid.NewAddress(cnr, obj)) } diff --git a/pkg/local_object_storage/metabase/inhume.go b/pkg/local_object_storage/metabase/inhume.go index 798bd9d068..8a3f605451 100644 --- a/pkg/local_object_storage/metabase/inhume.go +++ b/pkg/local_object_storage/metabase/inhume.go @@ -77,7 +77,7 @@ func (db *DB) MarkGarbage(addrs ...oid.Address) (uint64, []oid.Address, error) { } var metaCursor = metaBucket.Cursor() - obj, err := get(tx, addr, false, true, currEpoch) + obj, err := get(metaCursor, addr, false, true, currEpoch) if err == nil { if inGarbage(metaCursor, id) == statusAvailable { // object is available, decrement the @@ -100,7 +100,7 @@ func (db *DB) MarkGarbage(addrs ...oid.Address) (uint64, []oid.Address, error) { return err } - if isLockObject(metaCursor, id) { + if isObjectType(metaCursor, id, object.TypeLock) { deletedLockObjs = append(deletedLockObjs, addr) } } diff --git a/pkg/local_object_storage/metabase/iterators.go b/pkg/local_object_storage/metabase/iterators.go index 71df6f52d4..41199ed8dd 100644 --- a/pkg/local_object_storage/metabase/iterators.go +++ b/pkg/local_object_storage/metabase/iterators.go @@ -10,7 +10,6 @@ import ( islices "github.com/nspcc-dev/neofs-node/internal/slices" objectcore "github.com/nspcc-dev/neofs-node/pkg/core/object" "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/util/logicerr" - 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" "go.uber.org/zap" @@ -66,9 +65,9 @@ func keyToEpochOID(k []byte, expStart []byte) (uint64, oid.ID) { return binary.BigEndian.Uint64(f256[32-8:]), id } -// metaBucket is metadata bucket (0xff), typPrefix is the key for FilterType -// object ID-Attribute key, id is the ID we're looking for. -func fetchTypeForID(metaCursor *bbolt.Cursor, typPrefix []byte, id oid.ID) (object.Type, error) { +// fetchTypeForIDWBuf fetches object type using the given meta bucket cursor +// and reusable key buffer. +func fetchTypeForIDWBuf(metaCursor *bbolt.Cursor, typPrefix []byte, id oid.ID) (object.Type, error) { var typ object.Type copy(typPrefix[1:], id[:]) @@ -83,6 +82,14 @@ func fetchTypeForID(metaCursor *bbolt.Cursor, typPrefix []byte, id oid.ID) (obje return typ, errObjTypeNotFound } +// fetchTypeForID is similar to fetchTypeForIDWBuf, but allocates a buffer internally. +func fetchTypeForID(c *bbolt.Cursor, id oid.ID) (object.Type, error) { + var key = make([]byte, metaIDTypePrefixSize) + + fillIDTypePrefix(key) + return fetchTypeForIDWBuf(c, key, id) +} + // fillIDTypePrefix puts metaPrefixIDAttr and FilterType properly into typPrefix // for subsequent use in fetchTypeForID. func fillIDTypePrefix(typPrefix []byte) { @@ -137,7 +144,7 @@ func (db *DB) iterateExpired(tx *bbolt.Tx, curEpoch uint64, h ExpiredObjectHandl addr.SetContainer(cnrID) addr.SetObject(id) - typ, err := fetchTypeForID(b.Cursor(), typPrefix, id) + typ, err := fetchTypeForIDWBuf(b.Cursor(), typPrefix, id) if err != nil { db.log.Warn("inconsistent DB in expired iterator", zap.Stringer("object", addr), zap.Error(err)) @@ -169,25 +176,17 @@ func mkFilterPhysicalPrefix() []byte { return prefix } -func iteratePhyObjects(tx *bbolt.Tx, f func(cid.ID, oid.ID) error) error { - var oID oid.ID - +func iteratePhyObjects(tx *bbolt.Tx, f func(*bbolt.Cursor, oid.ID) error) error { return tx.ForEach(func(name []byte, b *bbolt.Bucket) error { cID, tablePrefix := parseContainerIDWithPrefix(name) if cID.IsZero() || tablePrefix != metadataPrefix { return nil } - var ( - c = b.Cursor() - prefix = mkFilterPhysicalPrefix() - ) - for k, _ := c.Seek(prefix); bytes.HasPrefix(k, prefix); k, _ = c.Next() { - if oID.Decode(k[len(prefix):]) == nil { - err := f(cID, oID) - if err != nil { - return err - } + for id := range iterAttrVal(b.Cursor(), object.FilterPhysical, []byte(binPropMarker)) { + err := f(b.Cursor(), id) + if err != nil { + return err } } return nil diff --git a/pkg/local_object_storage/metabase/list.go b/pkg/local_object_storage/metabase/list.go index 78fb94647a..a0fd882fff 100644 --- a/pkg/local_object_storage/metabase/list.go +++ b/pkg/local_object_storage/metabase/list.go @@ -18,9 +18,9 @@ var ErrEndOfListing = logicerr.New("end of object listing") // Cursor is a type for continuous object listing. type Cursor struct { - bucketName []byte - inBucketOffset []byte - attrsPrefix []byte + containerID cid.ID + lastObjectID oid.ID + attrsPrefix []byte } // ListWithCursor lists physical objects available in metabase starting from @@ -54,114 +54,77 @@ func (db *DB) ListWithCursor(count int, cursor *Cursor, attrs ...string) ([]obje } func (db *DB) listWithCursor(tx *bbolt.Tx, result []objectcore.AddressWithAttributes, count int, cursor *Cursor, attrs ...string) ([]objectcore.AddressWithAttributes, *Cursor, error) { - threshold := cursor == nil // threshold is a flag to ignore cursor - var bucketName []byte - - c := tx.Cursor() - name, _ := c.First() + var ( + c = tx.Cursor() + name []byte + ) - if !threshold { - name, _ = c.Seek(cursor.bucketName) + if cursor == nil { + cursor = new(Cursor) + name, _ = c.Seek([]byte{metadataPrefix}) + } else { + name, _ = c.Seek(metaBucketKey(cursor.containerID)) } - var offset []byte - -loop: for ; name != nil; name, _ = c.Next() { containerID, prefix := parseContainerIDWithPrefix(name) if containerID.IsZero() || prefix != metadataPrefix { continue } + if containerID != cursor.containerID { + cursor.lastObjectID = oid.ID{} // Reset for the next bucket. + } + cursor.containerID = containerID bkt := tx.Bucket(name) if bkt != nil { - result, offset, cursor = selectNFromBucket(bkt, containerID, - result, count, cursor, threshold, attrs...) + result, cursor = selectNFromBucket(bkt, result, count, cursor, attrs...) } - bucketName = name if len(result) >= count { - break loop + break } - - // set threshold flag after first `selectNFromBucket` invocation - // first invocation must look for cursor object - threshold = true - } - - if offset != nil { - // new slice is much faster but less memory efficient - // we need to copy, because offset exists during bbolt tx - cursor.inBucketOffset = bytes.Clone(offset) } if len(result) == 0 { return nil, nil, ErrEndOfListing } - // new slice is much faster but less memory efficient - // we need to copy, because bucketName exists during bbolt tx - cursor.bucketName = bytes.Clone(bucketName) - return result, cursor, nil } // selectNFromBucket similar to selectAllFromBucket but uses cursor to find // object to start selecting from. Ignores inhumed objects. func selectNFromBucket(bkt *bbolt.Bucket, // main bucket - cnt cid.ID, // container ID to []objectcore.AddressWithAttributes, // listing result limit int, // stop listing at `limit` items in result cursor *Cursor, // start from cursor object - threshold bool, // ignore cursor and start immediately attrs ...string, -) ([]objectcore.AddressWithAttributes, []byte, *Cursor) { - if cursor == nil { - cursor = new(Cursor) - } - +) ([]objectcore.AddressWithAttributes, *Cursor) { var ( c = bkt.Cursor() count = len(to) - offset []byte - phyPrefix = mkFilterPhysicalPrefix() typePrefix = make([]byte, metaIDTypePrefixSize) gotAttrs []string ) if containerMarkedGC(c) { - return to, nil, cursor + return to, cursor } fillIDTypePrefix(typePrefix) - if threshold { - offset = phyPrefix - } else { - offset = cursor.inBucketOffset - } - k, _ := c.Seek(offset) - - if !threshold { - k, _ = c.Next() // we are looking for objects _after_ the cursor - } - - for ; bytes.HasPrefix(k, phyPrefix); k, _ = c.Next() { + for obj := range iterPrefixedIDs(c, mkFilterPhysicalPrefix(), cursor.lastObjectID) { if count >= limit { break } - var obj oid.ID - if err := obj.Decode(k[len(phyPrefix):]); err != nil { - break - } - mCursor := bkt.Cursor() - offset = k + cursor.lastObjectID = obj if inGarbage(mCursor, obj) != statusAvailable { continue } - objType, err := fetchTypeForID(mCursor, typePrefix, obj) + objType, err := fetchTypeForIDWBuf(mCursor, typePrefix, obj) if err != nil { continue } @@ -182,13 +145,13 @@ func selectNFromBucket(bkt *bbolt.Bucket, // main bucket } var a oid.Address - a.SetContainer(cnt) + a.SetContainer(cursor.containerID) a.SetObject(obj) to = append(to, objectcore.AddressWithAttributes{Address: a, Type: objType, Attributes: gotAttrs}) count++ } - return to, offset, cursor + return to, cursor } func parseContainerIDWithPrefix(name []byte) (cid.ID, byte) { diff --git a/pkg/local_object_storage/metabase/lock.go b/pkg/local_object_storage/metabase/lock.go index f6b2a1297d..dedd8ffa3e 100644 --- a/pkg/local_object_storage/metabase/lock.go +++ b/pkg/local_object_storage/metabase/lock.go @@ -1,9 +1,6 @@ package meta import ( - "bytes" - "strconv" - "github.com/nspcc-dev/bbolt" "github.com/nspcc-dev/neofs-sdk-go/object" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" @@ -14,51 +11,16 @@ import ( // zero currEpoch skips expiration check. Returns associated object ID if it's // present. func associatedWithTypedObject(currEpoch uint64, metaCursor *bbolt.Cursor, idObj oid.ID, typ object.Type) (bool, oid.ID) { - if metaCursor == nil { - return false, oid.ID{} - } - - var ( - typString = typ.String() - idStr = idObj.EncodeToString() - accPrefix = make([]byte, 1+len(object.AttributeAssociatedObject)+1+len(idStr)+1) - typeKey = make([]byte, metaIDTypePrefixSize+len(typString)) - expirationPrefix = make([]byte, attrIDFixedLen+len(object.AttributeExpirationEpoch)) - ) - - expirationPrefix[0] = metaPrefixIDAttr - copy(expirationPrefix[1+oid.Size:], object.AttributeExpirationEpoch) - - accPrefix[0] = metaPrefixAttrIDPlain - copy(accPrefix[1:], object.AttributeAssociatedObject) - copy(accPrefix[1+len(object.AttributeAssociatedObject)+1:], idStr) - - fillIDTypePrefix(typeKey) - copy(typeKey[metaIDTypePrefixSize:], typString) - - for k, _ := metaCursor.Seek(accPrefix); bytes.HasPrefix(k, accPrefix); k, _ = metaCursor.Next() { - mainObj := k[len(accPrefix):] - copy(typeKey[1:], mainObj) - - if metaCursor.Bucket().Get(typeKey) != nil { - if currEpoch > 0 { - copy(expirationPrefix[1:], mainObj) + var idStr = idObj.EncodeToString() - expKey, _ := metaCursor.Seek(expirationPrefix) - if bytes.HasPrefix(expKey, expirationPrefix) { - // expPrefix already includes attribute delimiter (see attrIDFixedLen length) - var val = expKey[len(expirationPrefix):] + for associateID := range iterAttrVal(metaCursor, object.AttributeAssociatedObject, []byte(idStr)) { + var cur = metaCursor.Bucket().Cursor() - objExpiration, err := strconv.ParseUint(string(val), 10, 64) - associationExpired := (err == nil) && (currEpoch > objExpiration) - if associationExpired { - continue - } - } + if isObjectType(cur, associateID, typ) { + if currEpoch > 0 && isExpired(cur, associateID, currEpoch) { + continue } - var associateID oid.ID - copy(associateID[:], mainObj) return true, associateID } } diff --git a/pkg/local_object_storage/metabase/metadata.go b/pkg/local_object_storage/metabase/metadata.go index 6f172b43f0..5c45f3deda 100644 --- a/pkg/local_object_storage/metabase/metadata.go +++ b/pkg/local_object_storage/metabase/metadata.go @@ -456,18 +456,17 @@ func collectChildren(cnrMetaCrs *bbolt.Cursor, cnr cid.ID, parentID oid.ID) ([]o } if errors.As(parInfo, &siErr) { var ( - si = siErr.SplitInfo() - firstID = si.GetFirstPart() - splitID = si.SplitID() - res []oid.ID - children []oid.ID + si = siErr.SplitInfo() + firstID = si.GetFirstPart() + splitID = si.SplitID() + res []oid.ID ) switch { case !firstID.IsZero(): - children = collectRawWithAttribute(cnrMetaCrs, object.FilterFirstSplitObject, firstID[:]) - children = append(children, firstID) + res = append(res, firstID) + res = slices.AppendSeq(res, iterAttrVal(cnrMetaCrs, object.FilterFirstSplitObject, firstID[:])) case splitID != nil: - children = collectRawWithAttribute(cnrMetaCrs, object.FilterSplitID, splitID.ToV2()) + res = slices.AppendSeq(res, iterAttrVal(cnrMetaCrs, object.FilterSplitID, splitID.ToV2())) default: // Shouldn't happen, but try to extract at least something. var ( @@ -475,14 +474,13 @@ func collectChildren(cnrMetaCrs *bbolt.Cursor, cnr cid.ID, parentID oid.ID) ([]o last = si.GetLastPart() ) if !link.IsZero() { - children = append(children, link) + res = append(res, link) } if !last.IsZero() { - children = append(children, last) + res = append(res, last) } } - res = append(res, children...) - for _, id := range children { + for _, id := range res { grandchildren, err := collectChildren(cnrMetaCrs, cnr, id) if err != nil { return nil, err diff --git a/pkg/local_object_storage/metabase/put.go b/pkg/local_object_storage/metabase/put.go index eace339362..269976eb08 100644 --- a/pkg/local_object_storage/metabase/put.go +++ b/pkg/local_object_storage/metabase/put.go @@ -111,6 +111,10 @@ func (db *DB) put(tx *bbolt.Tx, obj *object.Object, nestingLevel int, currEpoch } func handleNonRegularObject(tx *bbolt.Tx, currEpoch uint64, obj object.Object) error { + target := obj.AssociatedObject() + if target.IsZero() { + return nil + } cID := obj.GetContainerID() oID := obj.GetID() metaBkt, err := tx.CreateBucketIfNotExists(metaBucketKey(cID)) @@ -119,85 +123,78 @@ func handleNonRegularObject(tx *bbolt.Tx, currEpoch uint64, obj object.Object) e } metaCursor := metaBkt.Cursor() typ := obj.Type() + targetTyp, targetTypErr := fetchTypeForID(metaCursor, target) switch typ { - case object.TypeLock, object.TypeTombstone: - if target := obj.AssociatedObject(); !target.IsZero() { - typPrefix := make([]byte, metaIDTypePrefixSize) - fillIDTypePrefix(typPrefix) - targetTyp, targetTypErr := fetchTypeForID(metaCursor, typPrefix, target) - - if typ == object.TypeLock { - if targetTypErr == nil && targetTyp != object.TypeRegular { - return logicerr.Wrap(apistatus.LockNonRegularObject{}) - } + case object.TypeLock: + if targetTypErr == nil && targetTyp != object.TypeRegular { + return logicerr.Wrap(apistatus.LockNonRegularObject{}) + } - st := objectStatus(metaCursor, target, currEpoch) - if st == statusTombstoned { - return logicerr.Wrap(apistatus.ErrObjectAlreadyRemoved) - } + st := objectStatus(metaCursor, target, currEpoch) + if st == statusTombstoned { + return logicerr.Wrap(apistatus.ErrObjectAlreadyRemoved) + } - if targetTypErr != nil && !errors.Is(targetTypErr, errObjTypeNotFound) { - return fmt.Errorf("can't get type for %s object's target %s: %w", typ, target, targetTypErr) - } - } else { // TS case - var ( - addr oid.Address - inhumed int - ) - addr.SetContainer(cID) - if targetTypErr == nil { - if targetTyp == object.TypeTombstone { - return fmt.Errorf("%s TS's target is another TS: %s", oID, target) - } - if targetTyp == object.TypeLock { - return ErrLockObjectRemoval - } - } + if targetTypErr != nil && !errors.Is(targetTypErr, errObjTypeNotFound) { + return fmt.Errorf("can't get type for %s object's target %s: %w", typ, target, targetTypErr) + } + case object.TypeTombstone: + var ( + addr oid.Address + inhumed int + ) + addr.SetContainer(cID) + if targetTypErr == nil { + if targetTyp == object.TypeTombstone { + return fmt.Errorf("%s TS's target is another TS: %s", oID, target) + } + if targetTyp == object.TypeLock { + return ErrLockObjectRemoval + } + } - if objectLocked(currEpoch, metaCursor, target) { - return apistatus.ErrObjectLocked - } + if objectLocked(currEpoch, metaCursor, target) { + return apistatus.ErrObjectLocked + } - if targetTypErr != nil && !errors.Is(targetTypErr, errObjTypeNotFound) { - return fmt.Errorf("can't get type for %s object's target %s: %w", typ, target, targetTypErr) - } + if targetTypErr != nil && !errors.Is(targetTypErr, errObjTypeNotFound) { + return fmt.Errorf("can't get type for %s object's target %s: %w", typ, target, targetTypErr) + } - children, err := collectChildren(metaCursor, cID, target) - if err != nil { - return fmt.Errorf("collect children: %w", err) + children, err := collectChildren(metaCursor, cID, target) + if err != nil { + return fmt.Errorf("collect children: %w", err) + } + children = append(children, target) + for _, id := range children { + addr.SetObject(id) + + obj, err := get(metaCursor, addr, false, true, currEpoch) + // Garbage mark should be put irrespective of errors, + // especially if the error is SplitInfo. + if err == nil { + if inGarbage(metaCursor, id) == statusAvailable { + // object is available, decrement the + // logical counter + inhumed++ } - children = append(children, target) - for _, id := range children { - addr.SetObject(id) - - obj, err := get(tx, addr, false, true, currEpoch) - // Garbage mark should be put irrespective of errors, - // especially if the error is SplitInfo. - if err == nil { - if inGarbage(metaCursor, id) == statusAvailable { - // object is available, decrement the - // logical counter - inhumed++ - } - // if object is stored, and it is regular object then update bucket - // with container size estimations - if obj.Type() == object.TypeRegular { - err = changeContainerInfo(tx, cID, -int(obj.PayloadSize()), -1) - if err != nil { - return err - } - } - } - err = metaBkt.Put(mkGarbageKey(id), nil) + // if object is stored, and it is regular object then update bucket + // with container size estimations + if obj.Type() == object.TypeRegular { + err = changeContainerInfo(tx, cID, -int(obj.PayloadSize()), -1) if err != nil { - return fmt.Errorf("put %s object to garbage bucket: %w", target, err) + return err } } - err = updateCounter(tx, logical, uint64(inhumed), false) - if err != nil { - return fmt.Errorf("could not increase logical object counter: %w", err) - } } + err = metaBkt.Put(mkGarbageKey(id), nil) + if err != nil { + return fmt.Errorf("put %s object to garbage bucket: %w", target, err) + } + } + err = updateCounter(tx, logical, uint64(inhumed), false) + if err != nil { + return fmt.Errorf("could not increase logical object counter: %w", err) } default: } diff --git a/pkg/local_object_storage/metabase/revive.go b/pkg/local_object_storage/metabase/revive.go index a71fbad295..ccf829545f 100644 --- a/pkg/local_object_storage/metabase/revive.go +++ b/pkg/local_object_storage/metabase/revive.go @@ -123,7 +123,7 @@ func (db *DB) ReviveObject(addr oid.Address) (res ReviveStatus, err error) { return err } - if obj, err := get(tx, addr, false, true, currEpoch); err == nil { + if obj, err := get(metaCursor, addr, false, true, currEpoch); err == nil { // if object is stored, and it is regular object then update bucket // with container size estimations if obj.Type() == object.TypeRegular { diff --git a/pkg/local_object_storage/metabase/select.go b/pkg/local_object_storage/metabase/select.go index 920dc5a700..284462153b 100644 --- a/pkg/local_object_storage/metabase/select.go +++ b/pkg/local_object_storage/metabase/select.go @@ -100,8 +100,9 @@ func (db *DB) CollectRawWithAttribute(cnr cid.ID, attr string, val []byte) ([]oi if metaBkt == nil { return nil } - var cur = metaBkt.Cursor() - res = collectRawWithAttribute(cur, attr, val) + for v := range iterAttrVal(metaBkt.Cursor(), attr, val) { + res = append(res, v) + } return nil }) if err != nil { @@ -110,19 +111,36 @@ func (db *DB) CollectRawWithAttribute(cnr cid.ID, attr string, val []byte) ([]oi return res, err } -func collectRawWithAttribute(cur *bbolt.Cursor, attr string, val []byte) []oid.ID { - var ( - pref = slices.Concat([]byte{metaPrefixAttrIDPlain}, []byte(attr), - objectcore.MetaAttributeDelimiter, val, objectcore.MetaAttributeDelimiter) - res []oid.ID - ) +func iterPrefixedIDs(cur *bbolt.Cursor, pref []byte, offset oid.ID) func(yield func(oid.ID) bool) { + var k []byte - for k, _ := cur.Seek(pref); bytes.HasPrefix(k, pref); k, _ = cur.Next() { - child, err := oid.DecodeBytes(k[len(pref):]) - if err != nil { - continue + if offset.IsZero() { + k, _ = cur.Seek(pref) + } else { + var seekPos = slices.Concat(pref, offset[:]) + + k, _ = cur.Seek(seekPos) + if bytes.Equal(k, seekPos) { + k, _ = cur.Next() // We are looking for objects _after_ the offset. } - res = append(res, child) } - return res + + return func(yield func(oid.ID) bool) { + for ; bytes.HasPrefix(k, pref); k, _ = cur.Next() { + id, err := oid.DecodeBytes(k[len(pref):]) + if err != nil { + continue + } + if !yield(id) { + break + } + } + } +} + +func iterAttrVal(cur *bbolt.Cursor, attr string, val []byte) func(yield func(oid.ID) bool) { + var pref = slices.Concat([]byte{metaPrefixAttrIDPlain}, []byte(attr), + objectcore.MetaAttributeDelimiter, val, objectcore.MetaAttributeDelimiter) + + return iterPrefixedIDs(cur, pref, oid.ID{}) } diff --git a/pkg/local_object_storage/metabase/status.go b/pkg/local_object_storage/metabase/status.go index 564fbafb81..8f12e9038c 100644 --- a/pkg/local_object_storage/metabase/status.go +++ b/pkg/local_object_storage/metabase/status.go @@ -1,11 +1,9 @@ package meta import ( - "bytes" "slices" "github.com/nspcc-dev/bbolt" - objectcore "github.com/nspcc-dev/neofs-node/pkg/core/object" "github.com/nspcc-dev/neofs-sdk-go/object" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" ) @@ -48,7 +46,9 @@ func (db *DB) ObjectStatus(address oid.Address) (ObjectStatus, error) { } var metaCursor = metaBucket.Cursor() - res.HeaderIndex = readAttributes(metaCursor, oID) + for k, v := range iterIDAttrs(metaCursor, oID) { + res.HeaderIndex = append(res.HeaderIndex, HeaderField{K: slices.Clone(k), V: slices.Clone(v)}) + } var objLocked = objectLocked(currEpoch, metaCursor, oID) @@ -59,10 +59,8 @@ func (db *DB) ObjectStatus(address oid.Address) (ObjectStatus, error) { removedStatus := inGarbage(metaCursor, oID) var existsRegular bool - var typPrefix = make([]byte, metaIDTypePrefixSize) - fillIDTypePrefix(typPrefix) - typ, err := fetchTypeForID(metaBucket.Cursor(), typPrefix, oID) + typ, err := fetchTypeForID(metaCursor, oID) existsRegular = (err == nil && typ == object.TypeRegular) if (removedStatus != statusAvailable && objLocked) || existsRegular { @@ -80,21 +78,3 @@ func (db *DB) ObjectStatus(address oid.Address) (ObjectStatus, error) { res.Error = err return res, err } - -func readAttributes(c *bbolt.Cursor, oID oid.ID) []HeaderField { - var newIndexes []HeaderField - - pref := slices.Concat([]byte{metaPrefixIDAttr}, oID[:]) - k, _ := c.Seek(pref) - for ; bytes.HasPrefix(k, pref); k, _ = c.Next() { - kCut := k[len(pref):] - k, v, found := bytes.Cut(kCut, objectcore.MetaAttributeDelimiter) - if !found { - continue - } - - newIndexes = append(newIndexes, HeaderField{K: slices.Clone(k), V: slices.Clone(v)}) - } - - return newIndexes -} diff --git a/pkg/local_object_storage/metabase/util.go b/pkg/local_object_storage/metabase/util.go index 498f7f139b..bf058d5faa 100644 --- a/pkg/local_object_storage/metabase/util.go +++ b/pkg/local_object_storage/metabase/util.go @@ -100,13 +100,13 @@ const ( addressKeySize = cidSize + objectKeySize ) -// return true if provided object is of LOCK type. -func isLockObject(cur *bbolt.Cursor, obj oid.ID) bool { - var typeKey = make([]byte, metaIDTypePrefixSize+len(object.TypeLock.String())) +// return true if provided object is of the given type. +func isObjectType(cur *bbolt.Cursor, obj oid.ID, typ object.Type) bool { + var typeKey = make([]byte, metaIDTypePrefixSize+len(typ.String())) fillIDTypePrefix(typeKey) copy(typeKey[1:], obj[:]) - copy(typeKey[metaIDTypePrefixSize:], object.TypeLock.String()) + copy(typeKey[metaIDTypePrefixSize:], typ.String()) k, _ := cur.Seek(typeKey)