diff --git a/pkg/local_object_storage/blobstor/blobovniczatree/put.go b/pkg/local_object_storage/blobstor/blobovniczatree/put.go index 438c2e2337..db7ca10825 100644 --- a/pkg/local_object_storage/blobstor/blobovniczatree/put.go +++ b/pkg/local_object_storage/blobstor/blobovniczatree/put.go @@ -13,8 +13,6 @@ import ( // Put saves object in the maximum weight blobobnicza. // // returns error if could not save object in any blobovnicza. -// -// nolint: funlen func (b *Blobovniczas) Put(prm common.PutPrm) (common.PutRes, error) { if b.readOnly { return common.PutRes{}, common.ErrReadOnly @@ -28,77 +26,85 @@ func (b *Blobovniczas) Put(prm common.PutPrm) (common.PutRes, error) { putPrm.SetAddress(prm.Address) putPrm.SetMarshaledObject(prm.RawData) - var ( - fn func(string) (bool, error) - id *blobovnicza.ID - allFull = true - ) - - fn = func(p string) (bool, error) { - active, err := b.getActivated(p) - if err != nil { - if !isLogical(err) { - b.reportError("could not get active blobovnicza", err) - } else { - b.log.Debug("could not get active blobovnicza", - zap.String("error", err.Error())) - } - - return false, nil - } - - if _, err := active.blz.Put(putPrm); err != nil { - // Check if blobovnicza is full. We could either receive `blobovnicza.ErrFull` error - // or update active blobovnicza in other thread. In the latter case the database will be closed - // and `updateActive` takes care of not updating the active blobovnicza twice. - if isFull := errors.Is(err, blobovnicza.ErrFull); isFull || errors.Is(err, bbolt.ErrDatabaseNotOpen) { - if isFull { - b.log.Debug("blobovnicza overflowed", - zap.String("path", filepath.Join(p, u64ToHexString(active.ind)))) - } - - if err := b.updateActive(p, &active.ind); err != nil { - if !isLogical(err) { - b.reportError("could not update active blobovnicza", err) - } else { - b.log.Debug("could not update active blobovnicza", - zap.String("level", p), - zap.String("error", err.Error())) - } - - return false, nil - } - - return fn(p) - } - - allFull = false - if !isLogical(err) { - b.reportError("could not put object to active blobovnicza", err) - } else { - b.log.Debug("could not put object to active blobovnicza", - zap.String("path", filepath.Join(p, u64ToHexString(active.ind))), - zap.String("error", err.Error())) - } - - return false, nil - } - - p = filepath.Join(p, u64ToHexString(active.ind)) - - id = blobovnicza.NewIDFromBytes([]byte(p)) - - return true, nil + it := &putIterator{ + B: b, + ID: nil, + AllFull: true, + PutPrm: putPrm, } - if err := b.iterateDeepest(prm.Address, fn); err != nil { + if err := b.iterateDeepest(prm.Address, it.iterate); err != nil { return common.PutRes{}, err - } else if id == nil { - if allFull { + } else if it.ID == nil { + if it.AllFull { return common.PutRes{}, common.ErrNoSpace } return common.PutRes{}, errPutFailed } - return common.PutRes{StorageID: id.Bytes()}, nil + return common.PutRes{StorageID: it.ID.Bytes()}, nil +} + +type putIterator struct { + B *Blobovniczas + ID *blobovnicza.ID + AllFull bool + PutPrm blobovnicza.PutPrm +} + +func (i *putIterator) iterate(path string) (bool, error) { + active, err := i.B.getActivated(path) + if err != nil { + if !isLogical(err) { + i.B.reportError("could not get active blobovnicza", err) + } else { + i.B.log.Debug("could not get active blobovnicza", + zap.String("error", err.Error())) + } + + return false, nil + } + + if _, err := active.blz.Put(i.PutPrm); err != nil { + // Check if blobovnicza is full. We could either receive `blobovnicza.ErrFull` error + // or update active blobovnicza in other thread. In the latter case the database will be closed + // and `updateActive` takes care of not updating the active blobovnicza twice. + if isFull := errors.Is(err, blobovnicza.ErrFull); isFull || errors.Is(err, bbolt.ErrDatabaseNotOpen) { + if isFull { + i.B.log.Debug("blobovnicza overflowed", + zap.String("path", filepath.Join(path, u64ToHexString(active.ind)))) + } + + if err := i.B.updateActive(path, &active.ind); err != nil { + if !isLogical(err) { + i.B.reportError("could not update active blobovnicza", err) + } else { + i.B.log.Debug("could not update active blobovnicza", + zap.String("level", path), + zap.String("error", err.Error())) + } + + return false, nil + } + + return i.iterate(path) + } + + i.AllFull = false + if !isLogical(err) { + i.B.reportError("could not put object to active blobovnicza", err) + } else { + i.B.log.Debug("could not put object to active blobovnicza", + zap.String("path", filepath.Join(path, u64ToHexString(active.ind))), + zap.String("error", err.Error())) + } + + return false, nil + } + + path = filepath.Join(path, u64ToHexString(active.ind)) + + i.ID = blobovnicza.NewIDFromBytes([]byte(path)) + + return true, nil }