[#1262] metabase: Do not allocate intermediate slices for indices

```
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 <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2022-03-21 16:59:05 +03:00 committed by Alex Vanin
parent 0e9b6be3fd
commit 43867a3093
2 changed files with 87 additions and 109 deletions

View file

@ -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
}

View file

@ -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 <unique> 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 <list> 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 <fake bucket tree> 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 {