156ba85326
If an object has not been marked for removal by the GC in the current epoch yet but has already expired, respond with `ErrObjectNotFound` api status. Also, optimize shard iteration: a node must stop any iteration if the object is found but gonna be removed soon. All the checks are performed by the Metabase. Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
109 lines
2.6 KiB
Go
109 lines
2.6 KiB
Go
package engine
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard"
|
|
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"
|
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
|
)
|
|
|
|
var errLockFailed = errors.New("lock operation failed")
|
|
|
|
// Lock marks objects as locked with another object. All objects from the
|
|
// specified container.
|
|
//
|
|
// Allows locking regular objects only (otherwise returns apistatus.LockNonRegularObject).
|
|
//
|
|
// Locked list should be unique. Panics if it is empty.
|
|
func (e *StorageEngine) Lock(idCnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
|
return e.execIfNotBlocked(func() error {
|
|
return e.lock(idCnr, locker, locked)
|
|
})
|
|
}
|
|
|
|
func (e *StorageEngine) lock(idCnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
|
for i := range locked {
|
|
switch e.lockSingle(idCnr, locker, locked[i], true) {
|
|
case 1:
|
|
return apistatus.LockNonRegularObject{}
|
|
case 0:
|
|
switch e.lockSingle(idCnr, locker, locked[i], false) {
|
|
case 1:
|
|
return apistatus.LockNonRegularObject{}
|
|
case 0:
|
|
return errLockFailed
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Returns:
|
|
// 0 - fail
|
|
// 1 - locking irregular object
|
|
// 2 - ok
|
|
func (e *StorageEngine) lockSingle(idCnr cid.ID, locker, locked oid.ID, checkExists bool) (status uint8) {
|
|
// code is pretty similar to inhumeAddr, maybe unify?
|
|
root := false
|
|
var errIrregular apistatus.LockNonRegularObject
|
|
|
|
var addrLocked oid.Address
|
|
addrLocked.SetContainer(idCnr)
|
|
addrLocked.SetObject(locked)
|
|
|
|
e.iterateOverSortedShards(addrLocked, func(_ int, sh hashedShard) (stop bool) {
|
|
defer func() {
|
|
// if object is root we continue since information about it
|
|
// can be presented in other shards
|
|
if checkExists && root {
|
|
stop = false
|
|
}
|
|
}()
|
|
|
|
if checkExists {
|
|
var existsPrm shard.ExistsPrm
|
|
existsPrm.SetAddress(addrLocked)
|
|
|
|
exRes, err := sh.Exists(existsPrm)
|
|
if err != nil {
|
|
var siErr *objectSDK.SplitInfoError
|
|
if !errors.As(err, &siErr) {
|
|
if shard.IsErrObjectExpired(err) {
|
|
// object is already expired =>
|
|
// do not lock it
|
|
return true
|
|
}
|
|
|
|
e.reportShardError(sh, "could not check locked object for presence in shard", err)
|
|
return
|
|
}
|
|
|
|
root = true
|
|
} else if !exRes.Exists() {
|
|
return
|
|
}
|
|
}
|
|
|
|
err := sh.Lock(idCnr, locker, []oid.ID{locked})
|
|
if err != nil {
|
|
e.reportShardError(sh, "could not lock object in shard", err)
|
|
|
|
if errors.As(err, &errIrregular) {
|
|
status = 1
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
status = 2
|
|
|
|
return true
|
|
})
|
|
|
|
return
|
|
}
|