[#1247] object: Return NOT_FOUND and ALREADY_REMOVED statuses

Replace `ErrNotFound`/`ErrAlreadyRemoved` error from
`pkg/core/object` package with `ObjectNotFound`/`ObjectAlreadyRemoved`
one from `apistatus` package. These errors are returned by storage
node's server as NeoFS API statuses.

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2022-03-17 11:03:58 +03:00 committed by Alex Vanin
parent f32c9670ad
commit 70ffdf3478
49 changed files with 348 additions and 178 deletions

View file

@ -2,13 +2,6 @@ package object
import "errors"
// ErrNotFound is a basic "not found" error returned by
// object read functions.
var ErrNotFound = errors.New("object not found")
// ErrRangeOutOfBounds is a basic error of violation of the boundaries of the
// payload of an object.
var ErrRangeOutOfBounds = errors.New("payload range is out of bounds")
// ErrAlreadyRemoved returned when object has tombstone in graveyard.
var ErrAlreadyRemoved = errors.New("object already removed")

View file

@ -7,12 +7,12 @@ import (
"time"
clientcore "github.com/nspcc-dev/neofs-node/pkg/core/client"
coreObject "github.com/nspcc-dev/neofs-node/pkg/core/object"
neofsapiclient "github.com/nspcc-dev/neofs-node/pkg/innerring/internal/client"
auditproc "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/audit"
"github.com/nspcc-dev/neofs-node/pkg/network/cache"
"github.com/nspcc-dev/neofs-node/pkg/services/audit"
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
@ -60,6 +60,8 @@ func (c *ClientCache) Get(info clientcore.NodeInfo) (clientcore.Client, error) {
// GetSG polls the container from audit task to get the object by id.
// Returns storage groups structure from received object.
//
// Returns apistatus.ObjectNotFound if storage group is missing.
func (c *ClientCache) GetSG(task *audit.Task, id *oidSDK.ID) (*storagegroup.StorageGroup, error) {
sgAddress := new(addressSDK.Address)
sgAddress.SetContainerID(task.ContainerID())
@ -115,7 +117,9 @@ func (c *ClientCache) getSG(ctx context.Context, addr *addressSDK.Address, nm *n
return sg, nil
}
return nil, coreObject.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
// GetHeader requests node from the container under audit to return object header by id.

View file

@ -167,6 +167,9 @@ func (s settlementDeps) ContainerNodes(e uint64, cid *cid.ID) ([]common.NodeInfo
return res, nil
}
// SGInfo returns audit.SGInfo by object address.
//
// Returns apistatus.ObjectNotFound if storage group is missing.
func (s settlementDeps) SGInfo(addr *addressSDK.Address) (audit.SGInfo, error) {
cn, nm, err := s.buildContainer(0, addr.ContainerID())
if err != nil {

View file

@ -8,7 +8,6 @@ import (
"os"
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/util/logger/test"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
@ -34,7 +33,7 @@ func testAddress() *addressSDK.Address {
return addr
}
func testPutGet(t *testing.T, blz *Blobovnicza, sz uint64, expPut, expGet error) *addressSDK.Address {
func testPutGet(t *testing.T, blz *Blobovnicza, sz uint64, assertErrPut, assertErrGet func(error) bool) *addressSDK.Address {
// create binary object
data := make([]byte, sz)
@ -45,26 +44,34 @@ func testPutGet(t *testing.T, blz *Blobovnicza, sz uint64, expPut, expGet error)
pPut.SetAddress(addr)
pPut.SetMarshaledObject(data)
_, err := blz.Put(pPut)
require.True(t, errors.Is(err, expPut))
if assertErrPut != nil {
require.True(t, assertErrPut(err))
} else {
require.NoError(t, err)
}
if expPut != nil {
if assertErrPut != nil {
return nil
}
testGet(t, blz, addr, data, expGet)
testGet(t, blz, addr, data, assertErrGet)
return addr
}
func testGet(t *testing.T, blz *Blobovnicza, addr *addressSDK.Address, expObj []byte, expErr error) {
func testGet(t *testing.T, blz *Blobovnicza, addr *addressSDK.Address, expObj []byte, assertErr func(error) bool) {
pGet := new(GetPrm)
pGet.SetAddress(addr)
// try to read object from Blobovnicza
res, err := blz.Get(pGet)
require.True(t, errors.Is(err, expErr))
if assertErr != nil {
require.True(t, assertErr(err))
} else {
require.NoError(t, err)
}
if expErr == nil {
if assertErr == nil {
require.Equal(t, expObj, res.Object())
}
}
@ -94,7 +101,7 @@ func TestBlobovnicza(t *testing.T) {
require.NoError(t, blz.Init())
// try to read non-existent address
testGet(t, blz, testAddress(), nil, object.ErrNotFound)
testGet(t, blz, testAddress(), nil, IsErrNotFound)
filled := uint64(15 * 1 << 10)
@ -109,7 +116,7 @@ func TestBlobovnicza(t *testing.T) {
require.NoError(t, err)
// should return 404
testGet(t, blz, addr, nil, object.ErrNotFound)
testGet(t, blz, addr, nil, IsErrNotFound)
// fill Blobovnicza fully
for ; filled < sizeLim; filled += objSizeLim {
@ -117,7 +124,9 @@ func TestBlobovnicza(t *testing.T) {
}
// from now objects should not be saved
testPutGet(t, blz, 1024, ErrFull, nil)
testPutGet(t, blz, 1024, func(err error) bool {
return errors.Is(err, ErrFull)
}, nil)
require.NoError(t, blz.Close())
}

View file

@ -1,7 +1,7 @@
package blobovnicza
import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
"go.etcd.io/bbolt"
"go.uber.org/zap"
@ -26,7 +26,7 @@ func (p *DeletePrm) SetAddress(addr *addressSDK.Address) {
// Returns any error encountered that
// did not allow to completely delete the object.
//
// Returns ErrNotFound if the object to be deleted is not in blobovnicza.
// Returns apistatus.ObjectNotFound if the object to be deleted is not in blobovnicza.
//
// Should not be called in read-only configuration.
func (b *Blobovnicza) Delete(prm *DeletePrm) (*DeleteRes, error) {
@ -65,7 +65,9 @@ func (b *Blobovnicza) Delete(prm *DeletePrm) (*DeleteRes, error) {
})
if err == nil && !removed {
err = object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
return nil, err

View file

@ -0,0 +1,13 @@
package blobovnicza
import (
"errors"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
)
// IsErrNotFound checks if error returned by Blobovnicza Get/Delete method
// corresponds to missing object.
func IsErrNotFound(err error) bool {
return errors.As(err, new(apistatus.ObjectNotFound))
}

View file

@ -1,7 +1,7 @@
package blobovnicza
import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
"go.etcd.io/bbolt"
"go.uber.org/zap"
@ -32,7 +32,7 @@ func (p *GetRes) Object() []byte {
// Returns any error encountered that
// did not allow to completely read the object.
//
// Returns ErrNotFound if requested object is not
// Returns apistatus.ObjectNotFound if requested object is not
// presented in Blobovnicza.
func (b *Blobovnicza) Get(prm *GetPrm) (*GetRes, error) {
var (
@ -60,7 +60,9 @@ func (b *Blobovnicza) Get(prm *GetPrm) (*GetRes, error) {
}
if data == nil {
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
return &GetRes{

View file

@ -13,6 +13,7 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobovnicza"
storagelog "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/internal/log"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
"go.uber.org/zap"
@ -224,7 +225,7 @@ func (b *blobovniczas) get(prm *GetSmallPrm) (res *GetSmallRes, err error) {
res, err = b.getObjectFromLevel(bPrm, p, !ok)
if err != nil {
if !errors.Is(err, object.ErrNotFound) {
if !blobovnicza.IsErrNotFound(err) {
b.log.Debug("could not get object from level",
zap.String("level", p),
zap.String("error", err.Error()),
@ -240,7 +241,9 @@ func (b *blobovniczas) get(prm *GetSmallPrm) (res *GetSmallRes, err error) {
if err == nil && res == nil {
// not found in any blobovnicza
err = object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
return
@ -273,7 +276,7 @@ func (b *blobovniczas) delete(prm *DeleteSmallPrm) (res *DeleteSmallRes, err err
res, err = b.deleteObjectFromLevel(bPrm, p, !ok, prm)
if err != nil {
if !errors.Is(err, object.ErrNotFound) {
if !blobovnicza.IsErrNotFound(err) {
b.log.Debug("could not remove object from level",
zap.String("level", p),
zap.String("error", err.Error()),
@ -293,7 +296,9 @@ func (b *blobovniczas) delete(prm *DeleteSmallPrm) (res *DeleteSmallRes, err err
if err == nil && res == nil {
// not found in any blobovnicza
err = object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
return
@ -323,7 +328,7 @@ func (b *blobovniczas) getRange(prm *GetRangeSmallPrm) (res *GetRangeSmallRes, e
res, err = b.getRangeFromLevel(prm, p, !ok)
if err != nil {
outOfBounds := errors.Is(err, object.ErrRangeOutOfBounds)
if !errors.Is(err, object.ErrNotFound) && !outOfBounds {
if !blobovnicza.IsErrNotFound(err) && !outOfBounds {
b.log.Debug("could not get object from level",
zap.String("level", p),
zap.String("error", err.Error()),
@ -342,7 +347,9 @@ func (b *blobovniczas) getRange(prm *GetRangeSmallPrm) (res *GetRangeSmallRes, e
if err == nil && res == nil {
// not found in any blobovnicza
err = object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
return
@ -365,7 +372,7 @@ func (b *blobovniczas) deleteObjectFromLevel(prm *blobovnicza.DeletePrm, blzPath
if ok {
if res, err := b.deleteObject(v.(*blobovnicza.Blobovnicza), prm, dp); err == nil {
return res, err
} else if !errors.Is(err, object.ErrNotFound) {
} else if !blobovnicza.IsErrNotFound(err) {
log.Debug("could not remove object from opened blobovnicza",
zap.String("error", err.Error()),
)
@ -383,7 +390,7 @@ func (b *blobovniczas) deleteObjectFromLevel(prm *blobovnicza.DeletePrm, blzPath
if ok && tryActive {
if res, err := b.deleteObject(active.blz, prm, dp); err == nil {
return res, err
} else if !errors.Is(err, object.ErrNotFound) {
} else if !blobovnicza.IsErrNotFound(err) {
log.Debug("could not remove object from active blobovnicza",
zap.String("error", err.Error()),
)
@ -397,7 +404,9 @@ func (b *blobovniczas) deleteObjectFromLevel(prm *blobovnicza.DeletePrm, blzPath
// and it's pointless to open them).
if u64FromHexString(filepath.Base(blzPath)) > active.ind {
log.Debug("index is too big")
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
// open blobovnicza (cached inside)
@ -426,7 +435,7 @@ func (b *blobovniczas) getObjectFromLevel(prm *blobovnicza.GetPrm, blzPath strin
if ok {
if res, err := b.getObject(v.(*blobovnicza.Blobovnicza), prm); err == nil {
return res, err
} else if !errors.Is(err, object.ErrNotFound) {
} else if !blobovnicza.IsErrNotFound(err) {
log.Debug("could not read object from opened blobovnicza",
zap.String("error", err.Error()),
)
@ -445,7 +454,7 @@ func (b *blobovniczas) getObjectFromLevel(prm *blobovnicza.GetPrm, blzPath strin
if ok && tryActive {
if res, err := b.getObject(active.blz, prm); err == nil {
return res, err
} else if !errors.Is(err, object.ErrNotFound) {
} else if !blobovnicza.IsErrNotFound(err) {
log.Debug("could not get object from active blobovnicza",
zap.String("error", err.Error()),
)
@ -459,7 +468,9 @@ func (b *blobovniczas) getObjectFromLevel(prm *blobovnicza.GetPrm, blzPath strin
// and it's pointless to open them).
if u64FromHexString(filepath.Base(blzPath)) > active.ind {
log.Debug("index is too big")
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
// open blobovnicza (cached inside)
@ -492,7 +503,7 @@ func (b *blobovniczas) getRangeFromLevel(prm *GetRangeSmallPrm, blzPath string,
errors.Is(err, object.ErrRangeOutOfBounds):
return res, err
default:
if !errors.Is(err, object.ErrNotFound) {
if !blobovnicza.IsErrNotFound(err) {
log.Debug("could not read payload range from opened blobovnicza",
zap.String("error", err.Error()),
)
@ -516,7 +527,7 @@ func (b *blobovniczas) getRangeFromLevel(prm *GetRangeSmallPrm, blzPath string,
errors.Is(err, object.ErrRangeOutOfBounds):
return res, err
default:
if !errors.Is(err, object.ErrNotFound) {
if !blobovnicza.IsErrNotFound(err) {
log.Debug("could not read payload range from active blobovnicza",
zap.String("error", err.Error()),
)
@ -531,7 +542,10 @@ func (b *blobovniczas) getRangeFromLevel(prm *GetRangeSmallPrm, blzPath string,
// and it's pointless to open them).
if u64FromHexString(filepath.Base(blzPath)) > active.ind {
log.Debug("index is too big")
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
// open blobovnicza (cached inside)

View file

@ -2,13 +2,13 @@ package blobstor
import (
"crypto/sha256"
"errors"
"math/rand"
"os"
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/util/logger/test"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
@ -153,9 +153,9 @@ func TestBlobovniczas(t *testing.T) {
gPrm.SetAddress(addrList[i])
_, err = b.get(gPrm)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
_, err = b.delete(dPrm)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
}
}

View file

@ -3,9 +3,9 @@ package blobstor
import (
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/fstree"
storagelog "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/internal/log"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
)
// DeleteBigPrm groups the parameters of DeleteBig operation.
@ -21,11 +21,13 @@ type DeleteBigRes struct{}
// Returns any error encountered that did not allow
// to completely remove the object.
//
// Returns ErrNotFound if there is no object to delete.
// Returns apistatus.ObjectNotFound if there is no object to delete.
func (b *BlobStor) DeleteBig(prm *DeleteBigPrm) (*DeleteBigRes, error) {
err := b.fsTree.Delete(prm.addr)
if errors.Is(err, fstree.ErrFileNotFound) {
err = object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
err = errNotFound
}
if err == nil {

View file

@ -17,7 +17,7 @@ type DeleteSmallRes struct{}
// Returns any error encountered that did not allow
// to completely remove the object.
//
// Returns ErrObjectNotFound if there is no object to delete.
// Returns apistatus.ObjectNotFound if there is no object to delete.
func (b *BlobStor) DeleteSmall(prm *DeleteSmallPrm) (*DeleteSmallRes, error) {
return b.blobovniczas.delete(prm)
}

View file

@ -4,8 +4,8 @@ import (
"errors"
"fmt"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/fstree"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
)
@ -24,14 +24,16 @@ type GetBigRes struct {
// Returns any error encountered that
// did not allow to completely read the object.
//
// Returns ErrNotFound if requested object is not
// Returns apistatus.ObjectNotFound if requested object is not
// presented in shallow dir.
func (b *BlobStor) GetBig(prm *GetBigPrm) (*GetBigRes, error) {
// get compressed object data
data, err := b.fsTree.Get(prm.addr)
if err != nil {
if errors.Is(err, fstree.ErrFileNotFound) {
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
return nil, fmt.Errorf("could not read object from fs tree: %w", err)

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/fstree"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
)
@ -26,12 +27,15 @@ type GetRangeBigRes struct {
// did not allow to completely read the object payload range.
//
// Returns ErrRangeOutOfBounds if requested object range is out of bounds.
// Returns apistatus.ObjectNotFound if object is missing.
func (b *BlobStor) GetRangeBig(prm *GetRangeBigPrm) (*GetRangeBigRes, error) {
// get compressed object data
data, err := b.fsTree.Get(prm.addr)
if err != nil {
if errors.Is(err, fstree.ErrFileNotFound) {
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
return nil, fmt.Errorf("could not read object from fs tree: %w", err)

View file

@ -21,6 +21,7 @@ type GetRangeSmallRes struct {
// did not allow to completely read the object payload range.
//
// Returns ErrRangeOutOfBounds if requested object range is out of bounds.
// Returns apistatus.ObjectNotFound if requested object is missing in blobovnicza(s).
func (b *BlobStor) GetRangeSmall(prm *GetRangeSmallPrm) (*GetRangeSmallRes, error) {
return b.blobovniczas.getRange(prm)
}

View file

@ -18,6 +18,8 @@ type GetSmallRes struct {
//
// Returns any error encountered that
// did not allow to completely read the object.
//
// Returns apistatus.ObjectNotFound if requested object is missing in blobovnicza(s).
func (b *BlobStor) GetSmall(prm *GetSmallPrm) (*GetSmallRes, error) {
return b.blobovniczas.get(prm)
}

View file

@ -1,10 +1,8 @@
package engine
import (
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
)
@ -16,7 +14,7 @@ func (e *StorageEngine) exists(addr *addressSDK.Address) (bool, error) {
e.iterateOverSortedShards(addr, func(_ int, sh hashedShard) (stop bool) {
res, err := sh.Exists(shPrm)
if err != nil {
if errors.Is(err, object.ErrAlreadyRemoved) {
if shard.IsErrRemoved(err) {
alreadyRemoved = true
return true
@ -33,7 +31,9 @@ func (e *StorageEngine) exists(addr *addressSDK.Address) (bool, error) {
})
if alreadyRemoved {
return false, object.ErrAlreadyRemoved
var errRemoved apistatus.ObjectAlreadyRemoved
return false, errRemoved
}
return exists, nil

View file

@ -3,9 +3,9 @@ package engine
import (
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/util"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
"go.uber.org/zap"
@ -42,7 +42,8 @@ func (r *GetRes) Object() *objectSDK.Object {
// Returns any error encountered that
// did not allow to completely read the object part.
//
// Returns ErrNotFound if requested object is missing in local storage.
// Returns apistatus.ObjectNotFound if requested object is missing in local storage.
// Returns apistatus.ObjectAlreadyRemoved if object has been marked as removed.
//
// Returns an error if executions are blocked (see BlockExecution).
func (e *StorageEngine) Get(prm *GetPrm) (res *GetRes, err error) {
@ -63,8 +64,10 @@ func (e *StorageEngine) get(prm *GetPrm) (*GetRes, error) {
obj *objectSDK.Object
siErr *objectSDK.SplitInfoError
errNotFound apistatus.ObjectNotFound
outSI *objectSDK.SplitInfo
outError = object.ErrNotFound
outError error = errNotFound
shardWithMeta hashedShard
metaError error
@ -81,7 +84,7 @@ func (e *StorageEngine) get(prm *GetPrm) (*GetRes, error) {
metaError = err
}
switch {
case errors.Is(err, object.ErrNotFound):
case shard.IsErrNotFound(err):
return false // ignore, go to next shard
case errors.As(err, &siErr):
siErr = err.(*objectSDK.SplitInfoError)
@ -98,7 +101,7 @@ func (e *StorageEngine) get(prm *GetPrm) (*GetRes, error) {
}
return false
case errors.Is(err, object.ErrAlreadyRemoved):
case shard.IsErrRemoved(err):
outError = err
return true // stop, return it back
@ -118,7 +121,7 @@ func (e *StorageEngine) get(prm *GetPrm) (*GetRes, error) {
}
if obj == nil {
if shardWithMeta.Shard == nil || !errors.Is(outError, object.ErrNotFound) {
if shardWithMeta.Shard == nil || !shard.IsErrNotFound(outError) {
return nil, outError
}

View file

@ -3,9 +3,9 @@ package engine
import (
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/util"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
)
@ -55,8 +55,8 @@ func (r *HeadRes) Header() *objectSDK.Object {
// Returns any error encountered that
// did not allow to completely read the object header.
//
// Returns object.ErrNotFound if requested object is missing in local storage.
// Returns object.ErrAlreadyRemoved if requested object was inhumed.
// Returns apistatus.ObjectNotFound if requested object is missing in local storage.
// Returns apistatus.ObjectAlreadyRemoved if requested object was inhumed.
//
// Returns an error if executions are blocked (see BlockExecution).
func (e *StorageEngine) Head(prm *HeadPrm) (res *HeadRes, err error) {
@ -77,8 +77,10 @@ func (e *StorageEngine) head(prm *HeadPrm) (*HeadRes, error) {
head *objectSDK.Object
siErr *objectSDK.SplitInfoError
errNotFound apistatus.ObjectNotFound
outSI *objectSDK.SplitInfo
outError = object.ErrNotFound
outError error = errNotFound
)
shPrm := new(shard.HeadPrm).
@ -89,7 +91,7 @@ func (e *StorageEngine) head(prm *HeadPrm) (*HeadRes, error) {
res, err := sh.Head(shPrm)
if err != nil {
switch {
case errors.Is(err, object.ErrNotFound):
case shard.IsErrNotFound(err):
return false // ignore, go to next shard
case errors.As(err, &siErr):
siErr = err.(*objectSDK.SplitInfoError)
@ -106,7 +108,7 @@ func (e *StorageEngine) head(prm *HeadPrm) (*HeadRes, error) {
}
return false
case errors.Is(err, object.ErrAlreadyRemoved):
case shard.IsErrRemoved(err):
outError = err
return true // stop, return it back

View file

@ -4,7 +4,6 @@ import (
"context"
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
@ -116,7 +115,7 @@ func (e *StorageEngine) inhumeAddr(addr *addressSDK.Address, prm *shard.InhumePr
WithAddress(addr),
)
if err != nil {
if errors.Is(err, object.ErrAlreadyRemoved) {
if shard.IsErrRemoved(err) {
// inhumed once - no need to be inhumed again
status = 2
return true

View file

@ -36,6 +36,8 @@ func (p *PutPrm) WithObject(obj *objectSDK.Object) *PutPrm {
// did not allow to completely save the object.
//
// Returns an error if executions are blocked (see BlockExecution).
//
// Returns apistatus.ObjectAlreadyRemoved if object has been marked as removed.
func (e *StorageEngine) Put(prm *PutPrm) (res *PutRes, err error) {
err = e.execIfNotBlocked(func() error {
res, err = e.put(prm)

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/util"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
"go.uber.org/zap"
@ -58,8 +59,8 @@ func (r *RngRes) Object() *objectSDK.Object {
// Returns any error encountered that
// did not allow to completely read the object part.
//
// Returns ErrNotFound if requested object is missing in local storage.
// Returns ErrAlreadyRemoved if requested object is inhumed.
// Returns apistatus.ObjectNotFound if requested object is missing in local storage.
// Returns apistatus.ObjectAlreadyRemoved if requested object is inhumed.
// Returns ErrRangeOutOfBounds if requested object range is out of bounds.
//
// Returns an error if executions are blocked (see BlockExecution).
@ -81,8 +82,10 @@ func (e *StorageEngine) getRange(prm *RngPrm) (*RngRes, error) {
obj *objectSDK.Object
siErr *objectSDK.SplitInfoError
errNotFound apistatus.ObjectNotFound
outSI *objectSDK.SplitInfo
outError = object.ErrNotFound
outError error = errNotFound
shardWithMeta hashedShard
metaError error
@ -100,7 +103,7 @@ func (e *StorageEngine) getRange(prm *RngPrm) (*RngRes, error) {
metaError = err
}
switch {
case errors.Is(err, object.ErrNotFound):
case shard.IsErrNotFound(err):
return false // ignore, go to next shard
case errors.As(err, &siErr):
siErr = err.(*objectSDK.SplitInfoError)
@ -118,7 +121,7 @@ func (e *StorageEngine) getRange(prm *RngPrm) (*RngRes, error) {
return false
case
errors.Is(err, object.ErrAlreadyRemoved),
shard.IsErrRemoved(err),
errors.Is(err, object.ErrRangeOutOfBounds):
outError = err
@ -139,7 +142,7 @@ func (e *StorageEngine) getRange(prm *RngPrm) (*RngRes, error) {
}
if obj == nil {
if shardWithMeta.Shard == nil || !errors.Is(outError, object.ErrNotFound) {
if shardWithMeta.Shard == nil || !shard.IsErrNotFound(outError) {
return nil, outError
}

View file

@ -20,9 +20,13 @@ func TestReset(t *testing.T) {
addrToInhume := generateAddress()
assertExists := func(addr *addressSDK.Address, expExists bool, expErr error) {
assertExists := func(addr *addressSDK.Address, expExists bool, assertErr func(error) bool) {
exists, err := meta.Exists(db, addr)
require.ErrorIs(t, err, expErr)
if assertErr != nil {
require.True(t, assertErr(err))
} else {
require.NoError(t, err)
}
require.Equal(t, expExists, exists)
}
@ -36,7 +40,7 @@ func TestReset(t *testing.T) {
require.NoError(t, err)
assertExists(addr, true, nil)
assertExists(addrToInhume, false, object.ErrAlreadyRemoved)
assertExists(addrToInhume, false, meta.IsErrRemoved)
err = db.Reset()
require.NoError(t, err)

View file

@ -7,6 +7,7 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
storagelog "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/internal/log"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
"go.etcd.io/bbolt"
@ -97,7 +98,7 @@ func (db *DB) delete(tx *bbolt.Tx, addr *addressSDK.Address, refCounter referenc
// unmarshal object, work only with physically stored (raw == true) objects
obj, err := db.get(tx, addr, false, true)
if err != nil {
if errors.Is(err, object.ErrNotFound) {
if errors.As(err, new(apistatus.ObjectNotFound)) {
return nil
}

View file

@ -0,0 +1,13 @@
package meta
import (
"errors"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
)
// IsErrRemoved checks if error returned by Shard Exists/Get/Put method
// corresponds to removed object.
func IsErrRemoved(err error) bool {
return errors.As(err, new(apistatus.ObjectAlreadyRemoved))
}

View file

@ -5,7 +5,7 @@ import (
"errors"
"fmt"
"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"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
@ -39,6 +39,8 @@ func (p *ExistsRes) Exists() bool {
}
// Exists checks if object is presented in DB.
//
// See DB.Exists docs.
func Exists(db *DB, addr *addressSDK.Address) (bool, error) {
r, err := db.Exists(new(ExistsPrm).WithAddress(addr))
if err != nil {
@ -50,6 +52,8 @@ func Exists(db *DB, addr *addressSDK.Address) (bool, error) {
// Exists returns ErrAlreadyRemoved if addr was marked as removed. Otherwise it
// returns true if addr is in primary index or false if it is not.
//
// Returns apistatus.ObjectAlreadyRemoved if object has been placed in graveyard.
func (db *DB) Exists(prm *ExistsPrm) (res *ExistsRes, err error) {
res = new(ExistsRes)
@ -66,9 +70,13 @@ func (db *DB) exists(tx *bbolt.Tx, addr *addressSDK.Address) (exists bool, err e
// check graveyard first
switch inGraveyard(tx, addr) {
case 1:
return false, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return false, errNotFound
case 2:
return false, object.ErrAlreadyRemoved
var errRemoved apistatus.ObjectAlreadyRemoved
return false, errRemoved
}
objKey := objectKey(addr.ObjectID())

View file

@ -3,7 +3,7 @@ package meta
import (
"fmt"
"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"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
@ -69,6 +69,9 @@ func GetRaw(db *DB, addr *addressSDK.Address, raw bool) (*objectSDK.Object, erro
}
// Get returns object header for specified address.
//
// Returns apistatus.ObjectNotFound if object is missing in DB.
// Returns apistatus.ObjectAlreadyRemoved if object has been placed in graveyard.
func (db *DB) Get(prm *GetPrm) (res *GetRes, err error) {
res = new(GetRes)
@ -89,9 +92,13 @@ func (db *DB) get(tx *bbolt.Tx, addr *addressSDK.Address, checkGraveyard, raw bo
if checkGraveyard {
switch inGraveyard(tx, addr) {
case 1:
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
case 2:
return nil, object.ErrAlreadyRemoved
var errRemoved apistatus.ObjectAlreadyRemoved
return nil, errRemoved
}
}
@ -139,7 +146,9 @@ func getVirtualObject(tx *bbolt.Tx, cid *cid.ID, key []byte, raw bool) (*objectS
parentBucket := tx.Bucket(parentBucketName(cid))
if parentBucket == nil {
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
relativeLst, err := decodeList(parentBucket.Get(key))
@ -148,7 +157,9 @@ func getVirtualObject(tx *bbolt.Tx, cid *cid.ID, key []byte, raw bool) (*objectS
}
if len(relativeLst) == 0 { // this should never happen though
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
// pick last item, for now there is not difference which address to pick
@ -167,7 +178,9 @@ func getVirtualObject(tx *bbolt.Tx, cid *cid.ID, key []byte, raw bool) (*objectS
par := child.Parent()
if par == nil { // this should never happen though
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
return par, nil
@ -179,5 +192,7 @@ func getSplitInfoError(tx *bbolt.Tx, cid *cid.ID, key []byte) error {
return objectSDK.NewSplitInfoError(splitInfo)
}
return object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return errNotFound
}

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
meta "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/metabase"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
"github.com/stretchr/testify/require"
@ -112,12 +113,12 @@ func TestDB_Get(t *testing.T) {
require.NoError(t, meta.Inhume(db, obj, ts))
_, err := meta.Get(db, obj)
require.ErrorIs(t, err, object.ErrAlreadyRemoved)
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
obj = generateAddress()
require.NoError(t, meta.Inhume(db, obj, nil))
_, err = meta.Get(db, obj)
require.ErrorIs(t, err, object.ErrNotFound)
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
})
}

View file

@ -1,7 +1,6 @@
package meta_test
import (
"errors"
"testing"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
@ -28,10 +27,10 @@ func TestDB_Inhume(t *testing.T) {
require.NoError(t, err)
_, err = meta.Exists(db, object.AddressOf(raw))
require.EqualError(t, err, object.ErrAlreadyRemoved.Error())
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
_, err = meta.Get(db, object.AddressOf(raw))
require.EqualError(t, err, object.ErrAlreadyRemoved.Error())
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
}
func TestInhumeTombOnTomb(t *testing.T) {
@ -56,7 +55,7 @@ func TestInhumeTombOnTomb(t *testing.T) {
// addr1 should become inhumed {addr1:addr2}
_, err = db.Exists(existsPrm.WithAddress(addr1))
require.True(t, errors.Is(err, object.ErrAlreadyRemoved))
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
// try to inhume addr3 via addr1
_, err = db.Inhume(inhumePrm.
@ -73,7 +72,7 @@ func TestInhumeTombOnTomb(t *testing.T) {
// addr3 should be inhumed {addr3: addr1}
_, err = db.Exists(existsPrm.WithAddress(addr3))
require.True(t, errors.Is(err, object.ErrAlreadyRemoved))
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
// try to inhume addr1 (which is already a tombstone in graveyard)
_, err = db.Inhume(inhumePrm.

View file

@ -56,6 +56,8 @@ var (
)
// Put saves the object in DB.
//
// See DB.Put docs.
func Put(db *DB, obj *objectSDK.Object, id *blobovnicza.ID) error {
_, err := db.Put(new(PutPrm).
WithObject(obj).
@ -67,6 +69,8 @@ func Put(db *DB, obj *objectSDK.Object, id *blobovnicza.ID) error {
// Put saves object header in metabase. Object payload expected to be cut.
// Big objects have nil blobovniczaID.
//
// Returns apistatus.ObjectAlreadyRemoved if object has been placed in graveyard.
func (db *DB) Put(prm *PutPrm) (res *PutRes, err error) {
err = db.boltDB.Batch(func(tx *bbolt.Tx) error {
return db.put(tx, prm.obj, prm.id, nil)

View file

@ -1,7 +1,6 @@
package shard
import (
"errors"
"fmt"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
@ -114,7 +113,7 @@ func (s *Shard) refillMetabase() error {
}
err := meta.Put(s.metaBase, obj, blzID)
if err != nil && !errors.Is(err, object.ErrAlreadyRemoved) {
if err != nil && !meta.IsErrRemoved(err) {
return err
}

View file

@ -8,6 +8,7 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor"
meta "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/metabase"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
objecttest "github.com/nspcc-dev/neofs-sdk-go/object/test"
@ -99,7 +100,7 @@ func TestRefillMetabase(t *testing.T) {
res, err := sh.Head(headPrm.WithAddress(addr))
if expObj == nil {
require.ErrorIs(t, err, object.ErrNotFound)
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
return
}
@ -122,9 +123,9 @@ func TestRefillMetabase(t *testing.T) {
_, err := sh.Head(headPrm.WithAddress(member))
if exists {
require.ErrorIs(t, err, object.ErrAlreadyRemoved)
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
} else {
require.ErrorIs(t, err, object.ErrNotFound)
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
}
}
}

View file

@ -1,12 +1,10 @@
package shard
import (
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobovnicza"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor"
meta "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/metabase"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/writecache"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
"go.uber.org/zap"
)
@ -46,7 +44,7 @@ func (s *Shard) Delete(prm *DeletePrm) (*DeleteRes, error) {
for i := range prm.addr {
if s.hasWriteCache() {
err := s.writeCache.Delete(prm.addr[i])
if err != nil && !errors.Is(err, object.ErrNotFound) {
if err != nil && !writecache.IsErrNotFound(err) {
s.log.Error("can't delete object from write cache", zap.String("error", err.Error()))
}
}

View file

@ -5,6 +5,7 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
"github.com/stretchr/testify/require"
)
@ -50,7 +51,7 @@ func testShardDelete(t *testing.T, hasWriteCache bool) {
require.NoError(t, err)
_, err = sh.Get(getPrm)
require.EqualError(t, err, object.ErrNotFound.Error())
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
})
t.Run("small object", func(t *testing.T) {
@ -73,6 +74,6 @@ func testShardDelete(t *testing.T, hasWriteCache bool) {
require.NoError(t, err)
_, err = sh.Get(getPrm)
require.EqualError(t, err, object.ErrNotFound.Error())
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
})
}

View file

@ -0,0 +1,19 @@
package shard
import (
"errors"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
)
// IsErrNotFound checks if error returned by Shard Get/Head/GetRange method
// corresponds to missing object.
func IsErrNotFound(err error) bool {
return errors.As(err, new(apistatus.ObjectNotFound))
}
// IsErrRemoved checks if error returned by Shard Exists/Get/Head/GetRange method
// corresponds to removed object.
func IsErrRemoved(err error) bool {
return errors.As(err, new(apistatus.ObjectAlreadyRemoved))
}

View file

@ -33,6 +33,8 @@ func (p *ExistsRes) Exists() bool {
//
// Returns any error encountered that does not allow to
// unambiguously determine the presence of an object.
//
// Returns apistatus.ObjectAlreadyRemoved if object has been marked as removed.
func (s *Shard) Exists(prm *ExistsPrm) (*ExistsRes, error) {
exists, err := s.objectExists(prm.addr)

View file

@ -8,6 +8,8 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobovnicza"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor"
meta "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/metabase"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/writecache"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
"go.uber.org/zap"
@ -62,7 +64,8 @@ func (r *GetRes) HasMeta() bool {
// Returns any error encountered that
// did not allow to completely read the object part.
//
// Returns object.ErrNotFound if requested object is missing in shard.
// Returns apistatus.ObjectNotFound if requested object is missing in shard.
// Returns apistatus.ObjectAlreadyRemoved if requested object has been marked as removed in shard.
func (s *Shard) Get(prm *GetPrm) (*GetRes, error) {
var big, small storFetcher
@ -112,7 +115,7 @@ func (s *Shard) fetchObjectData(addr *addressSDK.Address, skipMeta bool, big, sm
return res, false, nil
}
if errors.Is(err, object.ErrNotFound) {
if writecache.IsErrNotFound(err) {
s.log.Debug("object is missing in write-cache")
} else {
s.log.Error("failed to fetch object from write-cache", zap.Error(err))
@ -134,7 +137,9 @@ func (s *Shard) fetchObjectData(addr *addressSDK.Address, skipMeta bool, big, sm
}
if !exists {
return nil, false, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, false, errNotFound
}
blobovniczaID, err := meta.IsSmall(s.metaBase, addr)

View file

@ -110,10 +110,10 @@ func testGet(t *testing.T, sh *shard.Shard, getPrm *shard.GetPrm, hasWriteCache
res, err := sh.Get(getPrm)
if hasWriteCache {
require.Eventually(t, func() bool {
if errors.Is(err, object.ErrNotFound) {
if shard.IsErrNotFound(err) {
res, err = sh.Get(getPrm)
}
return !errors.Is(err, object.ErrNotFound)
return !shard.IsErrNotFound(err)
}, time.Second, time.Millisecond*100)
}
return res, err

View file

@ -1,11 +1,10 @@
package shard
import (
"errors"
"fmt"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
meta "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/metabase"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/writecache"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
)
@ -51,6 +50,9 @@ func (r *HeadRes) Object() *objectSDK.Object {
// Head reads header of the object from the shard.
//
// Returns any error encountered.
//
// Returns apistatus.ObjectNotFound if object is missing in Shard.
// Returns apistatus.ObjectAlreadyRemoved if requested object has been marked as removed in shard.
func (s *Shard) Head(prm *HeadPrm) (*HeadRes, error) {
// object can be saved in write-cache (if enabled) or in metabase
@ -61,7 +63,7 @@ func (s *Shard) Head(prm *HeadPrm) (*HeadRes, error) {
return &HeadRes{
obj: header,
}, nil
} else if !errors.Is(err, object.ErrNotFound) {
} else if !writecache.IsErrNotFound(err) {
// in this case we think that object is presented in write-cache, but corrupted
return nil, fmt.Errorf("could not read header from write-cache: %w", err)
}

View file

@ -83,10 +83,10 @@ func testHead(t *testing.T, sh *shard.Shard, headPrm *shard.HeadPrm, hasWriteCac
res, err := sh.Head(headPrm)
if hasWriteCache {
require.Eventually(t, func() bool {
if errors.Is(err, object.ErrNotFound) {
if shard.IsErrNotFound(err) {
res, err = sh.Head(headPrm)
}
return !errors.Is(err, object.ErrNotFound)
return !shard.IsErrNotFound(err)
}, time.Second, time.Millisecond*100)
}
return res, err

View file

@ -5,6 +5,7 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
"github.com/stretchr/testify/require"
)
@ -49,5 +50,5 @@ func testShardInhume(t *testing.T, hasWriteCache bool) {
require.NoError(t, err)
_, err = sh.Get(getPrm)
require.EqualError(t, err, object.ErrAlreadyRemoved.Error())
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
}

View file

@ -69,6 +69,8 @@ func (r *RngRes) HasMeta() bool {
// did not allow to completely read the object part.
//
// Returns ErrRangeOutOfBounds if requested object range is out of bounds.
// Returns apistatus.ObjectNotFound if requested object is missing.
// Returns apistatus.ObjectAlreadyRemoved if requested object has been marked as removed in shard.
func (s *Shard) GetRange(prm *RngPrm) (*RngRes, error) {
var big, small storFetcher

View file

@ -3,14 +3,16 @@ package writecache
import (
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/fstree"
storagelog "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/internal/log"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
"go.etcd.io/bbolt"
)
// Delete removes object from write-cache.
//
// Returns apistatus.ObjectNotFound is object is missing in write-cache.
func (c *cache) Delete(addr *addressSDK.Address) error {
c.modeMtx.RLock()
defer c.modeMtx.RUnlock()
@ -58,7 +60,9 @@ func (c *cache) Delete(addr *addressSDK.Address) error {
err := c.fsTree.Delete(addr)
if errors.Is(err, fstree.ErrFileNotFound) {
err = object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
err = errNotFound
}
if err == nil {

View file

@ -0,0 +1,13 @@
package writecache
import (
"errors"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
)
// IsErrNotFound checks if error returned by Cache Get/Head/Delete method
// corresponds to missing object.
func IsErrNotFound(err error) bool {
return errors.As(err, new(apistatus.ObjectNotFound))
}

View file

@ -1,13 +1,15 @@
package writecache
import (
"github.com/nspcc-dev/neofs-node/pkg/core/object"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
"go.etcd.io/bbolt"
)
// Get returns object from write-cache.
//
// Returns apistatus.ObjectNotFound if requested object is missing in write-cache.
func (c *cache) Get(addr *addressSDK.Address) (*objectSDK.Object, error) {
saddr := addr.String()
@ -30,7 +32,9 @@ func (c *cache) Get(addr *addressSDK.Address) (*objectSDK.Object, error) {
data, err := c.fsTree.Get(addr)
if err != nil {
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
obj := objectSDK.New()
@ -43,6 +47,8 @@ func (c *cache) Get(addr *addressSDK.Address) (*objectSDK.Object, error) {
}
// Head returns object header from write-cache.
//
// Returns apistatus.ObjectNotFound if requested object is missing in write-cache.
func (c *cache) Head(addr *addressSDK.Address) (*objectSDK.Object, error) {
// TODO: #1149 easiest to implement solution is presented here, consider more efficient way, e.g.:
// - provide header as common object.Object to Put, but marked to prevent correlation with full object
@ -61,6 +67,8 @@ func (c *cache) Head(addr *addressSDK.Address) (*objectSDK.Object, error) {
// Get fetches object from the underlying database.
// Key should be a stringified address.
//
// Returns apistatus.ObjectNotFound if requested object is missing in db.
func Get(db *bbolt.DB, key []byte) ([]byte, error) {
var value []byte
err := db.View(func(tx *bbolt.Tx) error {
@ -70,7 +78,9 @@ func Get(db *bbolt.DB, key []byte) ([]byte, error) {
}
value = b.Get(key)
if value == nil {
return object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return errNotFound
}
value = cloneBytes(value)
return nil

View file

@ -7,10 +7,10 @@ import (
lru "github.com/hashicorp/golang-lru"
"github.com/hashicorp/golang-lru/simplelru"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/fstree"
storagelog "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/internal/log"
"github.com/nspcc-dev/neofs-node/pkg/util"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
addressSDK "github.com/nspcc-dev/neofs-sdk-go/object/address"
"go.etcd.io/bbolt"
"go.uber.org/zap"
@ -103,7 +103,9 @@ func (c *cache) deleteFromDB(keys [][]byte) error {
for i := range keys {
has := b.Get(keys[i])
if has == nil {
return object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return errNotFound
}
if err := b.Delete(keys[i]); err != nil {
return err

View file

@ -15,6 +15,7 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/services/object/util"
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement"
"github.com/nspcc-dev/neofs-node/pkg/util/logger/test"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
"github.com/nspcc-dev/neofs-sdk-go/container"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
@ -106,7 +107,9 @@ func newTestClient() *testClient {
func (c *testClient) getObject(exec *execCtx, _ client.NodeInfo) (*objectSDK.Object, error) {
v, ok := c.results[exec.address().String()]
if !ok {
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
if v.err != nil {
@ -131,7 +134,9 @@ func (s *testStorage) get(exec *execCtx) (*objectSDK.Object, error) {
)
if _, ok = s.inhumed[sAddr]; ok {
return nil, object.ErrAlreadyRemoved
var errRemoved apistatus.ObjectAlreadyRemoved
return nil, errRemoved
}
if info, ok := s.virtual[sAddr]; ok {
@ -142,7 +147,9 @@ func (s *testStorage) get(exec *execCtx) (*objectSDK.Object, error) {
return cutToRange(obj, exec.ctxRange()), nil
}
return nil, object.ErrNotFound
var errNotFound apistatus.ObjectNotFound
return nil, errNotFound
}
func cutToRange(o *objectSDK.Object, rng *objectSDK.Range) *objectSDK.Object {
@ -309,19 +316,19 @@ func TestGetLocalOnly(t *testing.T) {
err := svc.Get(ctx, p)
require.True(t, errors.Is(err, object.ErrAlreadyRemoved))
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
rngPrm := newRngPrm(false, nil, 0, 0)
rngPrm.WithAddress(addr)
err = svc.GetRange(ctx, rngPrm)
require.True(t, errors.Is(err, object.ErrAlreadyRemoved))
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
headPrm := newHeadPrm(false, nil)
headPrm.WithAddress(addr)
err = svc.Head(ctx, headPrm)
require.True(t, errors.Is(err, object.ErrAlreadyRemoved))
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
})
t.Run("404", func(t *testing.T) {
@ -336,20 +343,20 @@ func TestGetLocalOnly(t *testing.T) {
err := svc.Get(ctx, p)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
rngPrm := newRngPrm(false, nil, 0, 0)
rngPrm.WithAddress(addr)
err = svc.GetRange(ctx, rngPrm)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
headPrm := newHeadPrm(false, nil)
headPrm.WithAddress(addr)
err = svc.Head(ctx, headPrm)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
})
t.Run("VIRTUAL", func(t *testing.T) {
@ -599,7 +606,7 @@ func TestGetRemoteSmall(t *testing.T) {
c1.addResult(addr, nil, errors.New("any error"))
c2 := newTestClient()
c2.addResult(addr, nil, object.ErrAlreadyRemoved)
c2.addResult(addr, nil, new(apistatus.ObjectAlreadyRemoved))
svc := newSvc(builder, &testClientCache{
clients: map[string]*testClient{
@ -612,19 +619,19 @@ func TestGetRemoteSmall(t *testing.T) {
p.WithAddress(addr)
err := svc.Get(ctx, p)
require.True(t, errors.Is(err, object.ErrAlreadyRemoved))
require.ErrorAs(t, err, new(*apistatus.ObjectAlreadyRemoved))
rngPrm := newRngPrm(false, nil, 0, 0)
rngPrm.WithAddress(addr)
err = svc.GetRange(ctx, rngPrm)
require.True(t, errors.Is(err, object.ErrAlreadyRemoved))
require.ErrorAs(t, err, new(*apistatus.ObjectAlreadyRemoved))
headPrm := newHeadPrm(false, nil)
headPrm.WithAddress(addr)
err = svc.Head(ctx, headPrm)
require.True(t, errors.Is(err, object.ErrAlreadyRemoved))
require.ErrorAs(t, err, new(*apistatus.ObjectAlreadyRemoved))
})
t.Run("404", func(t *testing.T) {
@ -656,19 +663,19 @@ func TestGetRemoteSmall(t *testing.T) {
p.WithAddress(addr)
err := svc.Get(ctx, p)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
rngPrm := newRngPrm(false, nil, 0, 0)
rngPrm.WithAddress(addr)
err = svc.GetRange(ctx, rngPrm)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
headPrm := newHeadPrm(false, nil)
headPrm.WithAddress(addr)
err = svc.Head(ctx, headPrm)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
})
t.Run("VIRTUAL", func(t *testing.T) {
@ -700,11 +707,11 @@ func TestGetRemoteSmall(t *testing.T) {
c1 := newTestClient()
c1.addResult(addr, nil, errors.New("any error"))
c1.addResult(splitAddr, nil, object.ErrNotFound)
c1.addResult(splitAddr, nil, apistatus.ObjectNotFound{})
c2 := newTestClient()
c2.addResult(addr, nil, objectSDK.NewSplitInfoError(splitInfo))
c2.addResult(splitAddr, nil, object.ErrNotFound)
c2.addResult(splitAddr, nil, apistatus.ObjectNotFound{})
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
@ -726,13 +733,13 @@ func TestGetRemoteSmall(t *testing.T) {
p.WithAddress(addr)
err := svc.Get(ctx, p)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
rngPrm := newRngPrm(false, nil, 0, 0)
rngPrm.WithAddress(addr)
err = svc.GetRange(ctx, rngPrm)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
})
t.Run("get chain element failure", func(t *testing.T) {
@ -776,7 +783,7 @@ func TestGetRemoteSmall(t *testing.T) {
c2.addResult(addr, nil, objectSDK.NewSplitInfoError(splitInfo))
c2.addResult(linkAddr, linkingObj, nil)
c2.addResult(child1Addr, children[0], nil)
c2.addResult(child2Addr, nil, object.ErrNotFound)
c2.addResult(child2Addr, nil, apistatus.ObjectNotFound{})
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
@ -800,13 +807,13 @@ func TestGetRemoteSmall(t *testing.T) {
p.WithAddress(addr)
err := svc.Get(ctx, p)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
rngPrm := newRngPrm(false, NewSimpleObjectWriter(), 0, 1)
rngPrm.WithAddress(addr)
err = svc.GetRange(ctx, rngPrm)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
})
t.Run("OK", func(t *testing.T) {
@ -913,11 +920,11 @@ func TestGetRemoteSmall(t *testing.T) {
c1 := newTestClient()
c1.addResult(addr, nil, errors.New("any error"))
c1.addResult(splitAddr, nil, object.ErrNotFound)
c1.addResult(splitAddr, nil, apistatus.ObjectNotFound{})
c2 := newTestClient()
c2.addResult(addr, nil, objectSDK.NewSplitInfoError(splitInfo))
c2.addResult(splitAddr, nil, object.ErrNotFound)
c2.addResult(splitAddr, nil, apistatus.ObjectNotFound{})
builder := &testPlacementBuilder{
vectors: map[string][]netmap.Nodes{
@ -939,13 +946,13 @@ func TestGetRemoteSmall(t *testing.T) {
p.WithAddress(addr)
err := svc.Get(ctx, p)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
rngPrm := newRngPrm(false, nil, 0, 0)
rngPrm.WithAddress(addr)
err = svc.GetRange(ctx, rngPrm)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
})
t.Run("get chain element failure", func(t *testing.T) {
@ -1000,19 +1007,19 @@ func TestGetRemoteSmall(t *testing.T) {
testHeadVirtual(svc, addr, splitInfo)
headSvc := newTestClient()
headSvc.addResult(preRightAddr, nil, object.ErrNotFound)
headSvc.addResult(preRightAddr, nil, apistatus.ObjectNotFound{})
p := newPrm(false, NewSimpleObjectWriter())
p.WithAddress(addr)
err := svc.Get(ctx, p)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
rngPrm := newRngPrm(false, nil, 0, 1)
rngPrm.WithAddress(addr)
err = svc.GetRange(ctx, rngPrm)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
})
t.Run("OK", func(t *testing.T) {
@ -1180,7 +1187,7 @@ func TestGetFromPastEpoch(t *testing.T) {
p.WithAddress(addr)
err := svc.Get(ctx, p)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
commonPrm.SetNetmapLookupDepth(1)
@ -1203,7 +1210,7 @@ func TestGetFromPastEpoch(t *testing.T) {
rp.SetRange(r)
err = svc.GetRange(ctx, rp)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
w = NewSimpleObjectWriter()
rp.SetChunkWriter(w)
@ -1220,7 +1227,7 @@ func TestGetFromPastEpoch(t *testing.T) {
hp.WithAddress(addr)
err = svc.Head(ctx, hp)
require.True(t, errors.Is(err, object.ErrNotFound))
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
w = NewSimpleObjectWriter()
hp.SetHeaderWriter(w)

View file

@ -4,6 +4,7 @@ import (
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
"go.uber.org/zap"
)
@ -14,6 +15,7 @@ func (exec *execCtx) executeLocal() {
exec.collectedObject, err = exec.svc.localStorage.get(exec)
var errSplitInfo *objectSDK.SplitInfoError
var errRemoved apistatus.ObjectAlreadyRemoved
switch {
default:
@ -27,9 +29,9 @@ func (exec *execCtx) executeLocal() {
exec.status = statusOK
exec.err = nil
exec.writeCollectedObject()
case errors.Is(err, object.ErrAlreadyRemoved):
case errors.As(err, &errRemoved):
exec.status = statusINHUMED
exec.err = object.ErrAlreadyRemoved
exec.err = errRemoved
case errors.As(err, &errSplitInfo):
exec.status = statusVIRTUAL
mergeSplitInfo(exec.splitInfo(), errSplitInfo.SplitInfo())

View file

@ -5,7 +5,7 @@ import (
"errors"
"github.com/nspcc-dev/neofs-node/pkg/core/client"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
"go.uber.org/zap"
)
@ -21,11 +21,14 @@ func (exec *execCtx) processNode(ctx context.Context, info client.NodeInfo) bool
obj, err := client.getObject(exec, info)
var errSplitInfo *objectSDK.SplitInfoError
var errRemoved *apistatus.ObjectAlreadyRemoved
switch {
default:
var errNotFound apistatus.ObjectNotFound
exec.status = statusUndefined
exec.err = object.ErrNotFound
exec.err = errNotFound
exec.log.Debug("remote call failed",
zap.String("error", err.Error()),
@ -35,9 +38,9 @@ func (exec *execCtx) processNode(ctx context.Context, info client.NodeInfo) bool
exec.err = nil
exec.collectedObject = obj
exec.writeCollectedObject()
case errors.Is(err, object.ErrAlreadyRemoved):
case errors.As(err, &errRemoved):
exec.status = statusINHUMED
exec.err = object.ErrAlreadyRemoved
exec.err = errRemoved
case errors.As(err, &errSplitInfo):
exec.status = statusVIRTUAL
mergeSplitInfo(exec.splitInfo(), errSplitInfo.SplitInfo())

View file

@ -148,8 +148,8 @@ func (x GetObjectRes) Object() *object.Object {
//
// Returns any error prevented the operation from completing correctly in error return.
// Returns:
// error of type *object.SplitInfoError if object if raw flag is set and requested object is virtual;
// object.ErrAlreadyRemoved error if requested object is marked to be removed.
// error of type *object.SplitInfoError if object raw flag is set and requested object is virtual;
// error of type *apistatus.ObjectAlreadyRemoved if requested object is marked to be removed.
func GetObject(prm GetObjectPrm) (*GetObjectRes, error) {
if prm.tokenSession != nil {
prm.cliPrm.WithinSession(*prm.tokenSession)
@ -193,8 +193,6 @@ func GetObject(prm GetObjectPrm) (*GetObjectRes, error) {
return nil, fmt.Errorf("read payload: %w", err)
}
// FIXME: #1158 object.ErrAlreadyRemoved never returns
obj.SetPayload(buf)
return &GetObjectRes{
@ -245,8 +243,8 @@ func (x HeadObjectRes) Header() *object.Object {
//
// Returns any error prevented the operation from completing correctly in error return.
// Returns:
// error of type *object.SplitInfoError if object if raw flag is set and requested object is virtual;
// object.ErrAlreadyRemoved error if requested object is marked to be removed.
// error of type *object.SplitInfoError if object raw flag is set and requested object is virtual;
// error of type *apistatus.ObjectAlreadyRemoved if requested object is marked to be removed.
func HeadObject(prm HeadObjectPrm) (*HeadObjectRes, error) {
if prm.local {
prm.cliPrm.MarkLocal()
@ -272,8 +270,6 @@ func HeadObject(prm HeadObjectPrm) (*HeadObjectRes, error) {
return nil, fmt.Errorf("read object header from NeoFS: %w", err)
}
// FIXME: #1158 object.ErrAlreadyRemoved never returns
var hdr object.Object
if !cliRes.ReadHeader(&hdr) {
@ -338,8 +334,8 @@ func (x PayloadRangeRes) PayloadRange() []byte {
//
// Returns any error prevented the operation from completing correctly in error return.
// Returns:
// error of type *object.SplitInfoError if object if raw flag is set and requested object is virtual;
// object.ErrAlreadyRemoved error if requested object is marked to be removed.
// error of type *object.SplitInfoError if object raw flag is set and requested object is virtual;
// error of type *apistatus.ObjectAlreadyRemoved if requested object is marked to be removed.
func PayloadRange(prm PayloadRangePrm) (*PayloadRangeRes, error) {
if prm.local {
prm.cliPrm.MarkLocal()
@ -368,8 +364,6 @@ func PayloadRange(prm PayloadRangePrm) (*PayloadRangeRes, error) {
return nil, fmt.Errorf("read payload: %w", err)
}
// FIXME: #1158 object.ErrAlreadyRemoved never returns
return &PayloadRangeRes{
data: data,
}, nil