diff --git a/pkg/local_object_storage/metabase/list.go b/pkg/local_object_storage/metabase/list.go index a690f7a86..1140162fd 100644 --- a/pkg/local_object_storage/metabase/list.go +++ b/pkg/local_object_storage/metabase/list.go @@ -1,8 +1,8 @@ package meta import ( + "bytes" "errors" - "strings" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" @@ -102,11 +102,17 @@ func (db *DB) listWithCursor(tx *bbolt.Tx, result []oid.Address, count int, curs } var containerID cid.ID + var offset []byte + graveyardBkt := tx.Bucket(graveyardBucketName) + garbageBkt := tx.Bucket(garbageBucketName) + + const idSize = 44 // size of the stringified object and container ids + var rawAddr = make([]byte, idSize*2+1) loop: for ; name != nil; name, _ = c.Next() { - postfix, ok := parseContainerIDWithPostfix(&containerID, name) - if !ok { + b58CID, postfix := parseContainerIDWithPostfix(&containerID, name) + if b58CID == nil { continue } @@ -120,8 +126,13 @@ loop: continue } - prefix := containerID.EncodeToString() + "/" - result, cursor = selectNFromBucket(tx, name, prefix, containerID, result, count, cursor, threshold) + bkt := tx.Bucket(name) + if bkt != nil { + rawAddr = append(rawAddr[:0], b58CID...) + rawAddr = append(rawAddr, '/') + result, offset, cursor = selectNFromBucket(bkt, graveyardBkt, garbageBkt, rawAddr, containerID, + result, count, cursor, threshold) + } bucketName = name if len(result) >= count { break loop @@ -132,6 +143,13 @@ loop: 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 = make([]byte, len(offset)) + copy(cursor.inBucketOffset, offset) + } + if len(result) == 0 { return nil, nil, ErrEndOfListing } @@ -146,20 +164,15 @@ loop: // selectNFromBucket similar to selectAllFromBucket but uses cursor to find // object to start selecting from. Ignores inhumed objects. -func selectNFromBucket(tx *bbolt.Tx, - name []byte, // bucket name - prefix string, // container ID prefix, optimization +func selectNFromBucket(bkt *bbolt.Bucket, // main bucket + graveyardBkt, garbageBkt *bbolt.Bucket, // cached graveyard buckets + addrRaw []byte, // container ID prefix, optimization cnt cid.ID, // container ID to []oid.Address, // listing result limit int, // stop listing at `limit` items in result cursor *Cursor, // start from cursor object threshold bool, // ignore cursor and start immediately -) ([]oid.Address, *Cursor) { - bkt := tx.Bucket(name) - if bkt == nil { - return to, cursor - } - +) ([]oid.Address, []byte, *Cursor) { if cursor == nil { cursor = new(Cursor) } @@ -175,12 +188,6 @@ func selectNFromBucket(tx *bbolt.Tx, k, _ = c.Next() // we are looking for objects _after_ the cursor } - addrRaw := make([]byte, len(prefix)+44) - copy(addrRaw, prefix) - - graveyardBkt := tx.Bucket(graveyardBucketName) - garbageBkt := tx.Bucket(garbageBucketName) - for ; k != nil; k, _ = c.Next() { if count >= limit { break @@ -192,8 +199,7 @@ func selectNFromBucket(tx *bbolt.Tx, } offset = k - addrRaw = append(addrRaw[:len(prefix)], k...) - if inGraveyardWithKey(addrRaw, graveyardBkt, garbageBkt) > 0 { + if inGraveyardWithKey(append(addrRaw, k...), graveyardBkt, garbageBkt) > 0 { continue } @@ -204,29 +210,24 @@ func selectNFromBucket(tx *bbolt.Tx, count++ } - // new slice is much faster but less memory efficient - // we need to copy, because offset exists during bbolt tx - cursor.inBucketOffset = make([]byte, len(offset)) - copy(cursor.inBucketOffset, offset) - - return to, cursor + return to, offset, cursor } -func parseContainerIDWithPostfix(containerID *cid.ID, name []byte) (string, bool) { +func parseContainerIDWithPostfix(containerID *cid.ID, name []byte) ([]byte, string) { var ( - containerIDStr = string(name) - postfix string + containerIDStr = name + postfix []byte ) - ind := strings.Index(string(name), invalidBase58String) + ind := bytes.IndexByte(name, invalidBase58String[0]) if ind > 0 { postfix = containerIDStr[ind:] containerIDStr = containerIDStr[:ind] } - if err := containerID.DecodeString(containerIDStr); err != nil { - return "", false + if err := containerID.DecodeString(string(containerIDStr)); err != nil { + return nil, "" } - return postfix, true + return containerIDStr, string(postfix) }