From 43867a30937663e08b0fbc73ebd6936bfdade2ce Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 21 Mar 2022 16:59:05 +0300 Subject: [PATCH] [#1262] metabase: Do not allocate intermediate slices for indices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` name old alloc/op new alloc/op delta Put/parallel-8 123kB ± 4% 119kB ± 3% -2.72% (p=0.006 n=10+9) Put/sequential-8 170kB ± 1% 168kB ± 1% -1.42% (p=0.000 n=10+10) name old allocs/op new allocs/op delta Put/parallel-8 473 ± 1% 469 ± 0% -0.87% (p=0.000 n=10+10) Put/sequential-8 792 ± 0% 787 ± 0% -0.58% (p=0.000 n=10+10) ``` Signed-off-by: Evgenii Stratonikov --- pkg/local_object_storage/metabase/delete.go | 85 ++++++--------- pkg/local_object_storage/metabase/put.go | 111 ++++++++++---------- 2 files changed, 87 insertions(+), 109 deletions(-) diff --git a/pkg/local_object_storage/metabase/delete.go b/pkg/local_object_storage/metabase/delete.go index 4f8fd1aa5..29905a847 100644 --- a/pkg/local_object_storage/metabase/delete.go +++ b/pkg/local_object_storage/metabase/delete.go @@ -133,36 +133,19 @@ func (db *DB) deleteObject( obj *objectSDK.Object, isParent bool, ) error { - uniqueIndexes, err := delUniqueIndexes(obj, isParent) + err := delUniqueIndexes(tx, obj, isParent) if err != nil { - return fmt.Errorf("can' build unique indexes: %w", err) + return fmt.Errorf("can't remove unique indexes") } - // delete unique indexes - for i := range uniqueIndexes { - delUniqueIndexItem(tx, uniqueIndexes[i]) - } - - // build list indexes - listIndexes, err := listIndexes(obj) + err = updateListIndexes(tx, obj, delListIndexItem) if err != nil { - return fmt.Errorf("can' build list indexes: %w", err) + return fmt.Errorf("can't remove list indexes: %w", err) } - // delete list indexes - for i := range listIndexes { - delListIndexItem(tx, listIndexes[i]) - } - - // build fake bucket tree indexes - fkbtIndexes, err := fkbtIndexes(obj) + err = updateFKBTIndexes(tx, obj, delFKBTIndexItem) if err != nil { - return fmt.Errorf("can' build fake bucket tree indexes: %w", err) - } - - // delete fkbt indexes - for i := range fkbtIndexes { - delFKBTIndexItem(tx, fkbtIndexes[i]) + return fmt.Errorf("can't remove fake bucket tree indexes: %w", err) } return nil @@ -190,29 +173,30 @@ func delUniqueIndexItem(tx *bbolt.Tx, item namedBucketItem) { } } -func delFKBTIndexItem(tx *bbolt.Tx, item namedBucketItem) { +func delFKBTIndexItem(tx *bbolt.Tx, item namedBucketItem) error { bkt := tx.Bucket(item.name) if bkt == nil { - return + return nil } fkbtRoot := bkt.Bucket(item.key) if fkbtRoot == nil { - return + return nil } _ = fkbtRoot.Delete(item.val) // ignore error, best effort there + return nil } -func delListIndexItem(tx *bbolt.Tx, item namedBucketItem) { +func delListIndexItem(tx *bbolt.Tx, item namedBucketItem) error { bkt := tx.Bucket(item.name) if bkt == nil { - return + return nil } lst, err := decodeList(bkt.Get(item.key)) if err != nil || len(lst) == 0 { - return + return nil } // remove element from the list @@ -228,25 +212,24 @@ func delListIndexItem(tx *bbolt.Tx, item namedBucketItem) { if len(newLst) == 0 { _ = bkt.Delete(item.key) // ignore error, best effort there - return + return nil } // if list is not empty, then update it encodedLst, err := encodeList(lst) if err != nil { - return // ignore error, best effort there + return nil // ignore error, best effort there } _ = bkt.Put(item.key, encodedLst) // ignore error, best effort there + return nil } -func delUniqueIndexes(obj *objectSDK.Object, isParent bool) ([]namedBucketItem, error) { +func delUniqueIndexes(tx *bbolt.Tx, obj *objectSDK.Object, isParent bool) error { addr := object.AddressOf(obj) objKey := objectKey(addr.ObjectID()) addrKey := addressKey(addr) - result := make([]namedBucketItem, 0, 5) - // add value to primary unique bucket if !isParent { var bucketName []byte @@ -261,34 +244,32 @@ func delUniqueIndexes(obj *objectSDK.Object, isParent bool) ([]namedBucketItem, case objectSDK.TypeLock: bucketName = bucketNameLockers(*addr.ContainerID()) default: - return nil, ErrUnknownObjectType + return ErrUnknownObjectType } - result = append(result, namedBucketItem{ + delUniqueIndexItem(tx, namedBucketItem{ name: bucketName, key: objKey, }) } else { - result = append(result, namedBucketItem{ + delUniqueIndexItem(tx, namedBucketItem{ name: parentBucketName(obj.ContainerID()), key: objKey, }) } - result = append(result, - namedBucketItem{ // remove from small blobovnicza id index - name: smallBucketName(addr.ContainerID()), - key: objKey, - }, - namedBucketItem{ // remove from root index - name: rootBucketName(addr.ContainerID()), - key: objKey, - }, - namedBucketItem{ // remove from ToMoveIt index - name: toMoveItBucketName, - key: addrKey, - }, - ) + delUniqueIndexItem(tx, namedBucketItem{ // remove from small blobovnicza id index + name: smallBucketName(addr.ContainerID()), + key: objKey, + }) + delUniqueIndexItem(tx, namedBucketItem{ // remove from root index + name: rootBucketName(addr.ContainerID()), + key: objKey, + }) + delUniqueIndexItem(tx, namedBucketItem{ // remove from ToMoveIt index + name: toMoveItBucketName, + key: addrKey, + }) - return result, nil + return nil } diff --git a/pkg/local_object_storage/metabase/put.go b/pkg/local_object_storage/metabase/put.go index aa1b51bb4..8239010cf 100644 --- a/pkg/local_object_storage/metabase/put.go +++ b/pkg/local_object_storage/metabase/put.go @@ -129,46 +129,19 @@ func (db *DB) put(tx *bbolt.Tx, obj *objectSDK.Object, id *blobovnicza.ID, si *o } } - // build unique indexes - uniqueIndexes, err := uniqueIndexes(obj, si, id) + err = putUniqueIndexes(tx, obj, si, id) if err != nil { - return fmt.Errorf("can' build unique indexes: %w", err) + return fmt.Errorf("can't put unique indexes: %w", err) } - // put unique indexes - for i := range uniqueIndexes { - err = putUniqueIndexItem(tx, uniqueIndexes[i]) - if err != nil { - return err - } - } - - // build list indexes - listIndexes, err := listIndexes(obj) + err = updateListIndexes(tx, obj, putListIndexItem) if err != nil { - return fmt.Errorf("can' build list indexes: %w", err) + return fmt.Errorf("can't put list indexes: %w", err) } - // put list indexes - for i := range listIndexes { - err = putListIndexItem(tx, listIndexes[i]) - if err != nil { - return err - } - } - - // build fake bucket tree indexes - fkbtIndexes, err := fkbtIndexes(obj) + err = updateFKBTIndexes(tx, obj, putFKBTIndexItem) if err != nil { - return fmt.Errorf("can' build fake bucket tree indexes: %w", err) - } - - // put fake bucket tree indexes - for i := range fkbtIndexes { - err = putFKBTIndexItem(tx, fkbtIndexes[i]) - if err != nil { - return err - } + return fmt.Errorf("can't put fake bucket tree indexes: %w", err) } // update container volume size estimation @@ -187,12 +160,15 @@ func (db *DB) put(tx *bbolt.Tx, obj *objectSDK.Object, id *blobovnicza.ID, si *o return nil } -// builds list of indexes from the object. -func uniqueIndexes(obj *objectSDK.Object, si *objectSDK.SplitInfo, id *blobovnicza.ID) ([]namedBucketItem, error) { +func putUniqueIndexes( + tx *bbolt.Tx, + obj *objectSDK.Object, + si *objectSDK.SplitInfo, + id *blobovnicza.ID, +) error { isParent := si != nil addr := object.AddressOf(obj) objKey := objectKey(addr.ObjectID()) - result := make([]namedBucketItem, 0, 3) // add value to primary unique bucket if !isParent { @@ -208,27 +184,33 @@ func uniqueIndexes(obj *objectSDK.Object, si *objectSDK.SplitInfo, id *blobovnic case objectSDK.TypeLock: bucketName = bucketNameLockers(*addr.ContainerID()) default: - return nil, ErrUnknownObjectType + return ErrUnknownObjectType } rawObject, err := obj.CutPayload().Marshal() if err != nil { - return nil, fmt.Errorf("can't marshal object header: %w", err) + return fmt.Errorf("can't marshal object header: %w", err) } - result = append(result, namedBucketItem{ + err = putUniqueIndexItem(tx, namedBucketItem{ name: bucketName, key: objKey, val: rawObject, }) + if err != nil { + return err + } // index blobovniczaID if it is present if id != nil { - result = append(result, namedBucketItem{ + err = putUniqueIndexItem(tx, namedBucketItem{ name: smallBucketName(addr.ContainerID()), key: objKey, val: *id, }) + if err != nil { + return err + } } } @@ -242,79 +224,94 @@ func uniqueIndexes(obj *objectSDK.Object, si *objectSDK.SplitInfo, id *blobovnic if isParent { splitInfo, err = si.Marshal() if err != nil { - return nil, fmt.Errorf("can't marshal split info: %w", err) + return fmt.Errorf("can't marshal split info: %w", err) } } - result = append(result, namedBucketItem{ + err = putUniqueIndexItem(tx, namedBucketItem{ name: rootBucketName(addr.ContainerID()), key: objKey, val: splitInfo, }) + if err != nil { + return err + } } - return result, nil + return nil } -// builds list of indexes from the object. -func listIndexes(obj *objectSDK.Object) ([]namedBucketItem, error) { - result := make([]namedBucketItem, 0, 3) +type updateIndexItemFunc = func(tx *bbolt.Tx, item namedBucketItem) error + +func updateListIndexes(tx *bbolt.Tx, obj *objectSDK.Object, f updateIndexItemFunc) error { addr := object.AddressOf(obj) objKey := objectKey(addr.ObjectID()) // index payload hashes - result = append(result, namedBucketItem{ + err := f(tx, namedBucketItem{ name: payloadHashBucketName(addr.ContainerID()), key: obj.PayloadChecksum().Sum(), val: objKey, }) + if err != nil { + return err + } // index parent ids if obj.ParentID() != nil { - result = append(result, namedBucketItem{ + err := f(tx, namedBucketItem{ name: parentBucketName(addr.ContainerID()), key: objectKey(obj.ParentID()), val: objKey, }) + if err != nil { + return err + } } // index split ids if obj.SplitID() != nil { - result = append(result, namedBucketItem{ + err := f(tx, namedBucketItem{ name: splitBucketName(addr.ContainerID()), key: obj.SplitID().ToV2(), val: objKey, }) + if err != nil { + return err + } } - return result, nil + return nil } -// builds list of indexes from the object. -func fkbtIndexes(obj *objectSDK.Object) ([]namedBucketItem, error) { +func updateFKBTIndexes(tx *bbolt.Tx, obj *objectSDK.Object, f updateIndexItemFunc) error { addr := object.AddressOf(obj) objKey := []byte(addr.ObjectID().String()) attrs := obj.Attributes() - result := make([]namedBucketItem, 0, 1+len(attrs)) - // owner - result = append(result, namedBucketItem{ + err := f(tx, namedBucketItem{ name: ownerBucketName(addr.ContainerID()), key: []byte(obj.OwnerID().String()), val: objKey, }) + if err != nil { + return err + } // user specified attributes for i := range attrs { - result = append(result, namedBucketItem{ + err := f(tx, namedBucketItem{ name: attributeBucketName(addr.ContainerID(), attrs[i].Key()), key: []byte(attrs[i].Value()), val: objKey, }) + if err != nil { + return err + } } - return result, nil + return nil } func putUniqueIndexItem(tx *bbolt.Tx, item namedBucketItem) error {