Add tracing spans to PUT, DELETE requests and tree service #242
137 changed files with 1478 additions and 624 deletions
|
@ -36,7 +36,7 @@ func inspectFunc(cmd *cobra.Command, _ []string) {
|
||||||
storageID := meta.StorageIDPrm{}
|
storageID := meta.StorageIDPrm{}
|
||||||
storageID.SetAddress(addr)
|
storageID.SetAddress(addr)
|
||||||
|
|
||||||
resStorageID, err := db.StorageID(storageID)
|
resStorageID, err := db.StorageID(cmd.Context(), storageID)
|
||||||
common.ExitOnErr(cmd, common.Errf("could not check if the obj is small: %w", err))
|
common.ExitOnErr(cmd, common.Errf("could not check if the obj is small: %w", err))
|
||||||
|
|
||||||
if id := resStorageID.StorageID(); id != nil {
|
if id := resStorageID.StorageID(); id != nil {
|
||||||
|
@ -51,7 +51,7 @@ func inspectFunc(cmd *cobra.Command, _ []string) {
|
||||||
|
|
||||||
siErr := new(object.SplitInfoError)
|
siErr := new(object.SplitInfoError)
|
||||||
|
|
||||||
res, err := db.Get(prm)
|
res, err := db.Get(cmd.Context(), prm)
|
||||||
if errors.As(err, &siErr) {
|
if errors.As(err, &siErr) {
|
||||||
link, linkSet := siErr.SplitInfo().Link()
|
link, linkSet := siErr.SplitInfo().Link()
|
||||||
last, lastSet := siErr.SplitInfo().LastPart()
|
last, lastSet := siErr.SplitInfo().LastPart()
|
||||||
|
|
|
@ -42,7 +42,7 @@ func (n *notificationSource) Iterate(ctx context.Context, epoch uint64, handler
|
||||||
for _, c := range listRes.Containers() {
|
for _, c := range listRes.Containers() {
|
||||||
selectPrm.WithContainerID(c)
|
selectPrm.WithContainerID(c)
|
||||||
|
|
||||||
selectRes, err := n.e.Select(selectPrm)
|
selectRes, err := n.e.Select(ctx, selectPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(logs.FrostFSNodeNotificatorCouldNotSelectObjectsFromContainer,
|
log.Error(logs.FrostFSNodeNotificatorCouldNotSelectObjectsFromContainer,
|
||||||
zap.Stringer("cid", c),
|
zap.Stringer("cid", c),
|
||||||
|
|
|
@ -617,20 +617,20 @@ type engineWithNotifications struct {
|
||||||
defaultTopic string
|
defaultTopic string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e engineWithNotifications) IsLocked(address oid.Address) (bool, error) {
|
func (e engineWithNotifications) IsLocked(ctx context.Context, address oid.Address) (bool, error) {
|
||||||
return e.base.IsLocked(address)
|
return e.base.IsLocked(ctx, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e engineWithNotifications) Delete(ctx context.Context, tombstone oid.Address, toDelete []oid.ID) error {
|
func (e engineWithNotifications) Delete(ctx context.Context, tombstone oid.Address, toDelete []oid.ID) error {
|
||||||
return e.base.Delete(ctx, tombstone, toDelete)
|
return e.base.Delete(ctx, tombstone, toDelete)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e engineWithNotifications) Lock(locker oid.Address, toLock []oid.ID) error {
|
func (e engineWithNotifications) Lock(ctx context.Context, locker oid.Address, toLock []oid.ID) error {
|
||||||
return e.base.Lock(locker, toLock)
|
return e.base.Lock(ctx, locker, toLock)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e engineWithNotifications) Put(o *objectSDK.Object) error {
|
func (e engineWithNotifications) Put(ctx context.Context, o *objectSDK.Object) error {
|
||||||
if err := e.base.Put(o); err != nil {
|
if err := e.base.Put(ctx, o); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,8 +654,8 @@ type engineWithoutNotifications struct {
|
||||||
engine *engine.StorageEngine
|
engine *engine.StorageEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e engineWithoutNotifications) IsLocked(address oid.Address) (bool, error) {
|
func (e engineWithoutNotifications) IsLocked(ctx context.Context, address oid.Address) (bool, error) {
|
||||||
return e.engine.IsLocked(address)
|
return e.engine.IsLocked(ctx, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e engineWithoutNotifications) Delete(ctx context.Context, tombstone oid.Address, toDelete []oid.ID) error {
|
func (e engineWithoutNotifications) Delete(ctx context.Context, tombstone oid.Address, toDelete []oid.ID) error {
|
||||||
|
@ -673,10 +673,10 @@ func (e engineWithoutNotifications) Delete(ctx context.Context, tombstone oid.Ad
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e engineWithoutNotifications) Lock(locker oid.Address, toLock []oid.ID) error {
|
func (e engineWithoutNotifications) Lock(ctx context.Context, locker oid.Address, toLock []oid.ID) error {
|
||||||
return e.engine.Lock(locker.Container(), locker.Object(), toLock)
|
return e.engine.Lock(ctx, locker.Container(), locker.Object(), toLock)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e engineWithoutNotifications) Put(o *objectSDK.Object) error {
|
func (e engineWithoutNotifications) Put(ctx context.Context, o *objectSDK.Object) error {
|
||||||
return engine.Put(e.engine, o)
|
return engine.Put(ctx, e.engine, o)
|
||||||
}
|
}
|
||||||
|
|
|
@ -490,7 +490,7 @@ const (
|
||||||
NetmapCantInvokeNetmapUpdatePeer = "can't invoke netmap.UpdatePeer" // Error in ../node/pkg/innerring/processors/netmap/process_peers.go
|
NetmapCantInvokeNetmapUpdatePeer = "can't invoke netmap.UpdatePeer" // Error in ../node/pkg/innerring/processors/netmap/process_peers.go
|
||||||
NetmapNonAlphabetModeIgnoreRemoveNodeFromSubnetNotification = "non alphabet mode, ignore remove node from subnet notification" // Info in ../node/pkg/innerring/processors/netmap/process_peers.go
|
NetmapNonAlphabetModeIgnoreRemoveNodeFromSubnetNotification = "non alphabet mode, ignore remove node from subnet notification" // Info in ../node/pkg/innerring/processors/netmap/process_peers.go
|
||||||
NetmapCouldNotGetNetworkMapCandidates = "could not get network map candidates" // Warn in ../node/pkg/innerring/processors/netmap/process_peers.go
|
NetmapCouldNotGetNetworkMapCandidates = "could not get network map candidates" // Warn in ../node/pkg/innerring/processors/netmap/process_peers.go
|
||||||
NetmapCouldNotUnmarshalSubnetId = "could not unmarshal subnet id" // Warn in ../node/pkg/innerring/processors/netmap/process_peers.go
|
NetmapCouldNotUnmarshalSubnetID = "could not unmarshal subnet id" // Warn in ../node/pkg/innerring/processors/netmap/process_peers.go
|
||||||
NetmapGotZeroSubnetInRemoveNodeNotification = "got zero subnet in remove node notification" // Warn in ../node/pkg/innerring/processors/netmap/process_peers.go
|
NetmapGotZeroSubnetInRemoveNodeNotification = "got zero subnet in remove node notification" // Warn in ../node/pkg/innerring/processors/netmap/process_peers.go
|
||||||
NetmapCouldNotIterateOverSubnetworksOfTheNode = "could not iterate over subnetworks of the node" // Warn in ../node/pkg/innerring/processors/netmap/process_peers.go
|
NetmapCouldNotIterateOverSubnetworksOfTheNode = "could not iterate over subnetworks of the node" // Warn in ../node/pkg/innerring/processors/netmap/process_peers.go
|
||||||
NetmapCouldNotInvokeNetmapUpdateState = "could not invoke netmap.UpdateState" // Error in ../node/pkg/innerring/processors/netmap/process_peers.go
|
NetmapCouldNotInvokeNetmapUpdateState = "could not invoke netmap.UpdateState" // Error in ../node/pkg/innerring/processors/netmap/process_peers.go
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -42,7 +43,7 @@ type DeleteHandler interface {
|
||||||
// LockSource is a source of lock relations between the objects.
|
// LockSource is a source of lock relations between the objects.
|
||||||
type LockSource interface {
|
type LockSource interface {
|
||||||
// IsLocked must clarify object's lock status.
|
// IsLocked must clarify object's lock status.
|
||||||
IsLocked(address oid.Address) (bool, error)
|
IsLocked(ctx context.Context, address oid.Address) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locker is an object lock storage interface.
|
// Locker is an object lock storage interface.
|
||||||
|
@ -89,7 +90,7 @@ func NewFormatValidator(opts ...FormatValidatorOption) *FormatValidator {
|
||||||
// If unprepared is true, only fields set by user are validated.
|
// If unprepared is true, only fields set by user are validated.
|
||||||
//
|
//
|
||||||
// Returns nil error if the object has valid structure.
|
// Returns nil error if the object has valid structure.
|
||||||
func (v *FormatValidator) Validate(obj *object.Object, unprepared bool) error {
|
func (v *FormatValidator) Validate(ctx context.Context, obj *object.Object, unprepared bool) error {
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
return errNilObject
|
return errNilObject
|
||||||
}
|
}
|
||||||
|
@ -117,7 +118,7 @@ func (v *FormatValidator) Validate(obj *object.Object, unprepared bool) error {
|
||||||
return fmt.Errorf("(%T) could not validate signature key: %w", v, err)
|
return fmt.Errorf("(%T) could not validate signature key: %w", v, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := v.checkExpiration(obj); err != nil {
|
if err := v.checkExpiration(ctx, obj); err != nil {
|
||||||
return fmt.Errorf("object did not pass expiration check: %w", err)
|
return fmt.Errorf("object did not pass expiration check: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +129,7 @@ func (v *FormatValidator) Validate(obj *object.Object, unprepared bool) error {
|
||||||
|
|
||||||
if obj = obj.Parent(); obj != nil {
|
if obj = obj.Parent(); obj != nil {
|
||||||
// Parent object already exists.
|
// Parent object already exists.
|
||||||
return v.Validate(obj, false)
|
return v.Validate(ctx, obj, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -327,7 +328,7 @@ func (v *FormatValidator) fillAndValidateTombstoneMeta(o *object.Object, meta *C
|
||||||
|
|
||||||
var errExpired = errors.New("object has expired")
|
var errExpired = errors.New("object has expired")
|
||||||
|
|
||||||
func (v *FormatValidator) checkExpiration(obj *object.Object) error {
|
func (v *FormatValidator) checkExpiration(ctx context.Context, obj *object.Object) error {
|
||||||
exp, err := expirationEpochAttribute(obj)
|
exp, err := expirationEpochAttribute(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, errNoExpirationEpoch) {
|
if errors.Is(err, errNoExpirationEpoch) {
|
||||||
|
@ -348,7 +349,7 @@ func (v *FormatValidator) checkExpiration(obj *object.Object) error {
|
||||||
addr.SetContainer(cID)
|
addr.SetContainer(cID)
|
||||||
addr.SetObject(oID)
|
addr.SetObject(oID)
|
||||||
|
|
||||||
locked, err := v.e.IsLocked(addr)
|
locked, err := v.e.IsLocked(ctx, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("locking status check for an expired object: %w", err)
|
return fmt.Errorf("locking status check for an expired object: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -40,7 +41,7 @@ type testLockSource struct {
|
||||||
m map[oid.Address]bool
|
m map[oid.Address]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testLockSource) IsLocked(address oid.Address) (bool, error) {
|
func (t testLockSource) IsLocked(_ context.Context, address oid.Address) (bool, error) {
|
||||||
return t.m[address], nil
|
return t.m[address], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,20 +63,20 @@ func TestFormatValidator_Validate(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("nil input", func(t *testing.T) {
|
t.Run("nil input", func(t *testing.T) {
|
||||||
require.Error(t, v.Validate(nil, true))
|
require.Error(t, v.Validate(context.Background(), nil, true))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("nil identifier", func(t *testing.T) {
|
t.Run("nil identifier", func(t *testing.T) {
|
||||||
obj := object.New()
|
obj := object.New()
|
||||||
|
|
||||||
require.ErrorIs(t, v.Validate(obj, false), errNilID)
|
require.ErrorIs(t, v.Validate(context.Background(), obj, false), errNilID)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("nil container identifier", func(t *testing.T) {
|
t.Run("nil container identifier", func(t *testing.T) {
|
||||||
obj := object.New()
|
obj := object.New()
|
||||||
obj.SetID(oidtest.ID())
|
obj.SetID(oidtest.ID())
|
||||||
|
|
||||||
require.ErrorIs(t, v.Validate(obj, true), errNilCID)
|
require.ErrorIs(t, v.Validate(context.Background(), obj, true), errNilCID)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("unsigned object", func(t *testing.T) {
|
t.Run("unsigned object", func(t *testing.T) {
|
||||||
|
@ -83,7 +84,7 @@ func TestFormatValidator_Validate(t *testing.T) {
|
||||||
obj.SetContainerID(cidtest.ID())
|
obj.SetContainerID(cidtest.ID())
|
||||||
obj.SetID(oidtest.ID())
|
obj.SetID(oidtest.ID())
|
||||||
|
|
||||||
require.Error(t, v.Validate(obj, false))
|
require.Error(t, v.Validate(context.Background(), obj, false))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("correct w/ session token", func(t *testing.T) {
|
t.Run("correct w/ session token", func(t *testing.T) {
|
||||||
|
@ -101,7 +102,7 @@ func TestFormatValidator_Validate(t *testing.T) {
|
||||||
|
|
||||||
require.NoError(t, object.SetIDWithSignature(ownerKey.PrivateKey, obj))
|
require.NoError(t, object.SetIDWithSignature(ownerKey.PrivateKey, obj))
|
||||||
|
|
||||||
require.NoError(t, v.Validate(obj, false))
|
require.NoError(t, v.Validate(context.Background(), obj, false))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("correct w/o session token", func(t *testing.T) {
|
t.Run("correct w/o session token", func(t *testing.T) {
|
||||||
|
@ -109,7 +110,7 @@ func TestFormatValidator_Validate(t *testing.T) {
|
||||||
|
|
||||||
require.NoError(t, object.SetIDWithSignature(ownerKey.PrivateKey, obj))
|
require.NoError(t, object.SetIDWithSignature(ownerKey.PrivateKey, obj))
|
||||||
|
|
||||||
require.NoError(t, v.Validate(obj, false))
|
require.NoError(t, v.Validate(context.Background(), obj, false))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("tombstone content", func(t *testing.T) {
|
t.Run("tombstone content", func(t *testing.T) {
|
||||||
|
@ -236,7 +237,7 @@ func TestFormatValidator_Validate(t *testing.T) {
|
||||||
|
|
||||||
t.Run("invalid attribute value", func(t *testing.T) {
|
t.Run("invalid attribute value", func(t *testing.T) {
|
||||||
val := "text"
|
val := "text"
|
||||||
err := v.Validate(fn(val), false)
|
err := v.Validate(context.Background(), fn(val), false)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -245,7 +246,7 @@ func TestFormatValidator_Validate(t *testing.T) {
|
||||||
obj := fn(val)
|
obj := fn(val)
|
||||||
|
|
||||||
t.Run("non-locked", func(t *testing.T) {
|
t.Run("non-locked", func(t *testing.T) {
|
||||||
err := v.Validate(obj, false)
|
err := v.Validate(context.Background(), obj, false)
|
||||||
require.ErrorIs(t, err, errExpired)
|
require.ErrorIs(t, err, errExpired)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -258,14 +259,14 @@ func TestFormatValidator_Validate(t *testing.T) {
|
||||||
addr.SetObject(oID)
|
addr.SetObject(oID)
|
||||||
ls.m[addr] = true
|
ls.m[addr] = true
|
||||||
|
|
||||||
err := v.Validate(obj, false)
|
err := v.Validate(context.Background(), obj, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("alive object", func(t *testing.T) {
|
t.Run("alive object", func(t *testing.T) {
|
||||||
val := strconv.FormatUint(curEpoch, 10)
|
val := strconv.FormatUint(curEpoch, 10)
|
||||||
err := v.Validate(fn(val), true)
|
err := v.Validate(context.Background(), fn(val), true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -159,7 +159,7 @@ func (np *Processor) processRemoveSubnetNode(ev subnetEvent.RemoveNode) {
|
||||||
|
|
||||||
err = subnetToRemoveFrom.Unmarshal(rawSubnet)
|
err = subnetToRemoveFrom.Unmarshal(rawSubnet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
np.log.Warn(logs.NetmapCouldNotUnmarshalSubnetId,
|
np.log.Warn(logs.NetmapCouldNotUnmarshalSubnetID,
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
|
@ -88,7 +88,7 @@ func TestBlobovnicza(t *testing.T) {
|
||||||
var dPrm DeletePrm
|
var dPrm DeletePrm
|
||||||
dPrm.SetAddress(addr)
|
dPrm.SetAddress(addr)
|
||||||
|
|
||||||
_, err := blz.Delete(dPrm)
|
_, err := blz.Delete(context.Background(), dPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// should return 404
|
// should return 404
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
package blobovnicza
|
package blobovnicza
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,7 +35,13 @@ func (p *DeletePrm) SetAddress(addr oid.Address) {
|
||||||
// Returns an error of type apistatus.ObjectNotFound if the object to be deleted is not in blobovnicza.
|
// Returns an error of type apistatus.ObjectNotFound if the object to be deleted is not in blobovnicza.
|
||||||
//
|
//
|
||||||
// Should not be called in read-only configuration.
|
// Should not be called in read-only configuration.
|
||||||
func (b *Blobovnicza) Delete(prm DeletePrm) (DeleteRes, error) {
|
func (b *Blobovnicza) Delete(ctx context.Context, prm DeletePrm) (DeleteRes, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "Blobovnicza.Delete",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.addr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
addrKey := addressKey(prm.addr)
|
addrKey := addressKey(prm.addr)
|
||||||
|
|
||||||
removed := false
|
removed := false
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
package blobovniczatree
|
package blobovniczatree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobovnicza"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobovnicza"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,7 +20,14 @@ import (
|
||||||
//
|
//
|
||||||
// If blobocvnicza ID is specified, only this blobovnicza is processed.
|
// If blobocvnicza ID is specified, only this blobovnicza is processed.
|
||||||
// Otherwise, all Blobovniczas are processed descending weight.
|
// Otherwise, all Blobovniczas are processed descending weight.
|
||||||
func (b *Blobovniczas) Delete(prm common.DeletePrm) (res common.DeleteRes, err error) {
|
func (b *Blobovniczas) Delete(ctx context.Context, prm common.DeletePrm) (res common.DeleteRes, err error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "Blobovniczas.Delete",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.Address.EncodeToString()),
|
||||||
|
attribute.String("storage_id", hex.EncodeToString(prm.StorageID)),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
if b.readOnly {
|
if b.readOnly {
|
||||||
return common.DeleteRes{}, common.ErrReadOnly
|
return common.DeleteRes{}, common.ErrReadOnly
|
||||||
}
|
}
|
||||||
|
@ -30,7 +42,7 @@ func (b *Blobovniczas) Delete(prm common.DeletePrm) (res common.DeleteRes, err e
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.deleteObject(blz, bPrm, prm)
|
return b.deleteObject(ctx, blz, bPrm, prm)
|
||||||
}
|
}
|
||||||
|
|
||||||
activeCache := make(map[string]struct{})
|
activeCache := make(map[string]struct{})
|
||||||
|
@ -42,7 +54,7 @@ func (b *Blobovniczas) Delete(prm common.DeletePrm) (res common.DeleteRes, err e
|
||||||
// don't process active blobovnicza of the level twice
|
// don't process active blobovnicza of the level twice
|
||||||
_, ok := activeCache[dirPath]
|
_, ok := activeCache[dirPath]
|
||||||
|
|
||||||
res, err = b.deleteObjectFromLevel(bPrm, p, !ok, prm)
|
res, err = b.deleteObjectFromLevel(ctx, bPrm, p, !ok, prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !blobovnicza.IsErrNotFound(err) {
|
if !blobovnicza.IsErrNotFound(err) {
|
||||||
b.log.Debug(logs.BlobovniczatreeCouldNotRemoveObjectFromLevel,
|
b.log.Debug(logs.BlobovniczatreeCouldNotRemoveObjectFromLevel,
|
||||||
|
@ -73,7 +85,7 @@ func (b *Blobovniczas) Delete(prm common.DeletePrm) (res common.DeleteRes, err e
|
||||||
// tries to delete object from particular blobovnicza.
|
// tries to delete object from particular blobovnicza.
|
||||||
//
|
//
|
||||||
// returns no error if object was removed from some blobovnicza of the same level.
|
// returns no error if object was removed from some blobovnicza of the same level.
|
||||||
func (b *Blobovniczas) deleteObjectFromLevel(prm blobovnicza.DeletePrm, blzPath string, tryActive bool, dp common.DeletePrm) (common.DeleteRes, error) {
|
func (b *Blobovniczas) deleteObjectFromLevel(ctx context.Context, prm blobovnicza.DeletePrm, blzPath string, tryActive bool, dp common.DeletePrm) (common.DeleteRes, error) {
|
||||||
lvlPath := filepath.Dir(blzPath)
|
lvlPath := filepath.Dir(blzPath)
|
||||||
|
|
||||||
// try to remove from blobovnicza if it is opened
|
// try to remove from blobovnicza if it is opened
|
||||||
|
@ -81,7 +93,7 @@ func (b *Blobovniczas) deleteObjectFromLevel(prm blobovnicza.DeletePrm, blzPath
|
||||||
v, ok := b.opened.Get(blzPath)
|
v, ok := b.opened.Get(blzPath)
|
||||||
b.lruMtx.Unlock()
|
b.lruMtx.Unlock()
|
||||||
if ok {
|
if ok {
|
||||||
if res, err := b.deleteObject(v, prm, dp); err == nil {
|
if res, err := b.deleteObject(ctx, v, prm, dp); err == nil {
|
||||||
return res, err
|
return res, err
|
||||||
} else if !blobovnicza.IsErrNotFound(err) {
|
} else if !blobovnicza.IsErrNotFound(err) {
|
||||||
b.log.Debug(logs.BlobovniczatreeCouldNotRemoveObjectFromOpenedBlobovnicza,
|
b.log.Debug(logs.BlobovniczatreeCouldNotRemoveObjectFromOpenedBlobovnicza,
|
||||||
|
@ -100,7 +112,7 @@ func (b *Blobovniczas) deleteObjectFromLevel(prm blobovnicza.DeletePrm, blzPath
|
||||||
b.activeMtx.RUnlock()
|
b.activeMtx.RUnlock()
|
||||||
|
|
||||||
if ok && tryActive {
|
if ok && tryActive {
|
||||||
if res, err := b.deleteObject(active.blz, prm, dp); err == nil {
|
if res, err := b.deleteObject(ctx, active.blz, prm, dp); err == nil {
|
||||||
return res, err
|
return res, err
|
||||||
} else if !blobovnicza.IsErrNotFound(err) {
|
} else if !blobovnicza.IsErrNotFound(err) {
|
||||||
b.log.Debug(logs.BlobovniczatreeCouldNotRemoveObjectFromActiveBlobovnicza,
|
b.log.Debug(logs.BlobovniczatreeCouldNotRemoveObjectFromActiveBlobovnicza,
|
||||||
|
@ -125,11 +137,11 @@ func (b *Blobovniczas) deleteObjectFromLevel(prm blobovnicza.DeletePrm, blzPath
|
||||||
return common.DeleteRes{}, err
|
return common.DeleteRes{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.deleteObject(blz, prm, dp)
|
return b.deleteObject(ctx, blz, prm, dp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// removes object from blobovnicza and returns common.DeleteRes.
|
// removes object from blobovnicza and returns common.DeleteRes.
|
||||||
func (b *Blobovniczas) deleteObject(blz *blobovnicza.Blobovnicza, prm blobovnicza.DeletePrm, dp common.DeletePrm) (common.DeleteRes, error) {
|
func (b *Blobovniczas) deleteObject(ctx context.Context, blz *blobovnicza.Blobovnicza, prm blobovnicza.DeletePrm, dp common.DeletePrm) (common.DeleteRes, error) {
|
||||||
_, err := blz.Delete(prm)
|
_, err := blz.Delete(ctx, prm)
|
||||||
return common.DeleteRes{}, err
|
return common.DeleteRes{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ func TestExistsInvalidStorageID(t *testing.T) {
|
||||||
d, err := obj.Marshal()
|
d, err := obj.Marshal()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
putRes, err := b.Put(common.PutPrm{Address: addr, RawData: d, DontCompress: true})
|
putRes, err := b.Put(context.Background(), common.PutPrm{Address: addr, RawData: d, DontCompress: true})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("valid but wrong storage id", func(t *testing.T) {
|
t.Run("valid but wrong storage id", func(t *testing.T) {
|
||||||
|
|
|
@ -1,20 +1,31 @@
|
||||||
package blobovniczatree
|
package blobovniczatree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobovnicza"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobovnicza"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Put saves object in the maximum weight blobobnicza.
|
// Put saves object in the maximum weight blobobnicza.
|
||||||
//
|
//
|
||||||
// returns error if could not save object in any blobovnicza.
|
// returns error if could not save object in any blobovnicza.
|
||||||
func (b *Blobovniczas) Put(prm common.PutPrm) (common.PutRes, error) {
|
func (b *Blobovniczas) Put(ctx context.Context, prm common.PutPrm) (common.PutRes, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "Blobovniczas.Put",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.Address.EncodeToString()),
|
||||||
|
attribute.Bool("dont_compress", prm.DontCompress),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
if b.readOnly {
|
if b.readOnly {
|
||||||
return common.PutRes{}, common.ErrReadOnly
|
return common.PutRes{}, common.ErrReadOnly
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,12 +75,12 @@ func TestCompression(t *testing.T) {
|
||||||
testPut := func(t *testing.T, b *BlobStor, i int) {
|
testPut := func(t *testing.T, b *BlobStor, i int) {
|
||||||
var prm common.PutPrm
|
var prm common.PutPrm
|
||||||
prm.Object = smallObj[i]
|
prm.Object = smallObj[i]
|
||||||
_, err := b.Put(prm)
|
_, err := b.Put(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
prm = common.PutPrm{}
|
prm = common.PutPrm{}
|
||||||
prm.Object = bigObj[i]
|
prm.Object = bigObj[i]
|
||||||
_, err = b.Put(prm)
|
_, err = b.Put(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ type Storage interface {
|
||||||
Get(context.Context, GetPrm) (GetRes, error)
|
Get(context.Context, GetPrm) (GetRes, error)
|
||||||
GetRange(context.Context, GetRangePrm) (GetRangeRes, error)
|
GetRange(context.Context, GetRangePrm) (GetRangeRes, error)
|
||||||
Exists(context.Context, ExistsPrm) (ExistsRes, error)
|
Exists(context.Context, ExistsPrm) (ExistsRes, error)
|
||||||
Put(PutPrm) (PutRes, error)
|
Put(context.Context, PutPrm) (PutRes, error)
|
||||||
Delete(DeletePrm) (DeleteRes, error)
|
Delete(context.Context, DeletePrm) (DeleteRes, error)
|
||||||
Iterate(IteratePrm) (IterateRes, error)
|
Iterate(IteratePrm) (IterateRes, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,31 @@
|
||||||
package blobstor
|
package blobstor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *BlobStor) Delete(prm common.DeletePrm) (common.DeleteRes, error) {
|
func (b *BlobStor) Delete(ctx context.Context, prm common.DeletePrm) (common.DeleteRes, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "BlobStor.Delete",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.Address.EncodeToString()),
|
||||||
|
attribute.String("storage_id", hex.EncodeToString(prm.StorageID)),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
b.modeMtx.RLock()
|
b.modeMtx.RLock()
|
||||||
defer b.modeMtx.RUnlock()
|
defer b.modeMtx.RUnlock()
|
||||||
|
|
||||||
if prm.StorageID == nil {
|
if prm.StorageID == nil {
|
||||||
for i := range b.storage {
|
for i := range b.storage {
|
||||||
res, err := b.storage[i].Storage.Delete(prm)
|
res, err := b.storage[i].Storage.Delete(ctx, prm)
|
||||||
if err == nil || !errors.As(err, new(apistatus.ObjectNotFound)) {
|
if err == nil || !errors.As(err, new(apistatus.ObjectNotFound)) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logOp(b.log, deleteOp, prm.Address, b.storage[i].Storage.Type(), prm.StorageID)
|
logOp(b.log, deleteOp, prm.Address, b.storage[i].Storage.Type(), prm.StorageID)
|
||||||
|
@ -31,7 +43,7 @@ func (b *BlobStor) Delete(prm common.DeletePrm) (common.DeleteRes, error) {
|
||||||
st = b.storage[0].Storage
|
st = b.storage[0].Storage
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := st.Delete(prm)
|
res, err := st.Delete(ctx, prm)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logOp(b.log, deleteOp, prm.Address, st.Type(), prm.StorageID)
|
logOp(b.log, deleteOp, prm.Address, st.Type(), prm.StorageID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ func TestExists(t *testing.T) {
|
||||||
for i := range objects {
|
for i := range objects {
|
||||||
var prm common.PutPrm
|
var prm common.PutPrm
|
||||||
prm.Object = objects[i]
|
prm.Object = objects[i]
|
||||||
_, err := b.Put(prm)
|
_, err := b.Put(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -196,7 +196,13 @@ func (t *FSTree) treePath(addr oid.Address) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete removes the object with the specified address from the storage.
|
// Delete removes the object with the specified address from the storage.
|
||||||
func (t *FSTree) Delete(prm common.DeletePrm) (common.DeleteRes, error) {
|
func (t *FSTree) Delete(ctx context.Context, prm common.DeletePrm) (common.DeleteRes, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "FSTree.Delete",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.Address.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
if t.readOnly {
|
if t.readOnly {
|
||||||
return common.DeleteRes{}, common.ErrReadOnly
|
return common.DeleteRes{}, common.ErrReadOnly
|
||||||
}
|
}
|
||||||
|
@ -230,7 +236,14 @@ func (t *FSTree) Exists(ctx context.Context, prm common.ExistsPrm) (common.Exist
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put puts an object in the storage.
|
// Put puts an object in the storage.
|
||||||
func (t *FSTree) Put(prm common.PutPrm) (common.PutRes, error) {
|
func (t *FSTree) Put(ctx context.Context, prm common.PutPrm) (common.PutRes, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "FSTree.Put",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.Address.EncodeToString()),
|
||||||
|
attribute.Bool("dont_compress", prm.DontCompress),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
if t.readOnly {
|
if t.readOnly {
|
||||||
return common.PutRes{}, common.ErrReadOnly
|
return common.PutRes{}, common.ErrReadOnly
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package blobstortest
|
package blobstortest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ func prepare(t *testing.T, count int, s common.Storage, min, max uint64) []objec
|
||||||
prm.Object = objects[i].obj
|
prm.Object = objects[i].obj
|
||||||
prm.RawData = objects[i].raw
|
prm.RawData = objects[i].raw
|
||||||
|
|
||||||
putRes, err := s.Put(prm)
|
putRes, err := s.Put(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
objects[i].storageID = putRes.StorageID
|
objects[i].storageID = putRes.StorageID
|
||||||
|
|
|
@ -36,7 +36,7 @@ func TestControl(t *testing.T, cons Constructor, min, max uint64) {
|
||||||
prm.Object = NewObject(min + uint64(rand.Intn(int(max-min+1))))
|
prm.Object = NewObject(min + uint64(rand.Intn(int(max-min+1))))
|
||||||
prm.Address = objectCore.AddressOf(prm.Object)
|
prm.Address = objectCore.AddressOf(prm.Object)
|
||||||
|
|
||||||
_, err := s.Put(prm)
|
_, err := s.Put(context.Background(), prm)
|
||||||
require.ErrorIs(t, err, common.ErrReadOnly)
|
require.ErrorIs(t, err, common.ErrReadOnly)
|
||||||
})
|
})
|
||||||
t.Run("delete fails", func(t *testing.T) {
|
t.Run("delete fails", func(t *testing.T) {
|
||||||
|
@ -44,7 +44,7 @@ func TestControl(t *testing.T, cons Constructor, min, max uint64) {
|
||||||
prm.Address = objects[0].addr
|
prm.Address = objects[0].addr
|
||||||
prm.StorageID = objects[0].storageID
|
prm.StorageID = objects[0].storageID
|
||||||
|
|
||||||
_, err := s.Delete(prm)
|
_, err := s.Delete(context.Background(), prm)
|
||||||
require.ErrorIs(t, err, common.ErrReadOnly)
|
require.ErrorIs(t, err, common.ErrReadOnly)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ func TestDelete(t *testing.T, cons Constructor, min, max uint64) {
|
||||||
var prm common.DeletePrm
|
var prm common.DeletePrm
|
||||||
prm.Address = oidtest.Address()
|
prm.Address = oidtest.Address()
|
||||||
|
|
||||||
_, err := s.Delete(prm)
|
_, err := s.Delete(context.Background(), prm)
|
||||||
require.Error(t, err, new(apistatus.ObjectNotFound))
|
require.Error(t, err, new(apistatus.ObjectNotFound))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ func TestDelete(t *testing.T, cons Constructor, min, max uint64) {
|
||||||
prm.Address = objects[0].addr
|
prm.Address = objects[0].addr
|
||||||
prm.StorageID = objects[0].storageID
|
prm.StorageID = objects[0].storageID
|
||||||
|
|
||||||
_, err := s.Delete(prm)
|
_, err := s.Delete(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("exists fail", func(t *testing.T) {
|
t.Run("exists fail", func(t *testing.T) {
|
||||||
|
@ -55,7 +55,7 @@ func TestDelete(t *testing.T, cons Constructor, min, max uint64) {
|
||||||
var prm common.DeletePrm
|
var prm common.DeletePrm
|
||||||
prm.Address = objects[1].addr
|
prm.Address = objects[1].addr
|
||||||
|
|
||||||
_, err := s.Delete(prm)
|
_, err := s.Delete(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -64,10 +64,10 @@ func TestDelete(t *testing.T, cons Constructor, min, max uint64) {
|
||||||
prm.Address = objects[2].addr
|
prm.Address = objects[2].addr
|
||||||
prm.StorageID = objects[2].storageID
|
prm.StorageID = objects[2].storageID
|
||||||
|
|
||||||
_, err := s.Delete(prm)
|
_, err := s.Delete(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = s.Delete(prm)
|
_, err = s.Delete(context.Background(), prm)
|
||||||
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
|
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package blobstortest
|
package blobstortest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ func TestIterate(t *testing.T, cons Constructor, min, max uint64) {
|
||||||
var delPrm common.DeletePrm
|
var delPrm common.DeletePrm
|
||||||
delPrm.Address = objects[2].addr
|
delPrm.Address = objects[2].addr
|
||||||
delPrm.StorageID = objects[2].storageID
|
delPrm.StorageID = objects[2].storageID
|
||||||
_, err := s.Delete(delPrm)
|
_, err := s.Delete(context.Background(), delPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
objects = append(objects[:delID], objects[delID+1:]...)
|
objects = append(objects[:delID], objects[delID+1:]...)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package blobstor
|
package blobstor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -63,7 +64,7 @@ func TestIterateObjects(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range mObjs {
|
for _, v := range mObjs {
|
||||||
_, err := blobStor.Put(common.PutPrm{Address: v.addr, RawData: v.data})
|
_, err := blobStor.Put(context.Background(), common.PutPrm{Address: v.addr, RawData: v.data})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ func (s *memstoreImpl) Exists(_ context.Context, req common.ExistsPrm) (common.E
|
||||||
return common.ExistsRes{Exists: exists}, nil
|
return common.ExistsRes{Exists: exists}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *memstoreImpl) Put(req common.PutPrm) (common.PutRes, error) {
|
func (s *memstoreImpl) Put(_ context.Context, req common.PutPrm) (common.PutRes, error) {
|
||||||
if s.readOnly {
|
if s.readOnly {
|
||||||
return common.PutRes{}, common.ErrReadOnly
|
return common.PutRes{}, common.ErrReadOnly
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ func (s *memstoreImpl) Put(req common.PutPrm) (common.PutRes, error) {
|
||||||
return common.PutRes{StorageID: []byte(s.rootPath)}, nil
|
return common.PutRes{StorageID: []byte(s.rootPath)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *memstoreImpl) Delete(req common.DeletePrm) (common.DeleteRes, error) {
|
func (s *memstoreImpl) Delete(_ context.Context, req common.DeletePrm) (common.DeleteRes, error) {
|
||||||
if s.readOnly {
|
if s.readOnly {
|
||||||
return common.DeleteRes{}, common.ErrReadOnly
|
return common.DeleteRes{}, common.ErrReadOnly
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ func TestSimpleLifecycle(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
{
|
{
|
||||||
_, err := s.Put(common.PutPrm{Address: addr, RawData: d, DontCompress: true})
|
_, err := s.Put(context.Background(), common.PutPrm{Address: addr, RawData: d, DontCompress: true})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ func TestSimpleLifecycle(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
_, err := s.Delete(common.DeletePrm{Address: addr})
|
_, err := s.Delete(context.Background(), common.DeletePrm{Address: addr})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ func BenchmarkSubstorageReadPerf(b *testing.B) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("marshal: %v", err)
|
return fmt.Errorf("marshal: %v", err)
|
||||||
}
|
}
|
||||||
_, err = st.Put(common.PutPrm{
|
_, err = st.Put(context.Background(), common.PutPrm{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
RawData: raw,
|
RawData: raw,
|
||||||
})
|
})
|
||||||
|
@ -165,7 +165,7 @@ func BenchmarkSubstorageWritePerf(b *testing.B) {
|
||||||
addr := testutil.AddressFromObject(b, obj)
|
addr := testutil.AddressFromObject(b, obj)
|
||||||
raw, err := obj.Marshal()
|
raw, err := obj.Marshal()
|
||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
if _, err := st.Put(common.PutPrm{
|
if _, err := st.Put(context.Background(), common.PutPrm{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
RawData: raw,
|
RawData: raw,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
@ -202,7 +202,7 @@ func BenchmarkSubstorageIteratePerf(b *testing.B) {
|
||||||
addr := testutil.AddressFromObject(b, obj)
|
addr := testutil.AddressFromObject(b, obj)
|
||||||
raw, err := obj.Marshal()
|
raw, err := obj.Marshal()
|
||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
if _, err := st.Put(common.PutPrm{
|
if _, err := st.Put(context.Background(), common.PutPrm{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
RawData: raw,
|
RawData: raw,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package blobstor
|
package blobstor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNoPlaceFound is returned when object can't be saved to any sub-storage component
|
// ErrNoPlaceFound is returned when object can't be saved to any sub-storage component
|
||||||
|
@ -21,7 +25,14 @@ var ErrNoPlaceFound = logicerr.New("couldn't find a place to store an object")
|
||||||
//
|
//
|
||||||
// Returns any error encountered that
|
// Returns any error encountered that
|
||||||
// did not allow to completely save the object.
|
// did not allow to completely save the object.
|
||||||
func (b *BlobStor) Put(prm common.PutPrm) (common.PutRes, error) {
|
func (b *BlobStor) Put(ctx context.Context, prm common.PutPrm) (common.PutRes, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "BlobStor.Put",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.Address.EncodeToString()),
|
||||||
|
attribute.Bool("dont_compress", prm.DontCompress),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
b.modeMtx.RLock()
|
b.modeMtx.RLock()
|
||||||
defer b.modeMtx.RUnlock()
|
defer b.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
@ -39,7 +50,7 @@ func (b *BlobStor) Put(prm common.PutPrm) (common.PutRes, error) {
|
||||||
|
|
||||||
for i := range b.storage {
|
for i := range b.storage {
|
||||||
if b.storage[i].Policy == nil || b.storage[i].Policy(prm.Object, prm.RawData) {
|
if b.storage[i].Policy == nil || b.storage[i].Policy(prm.Object, prm.RawData) {
|
||||||
res, err := b.storage[i].Storage.Put(prm)
|
res, err := b.storage[i].Storage.Put(ctx, prm)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logOp(b.log, putOp, prm.Address, b.storage[i].Storage.Type(), res.StorageID)
|
logOp(b.log, putOp, prm.Address, b.storage[i].Storage.Type(), res.StorageID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,27 +176,27 @@ func (s *TestStore) Exists(ctx context.Context, req common.ExistsPrm) (common.Ex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestStore) Put(req common.PutPrm) (common.PutRes, error) {
|
func (s *TestStore) Put(ctx context.Context, req common.PutPrm) (common.PutRes, error) {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
switch {
|
switch {
|
||||||
case s.overrides.Put != nil:
|
case s.overrides.Put != nil:
|
||||||
return s.overrides.Put(req)
|
return s.overrides.Put(req)
|
||||||
case s.st != nil:
|
case s.st != nil:
|
||||||
return s.st.Put(req)
|
return s.st.Put(ctx, req)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unexpected storage call: Put(%+v)", req))
|
panic(fmt.Sprintf("unexpected storage call: Put(%+v)", req))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestStore) Delete(req common.DeletePrm) (common.DeleteRes, error) {
|
func (s *TestStore) Delete(ctx context.Context, req common.DeletePrm) (common.DeleteRes, error) {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
switch {
|
switch {
|
||||||
case s.overrides.Delete != nil:
|
case s.overrides.Delete != nil:
|
||||||
return s.overrides.Delete(req)
|
return s.overrides.Delete(req)
|
||||||
case s.st != nil:
|
case s.st != nil:
|
||||||
return s.st.Delete(req)
|
return s.st.Delete(ctx, req)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unexpected storage call: Delete(%+v)", req))
|
panic(fmt.Sprintf("unexpected storage call: Delete(%+v)", req))
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,7 +308,7 @@ loop:
|
||||||
e.removeShards(shardsToRemove...)
|
e.removeShards(shardsToRemove...)
|
||||||
|
|
||||||
for _, p := range shardsToReload {
|
for _, p := range shardsToReload {
|
||||||
err := p.sh.Reload(p.opts...)
|
err := p.sh.Reload(ctx, p.opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.log.Error(logs.EngineCouldNotReloadAShard,
|
e.log.Error(logs.EngineCouldNotReloadAShard,
|
||||||
zap.Stringer("shard id", p.sh.ID()),
|
zap.Stringer("shard id", p.sh.ID()),
|
||||||
|
|
|
@ -204,7 +204,7 @@ func TestExecBlocks(t *testing.T) {
|
||||||
|
|
||||||
addr := object.AddressOf(obj)
|
addr := object.AddressOf(obj)
|
||||||
|
|
||||||
require.NoError(t, Put(e, obj))
|
require.NoError(t, Put(context.Background(), e, obj))
|
||||||
|
|
||||||
// block executions
|
// block executions
|
||||||
errBlock := errors.New("block exec err")
|
errBlock := errors.New("block exec err")
|
||||||
|
|
|
@ -4,11 +4,14 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,6 +50,13 @@ func (p *DeletePrm) WithForceRemoval() {
|
||||||
// on operations with that object) if WithForceRemoval option has
|
// on operations with that object) if WithForceRemoval option has
|
||||||
// been provided.
|
// been provided.
|
||||||
func (e *StorageEngine) Delete(ctx context.Context, prm DeletePrm) (res DeleteRes, err error) {
|
func (e *StorageEngine) Delete(ctx context.Context, prm DeletePrm) (res DeleteRes, err error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.Delete",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.addr.EncodeToString()),
|
||||||
|
attribute.Bool("force_removal", prm.forceRemoval),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
err = e.execIfNotBlocked(func() error {
|
err = e.execIfNotBlocked(func() error {
|
||||||
res, err = e.delete(ctx, prm)
|
res, err = e.delete(ctx, prm)
|
||||||
return err
|
return err
|
||||||
|
@ -135,7 +145,7 @@ func (e *StorageEngine) deleteChildren(ctx context.Context, addr oid.Address, fo
|
||||||
}
|
}
|
||||||
|
|
||||||
e.iterateOverSortedShards(addr, func(_ int, sh hashedShard) (stop bool) {
|
e.iterateOverSortedShards(addr, func(_ int, sh hashedShard) (stop bool) {
|
||||||
res, err := sh.Select(selectPrm)
|
res, err := sh.Select(ctx, selectPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.log.Warn(logs.EngineErrorDuringSearchingForObjectChildren,
|
e.log.Warn(logs.EngineErrorDuringSearchingForObjectChildren,
|
||||||
zap.Stringer("addr", addr),
|
zap.Stringer("addr", addr),
|
||||||
|
|
|
@ -59,9 +59,9 @@ func TestDeleteBigObject(t *testing.T) {
|
||||||
defer e.Close()
|
defer e.Close()
|
||||||
|
|
||||||
for i := range children {
|
for i := range children {
|
||||||
require.NoError(t, Put(e, children[i]))
|
require.NoError(t, Put(context.Background(), e, children[i]))
|
||||||
}
|
}
|
||||||
require.NoError(t, Put(e, link))
|
require.NoError(t, Put(context.Background(), e, link))
|
||||||
|
|
||||||
var splitErr *objectSDK.SplitInfoError
|
var splitErr *objectSDK.SplitInfoError
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ func benchmarkExists(b *testing.B, shardNum int) {
|
||||||
addr := oidtest.Address()
|
addr := oidtest.Address()
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
obj := testutil.GenerateObjectWithCID(cidtest.ID())
|
obj := testutil.GenerateObjectWithCID(cidtest.ID())
|
||||||
err := Put(e, obj)
|
err := Put(context.Background(), e, obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func benchmarkExists(b *testing.B, shardNum int) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
ok, err := e.exists(addr)
|
ok, err := e.exists(context.Background(), addr)
|
||||||
if err != nil || ok {
|
if err != nil || ok {
|
||||||
b.Fatalf("%t %v", ok, err)
|
b.Fatalf("%t %v", ok, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ func TestErrorReporting(t *testing.T) {
|
||||||
var prm shard.PutPrm
|
var prm shard.PutPrm
|
||||||
prm.SetObject(obj)
|
prm.SetObject(obj)
|
||||||
te.ng.mtx.RLock()
|
te.ng.mtx.RLock()
|
||||||
_, err := te.ng.shards[te.shards[0].id.String()].Shard.Put(prm)
|
_, err := te.ng.shards[te.shards[0].id.String()].Shard.Put(context.Background(), prm)
|
||||||
te.ng.mtx.RUnlock()
|
te.ng.mtx.RUnlock()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ func TestErrorReporting(t *testing.T) {
|
||||||
var prm shard.PutPrm
|
var prm shard.PutPrm
|
||||||
prm.SetObject(obj)
|
prm.SetObject(obj)
|
||||||
te.ng.mtx.RLock()
|
te.ng.mtx.RLock()
|
||||||
_, err := te.ng.shards[te.shards[0].id.String()].Put(prm)
|
_, err := te.ng.shards[te.shards[0].id.String()].Put(context.Background(), prm)
|
||||||
te.ng.mtx.RUnlock()
|
te.ng.mtx.RUnlock()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ func TestBlobstorFailback(t *testing.T) {
|
||||||
var prm shard.PutPrm
|
var prm shard.PutPrm
|
||||||
prm.SetObject(obj)
|
prm.SetObject(obj)
|
||||||
te.ng.mtx.RLock()
|
te.ng.mtx.RLock()
|
||||||
_, err = te.ng.shards[te.shards[0].id.String()].Shard.Put(prm)
|
_, err = te.ng.shards[te.shards[0].id.String()].Shard.Put(context.Background(), prm)
|
||||||
te.ng.mtx.RUnlock()
|
te.ng.mtx.RUnlock()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
objs = append(objs, obj)
|
objs = append(objs, obj)
|
||||||
|
|
|
@ -57,7 +57,7 @@ func newEngineEvacuate(t *testing.T, shardNum int, objPerShard int) (*StorageEng
|
||||||
|
|
||||||
var putPrm shard.PutPrm
|
var putPrm shard.PutPrm
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
_, err := e.shards[sh.String()].Put(putPrm)
|
_, err := e.shards[sh.String()].Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ func newEngineEvacuate(t *testing.T, shardNum int, objPerShard int) (*StorageEng
|
||||||
var putPrm PutPrm
|
var putPrm PutPrm
|
||||||
putPrm.WithObject(objects[len(objects)-1])
|
putPrm.WithObject(objects[len(objects)-1])
|
||||||
|
|
||||||
_, err := e.Put(putPrm)
|
err := e.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
res, err := e.shards[ids[len(ids)-1].String()].List()
|
res, err := e.shards[ids[len(ids)-1].String()].List()
|
||||||
|
|
|
@ -10,14 +10,14 @@ import (
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *StorageEngine) exists(addr oid.Address) (bool, error) {
|
func (e *StorageEngine) exists(ctx context.Context, addr oid.Address) (bool, error) {
|
||||||
var shPrm shard.ExistsPrm
|
var shPrm shard.ExistsPrm
|
||||||
shPrm.SetAddress(addr)
|
shPrm.SetAddress(addr)
|
||||||
alreadyRemoved := false
|
alreadyRemoved := false
|
||||||
exists := false
|
exists := false
|
||||||
|
|
||||||
e.iterateOverSortedShards(addr, func(_ int, sh hashedShard) (stop bool) {
|
e.iterateOverSortedShards(addr, func(_ int, sh hashedShard) (stop bool) {
|
||||||
res, err := sh.Exists(context.TODO(), shPrm)
|
res, err := sh.Exists(ctx, shPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if shard.IsErrRemoved(err) {
|
if shard.IsErrRemoved(err) {
|
||||||
alreadyRemoved = true
|
alreadyRemoved = true
|
||||||
|
|
|
@ -48,6 +48,12 @@ func (r GetRes) Object() *objectSDK.Object {
|
||||||
//
|
//
|
||||||
// Returns an error if executions are blocked (see BlockExecution).
|
// Returns an error if executions are blocked (see BlockExecution).
|
||||||
func (e *StorageEngine) Get(ctx context.Context, prm GetPrm) (res GetRes, err error) {
|
func (e *StorageEngine) Get(ctx context.Context, prm GetPrm) (res GetRes, err error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.Get",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.addr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
err = e.execIfNotBlocked(func() error {
|
err = e.execIfNotBlocked(func() error {
|
||||||
res, err = e.get(ctx, prm)
|
res, err = e.get(ctx, prm)
|
||||||
return err
|
return err
|
||||||
|
@ -57,12 +63,6 @@ func (e *StorageEngine) Get(ctx context.Context, prm GetPrm) (res GetRes, err er
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *StorageEngine) get(ctx context.Context, prm GetPrm) (GetRes, error) {
|
func (e *StorageEngine) get(ctx context.Context, prm GetPrm) (GetRes, error) {
|
||||||
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.get",
|
|
||||||
trace.WithAttributes(
|
|
||||||
attribute.String("address", prm.addr.EncodeToString()),
|
|
||||||
))
|
|
||||||
defer span.End()
|
|
||||||
|
|
||||||
if e.metrics != nil {
|
if e.metrics != nil {
|
||||||
defer elapsed(e.metrics.AddGetDuration)()
|
defer elapsed(e.metrics.AddGetDuration)()
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,11 +55,11 @@ func TestHeadRaw(t *testing.T) {
|
||||||
putPrmLink.SetObject(link)
|
putPrmLink.SetObject(link)
|
||||||
|
|
||||||
// put most left object in one shard
|
// put most left object in one shard
|
||||||
_, err := s1.Put(putPrmLeft)
|
_, err := s1.Put(context.Background(), putPrmLeft)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// put link object in another shard
|
// put link object in another shard
|
||||||
_, err = s2.Put(putPrmLink)
|
_, err = s2.Put(context.Background(), putPrmLink)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// head with raw flag should return SplitInfoError
|
// head with raw flag should return SplitInfoError
|
||||||
|
|
|
@ -4,12 +4,15 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,6 +65,9 @@ var errInhumeFailure = errors.New("inhume operation failed")
|
||||||
//
|
//
|
||||||
// Returns an error if executions are blocked (see BlockExecution).
|
// Returns an error if executions are blocked (see BlockExecution).
|
||||||
func (e *StorageEngine) Inhume(ctx context.Context, prm InhumePrm) (res InhumeRes, err error) {
|
func (e *StorageEngine) Inhume(ctx context.Context, prm InhumePrm) (res InhumeRes, err error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.Inhume")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
err = e.execIfNotBlocked(func() error {
|
err = e.execIfNotBlocked(func() error {
|
||||||
res, err = e.inhume(ctx, prm)
|
res, err = e.inhume(ctx, prm)
|
||||||
return err
|
return err
|
||||||
|
@ -82,7 +88,7 @@ func (e *StorageEngine) inhume(ctx context.Context, prm InhumePrm) (InhumeRes, e
|
||||||
|
|
||||||
for i := range prm.addrs {
|
for i := range prm.addrs {
|
||||||
if !prm.forceRemoval {
|
if !prm.forceRemoval {
|
||||||
locked, err := e.IsLocked(prm.addrs[i])
|
locked, err := e.IsLocked(ctx, prm.addrs[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.log.Warn(logs.EngineRemovingAnObjectWithoutFullLockingCheck,
|
e.log.Warn(logs.EngineRemovingAnObjectWithoutFullLockingCheck,
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
|
@ -181,13 +187,19 @@ func (e *StorageEngine) inhumeAddr(ctx context.Context, addr oid.Address, prm sh
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsLocked checks whether an object is locked according to StorageEngine's state.
|
// IsLocked checks whether an object is locked according to StorageEngine's state.
|
||||||
func (e *StorageEngine) IsLocked(addr oid.Address) (bool, error) {
|
func (e *StorageEngine) IsLocked(ctx context.Context, addr oid.Address) (bool, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.IsLocked",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", addr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
var locked bool
|
var locked bool
|
||||||
var err error
|
var err error
|
||||||
var outErr error
|
var outErr error
|
||||||
|
|
||||||
e.iterateOverUnsortedShards(func(h hashedShard) (stop bool) {
|
e.iterateOverUnsortedShards(func(h hashedShard) (stop bool) {
|
||||||
locked, err = h.Shard.IsLocked(addr)
|
locked, err = h.Shard.IsLocked(ctx, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.reportShardError(h, "can't check object's lockers", err, zap.Stringer("addr", addr))
|
e.reportShardError(h, "can't check object's lockers", err, zap.Stringer("addr", addr))
|
||||||
outErr = err
|
outErr = err
|
||||||
|
@ -206,7 +218,7 @@ func (e *StorageEngine) IsLocked(addr oid.Address) (bool, error) {
|
||||||
|
|
||||||
func (e *StorageEngine) processExpiredTombstones(ctx context.Context, addrs []meta.TombstonedObject) {
|
func (e *StorageEngine) processExpiredTombstones(ctx context.Context, addrs []meta.TombstonedObject) {
|
||||||
e.iterateOverUnsortedShards(func(sh hashedShard) (stop bool) {
|
e.iterateOverUnsortedShards(func(sh hashedShard) (stop bool) {
|
||||||
sh.HandleExpiredTombstones(addrs)
|
sh.HandleExpiredTombstones(ctx, addrs)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
|
|
@ -42,7 +42,7 @@ func TestStorageEngine_Inhume(t *testing.T) {
|
||||||
e := testNewEngine(t).setShardsNum(t, 1).engine
|
e := testNewEngine(t).setShardsNum(t, 1).engine
|
||||||
defer e.Close()
|
defer e.Close()
|
||||||
|
|
||||||
err := Put(e, parent)
|
err := Put(context.Background(), e, parent)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var inhumePrm InhumePrm
|
var inhumePrm InhumePrm
|
||||||
|
@ -51,7 +51,7 @@ func TestStorageEngine_Inhume(t *testing.T) {
|
||||||
_, err = e.Inhume(context.Background(), inhumePrm)
|
_, err = e.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
addrs, err := Select(e, cnr, fs)
|
addrs, err := Select(context.Background(), e, cnr, fs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Empty(t, addrs)
|
require.Empty(t, addrs)
|
||||||
})
|
})
|
||||||
|
@ -65,12 +65,12 @@ func TestStorageEngine_Inhume(t *testing.T) {
|
||||||
|
|
||||||
var putChild shard.PutPrm
|
var putChild shard.PutPrm
|
||||||
putChild.SetObject(child)
|
putChild.SetObject(child)
|
||||||
_, err := s1.Put(putChild)
|
_, err := s1.Put(context.Background(), putChild)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var putLink shard.PutPrm
|
var putLink shard.PutPrm
|
||||||
putLink.SetObject(link)
|
putLink.SetObject(link)
|
||||||
_, err = s2.Put(putLink)
|
_, err = s2.Put(context.Background(), putLink)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var inhumePrm InhumePrm
|
var inhumePrm InhumePrm
|
||||||
|
@ -79,7 +79,7 @@ func TestStorageEngine_Inhume(t *testing.T) {
|
||||||
_, err = e.Inhume(context.Background(), inhumePrm)
|
_, err = e.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
addrs, err := Select(e, cnr, fs)
|
addrs, err := Select(context.Background(), e, cnr, fs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Empty(t, addrs)
|
require.Empty(t, addrs)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -35,7 +36,7 @@ func TestListWithCursor(t *testing.T) {
|
||||||
var prm PutPrm
|
var prm PutPrm
|
||||||
prm.WithObject(obj)
|
prm.WithObject(obj)
|
||||||
|
|
||||||
_, err := e.Put(prm)
|
err := e.Put(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected = append(expected, object.AddressWithType{Type: objectSDK.TypeRegular, Address: object.AddressOf(obj)})
|
expected = append(expected, object.AddressWithType{Type: objectSDK.TypeRegular, Address: object.AddressOf(obj)})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,15 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errLockFailed = errors.New("lock operation failed")
|
var errLockFailed = errors.New("lock operation failed")
|
||||||
|
@ -20,19 +23,27 @@ var errLockFailed = errors.New("lock operation failed")
|
||||||
// Allows locking regular objects only (otherwise returns apistatus.LockNonRegularObject).
|
// Allows locking regular objects only (otherwise returns apistatus.LockNonRegularObject).
|
||||||
//
|
//
|
||||||
// Locked list should be unique. Panics if it is empty.
|
// Locked list should be unique. Panics if it is empty.
|
||||||
func (e *StorageEngine) Lock(idCnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
func (e *StorageEngine) Lock(ctx context.Context, idCnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.Lock",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", idCnr.EncodeToString()),
|
||||||
|
attribute.String("locker", locker.EncodeToString()),
|
||||||
|
attribute.Int("locked_count", len(locked)),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
return e.execIfNotBlocked(func() error {
|
return e.execIfNotBlocked(func() error {
|
||||||
return e.lock(idCnr, locker, locked)
|
return e.lock(ctx, idCnr, locker, locked)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *StorageEngine) lock(idCnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
func (e *StorageEngine) lock(ctx context.Context, idCnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
||||||
for i := range locked {
|
for i := range locked {
|
||||||
switch e.lockSingle(idCnr, locker, locked[i], true) {
|
switch e.lockSingle(ctx, idCnr, locker, locked[i], true) {
|
||||||
case 1:
|
case 1:
|
||||||
return logicerr.Wrap(apistatus.LockNonRegularObject{})
|
return logicerr.Wrap(apistatus.LockNonRegularObject{})
|
||||||
case 0:
|
case 0:
|
||||||
switch e.lockSingle(idCnr, locker, locked[i], false) {
|
switch e.lockSingle(ctx, idCnr, locker, locked[i], false) {
|
||||||
case 1:
|
case 1:
|
||||||
return logicerr.Wrap(apistatus.LockNonRegularObject{})
|
return logicerr.Wrap(apistatus.LockNonRegularObject{})
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -48,7 +59,7 @@ func (e *StorageEngine) lock(idCnr cid.ID, locker oid.ID, locked []oid.ID) error
|
||||||
// - 0: fail
|
// - 0: fail
|
||||||
// - 1: locking irregular object
|
// - 1: locking irregular object
|
||||||
// - 2: ok
|
// - 2: ok
|
||||||
func (e *StorageEngine) lockSingle(idCnr cid.ID, locker, locked oid.ID, checkExists bool) (status uint8) {
|
func (e *StorageEngine) lockSingle(ctx context.Context, idCnr cid.ID, locker, locked oid.ID, checkExists bool) (status uint8) {
|
||||||
// code is pretty similar to inhumeAddr, maybe unify?
|
// code is pretty similar to inhumeAddr, maybe unify?
|
||||||
root := false
|
root := false
|
||||||
var errIrregular apistatus.LockNonRegularObject
|
var errIrregular apistatus.LockNonRegularObject
|
||||||
|
@ -70,7 +81,7 @@ func (e *StorageEngine) lockSingle(idCnr cid.ID, locker, locked oid.ID, checkExi
|
||||||
var existsPrm shard.ExistsPrm
|
var existsPrm shard.ExistsPrm
|
||||||
existsPrm.SetAddress(addrLocked)
|
existsPrm.SetAddress(addrLocked)
|
||||||
|
|
||||||
exRes, err := sh.Exists(context.TODO(), existsPrm)
|
exRes, err := sh.Exists(ctx, existsPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var siErr *objectSDK.SplitInfoError
|
var siErr *objectSDK.SplitInfoError
|
||||||
if !errors.As(err, &siErr) {
|
if !errors.As(err, &siErr) {
|
||||||
|
@ -90,7 +101,7 @@ func (e *StorageEngine) lockSingle(idCnr cid.ID, locker, locked oid.ID, checkExi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := sh.Lock(idCnr, locker, []oid.ID{locked})
|
err := sh.Lock(ctx, idCnr, locker, []oid.ID{locked})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.reportShardError(sh, "could not lock object in shard", err)
|
e.reportShardError(sh, "could not lock object in shard", err)
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ func TestLockUserScenario(t *testing.T) {
|
||||||
id, _ := obj.ID()
|
id, _ := obj.ID()
|
||||||
objAddr.SetObject(id)
|
objAddr.SetObject(id)
|
||||||
|
|
||||||
err = Put(e, obj)
|
err = Put(context.Background(), e, obj)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// 2.
|
// 2.
|
||||||
|
@ -107,10 +107,10 @@ func TestLockUserScenario(t *testing.T) {
|
||||||
locker.WriteMembers([]oid.ID{id})
|
locker.WriteMembers([]oid.ID{id})
|
||||||
object.WriteLock(lockerObj, locker)
|
object.WriteLock(lockerObj, locker)
|
||||||
|
|
||||||
err = Put(e, lockerObj)
|
err = Put(context.Background(), e, lockerObj)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = e.Lock(cnr, lockerID, []oid.ID{id})
|
err = e.Lock(context.Background(), cnr, lockerID, []oid.ID{id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// 3.
|
// 3.
|
||||||
|
@ -125,7 +125,7 @@ func TestLockUserScenario(t *testing.T) {
|
||||||
tombObj.SetID(tombForLockID)
|
tombObj.SetID(tombForLockID)
|
||||||
tombObj.SetAttributes(a)
|
tombObj.SetAttributes(a)
|
||||||
|
|
||||||
err = Put(e, tombObj)
|
err = Put(context.Background(), e, tombObj)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
inhumePrm.WithTarget(tombForLockAddr, lockerAddr)
|
inhumePrm.WithTarget(tombForLockAddr, lockerAddr)
|
||||||
|
@ -180,7 +180,7 @@ func TestLockExpiration(t *testing.T) {
|
||||||
// 1.
|
// 1.
|
||||||
obj := testutil.GenerateObjectWithCID(cnr)
|
obj := testutil.GenerateObjectWithCID(cnr)
|
||||||
|
|
||||||
err = Put(e, obj)
|
err = Put(context.Background(), e, obj)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// 2.
|
// 2.
|
||||||
|
@ -192,13 +192,13 @@ func TestLockExpiration(t *testing.T) {
|
||||||
lock.SetType(object.TypeLock)
|
lock.SetType(object.TypeLock)
|
||||||
lock.SetAttributes(a)
|
lock.SetAttributes(a)
|
||||||
|
|
||||||
err = Put(e, lock)
|
err = Put(context.Background(), e, lock)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
id, _ := obj.ID()
|
id, _ := obj.ID()
|
||||||
idLock, _ := lock.ID()
|
idLock, _ := lock.ID()
|
||||||
|
|
||||||
err = e.Lock(cnr, idLock, []oid.ID{id})
|
err = e.Lock(context.Background(), cnr, idLock, []oid.ID{id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var inhumePrm InhumePrm
|
var inhumePrm InhumePrm
|
||||||
|
@ -255,20 +255,20 @@ func TestLockForceRemoval(t *testing.T) {
|
||||||
// 1.
|
// 1.
|
||||||
obj := testutil.GenerateObjectWithCID(cnr)
|
obj := testutil.GenerateObjectWithCID(cnr)
|
||||||
|
|
||||||
err = Put(e, obj)
|
err = Put(context.Background(), e, obj)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// 2.
|
// 2.
|
||||||
lock := testutil.GenerateObjectWithCID(cnr)
|
lock := testutil.GenerateObjectWithCID(cnr)
|
||||||
lock.SetType(object.TypeLock)
|
lock.SetType(object.TypeLock)
|
||||||
|
|
||||||
err = Put(e, lock)
|
err = Put(context.Background(), e, lock)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
id, _ := obj.ID()
|
id, _ := obj.ID()
|
||||||
idLock, _ := lock.ID()
|
idLock, _ := lock.ID()
|
||||||
|
|
||||||
err = e.Lock(cnr, idLock, []oid.ID{id})
|
err = e.Lock(context.Background(), cnr, idLock, []oid.ID{id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// 3.
|
// 3.
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor"
|
||||||
|
@ -12,6 +13,8 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,9 +23,6 @@ type PutPrm struct {
|
||||||
obj *objectSDK.Object
|
obj *objectSDK.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutRes groups the resulting values of Put operation.
|
|
||||||
type PutRes struct{}
|
|
||||||
|
|
||||||
var errPutShard = errors.New("could not put object to any shard")
|
var errPutShard = errors.New("could not put object to any shard")
|
||||||
|
|
||||||
// WithObject is a Put option to set object to save.
|
// WithObject is a Put option to set object to save.
|
||||||
|
@ -40,16 +40,22 @@ func (p *PutPrm) WithObject(obj *objectSDK.Object) {
|
||||||
// Returns an error if executions are blocked (see BlockExecution).
|
// Returns an error if executions are blocked (see BlockExecution).
|
||||||
//
|
//
|
||||||
// Returns an error of type apistatus.ObjectAlreadyRemoved if the object has been marked as removed.
|
// Returns an error of type apistatus.ObjectAlreadyRemoved if the object has been marked as removed.
|
||||||
func (e *StorageEngine) Put(prm PutPrm) (res PutRes, err error) {
|
func (e *StorageEngine) Put(ctx context.Context, prm PutPrm) (err error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.Put",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", object.AddressOf(prm.obj).EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
err = e.execIfNotBlocked(func() error {
|
err = e.execIfNotBlocked(func() error {
|
||||||
res, err = e.put(prm)
|
err = e.put(ctx, prm)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *StorageEngine) put(prm PutPrm) (PutRes, error) {
|
func (e *StorageEngine) put(ctx context.Context, prm PutPrm) error {
|
||||||
if e.metrics != nil {
|
if e.metrics != nil {
|
||||||
defer elapsed(e.metrics.AddPutDuration)()
|
defer elapsed(e.metrics.AddPutDuration)()
|
||||||
}
|
}
|
||||||
|
@ -58,9 +64,9 @@ func (e *StorageEngine) put(prm PutPrm) (PutRes, error) {
|
||||||
|
|
||||||
// In #1146 this check was parallelized, however, it became
|
// In #1146 this check was parallelized, however, it became
|
||||||
// much slower on fast machines for 4 shards.
|
// much slower on fast machines for 4 shards.
|
||||||
_, err := e.exists(addr)
|
_, err := e.exists(ctx, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PutRes{}, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
finished := false
|
finished := false
|
||||||
|
@ -74,7 +80,7 @@ func (e *StorageEngine) put(prm PutPrm) (PutRes, error) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
putDone, exists := e.putToShard(context.TODO(), sh, ind, pool, addr, prm.obj)
|
putDone, exists := e.putToShard(ctx, sh, ind, pool, addr, prm.obj)
|
||||||
finished = putDone || exists
|
finished = putDone || exists
|
||||||
return finished
|
return finished
|
||||||
})
|
})
|
||||||
|
@ -83,7 +89,7 @@ func (e *StorageEngine) put(prm PutPrm) (PutRes, error) {
|
||||||
err = errPutShard
|
err = errPutShard
|
||||||
}
|
}
|
||||||
|
|
||||||
return PutRes{}, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// putToShard puts object to sh.
|
// putToShard puts object to sh.
|
||||||
|
@ -117,7 +123,7 @@ func (e *StorageEngine) putToShard(ctx context.Context, sh hashedShard, ind int,
|
||||||
var toMoveItPrm shard.ToMoveItPrm
|
var toMoveItPrm shard.ToMoveItPrm
|
||||||
toMoveItPrm.SetAddress(addr)
|
toMoveItPrm.SetAddress(addr)
|
||||||
|
|
||||||
_, err = sh.ToMoveIt(toMoveItPrm)
|
_, err = sh.ToMoveIt(ctx, toMoveItPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.log.Warn(logs.EngineCouldNotMarkObjectForShardRelocation,
|
e.log.Warn(logs.EngineCouldNotMarkObjectForShardRelocation,
|
||||||
zap.Stringer("shard", sh.ID()),
|
zap.Stringer("shard", sh.ID()),
|
||||||
|
@ -132,7 +138,7 @@ func (e *StorageEngine) putToShard(ctx context.Context, sh hashedShard, ind int,
|
||||||
var putPrm shard.PutPrm
|
var putPrm shard.PutPrm
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
|
|
||||||
_, err = sh.Put(putPrm)
|
_, err = sh.Put(ctx, putPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, shard.ErrReadOnlyMode) || errors.Is(err, blobstor.ErrNoPlaceFound) ||
|
if errors.Is(err, shard.ErrReadOnlyMode) || errors.Is(err, blobstor.ErrNoPlaceFound) ||
|
||||||
errors.Is(err, common.ErrReadOnly) || errors.Is(err, common.ErrNoSpace) {
|
errors.Is(err, common.ErrReadOnly) || errors.Is(err, common.ErrNoSpace) {
|
||||||
|
@ -157,11 +163,9 @@ func (e *StorageEngine) putToShard(ctx context.Context, sh hashedShard, ind int,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put writes provided object to local storage.
|
// Put writes provided object to local storage.
|
||||||
func Put(storage *StorageEngine, obj *objectSDK.Object) error {
|
func Put(ctx context.Context, storage *StorageEngine, obj *objectSDK.Object) error {
|
||||||
var putPrm PutPrm
|
var putPrm PutPrm
|
||||||
putPrm.WithObject(obj)
|
putPrm.WithObject(obj)
|
||||||
|
|
||||||
_, err := storage.Put(putPrm)
|
return storage.Put(ctx, putPrm)
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ func (e *StorageEngine) removeObjects(ctx context.Context, ch <-chan oid.Address
|
||||||
|
|
||||||
var deletePrm shard.DeletePrm
|
var deletePrm shard.DeletePrm
|
||||||
deletePrm.SetAddresses(addr)
|
deletePrm.SetAddresses(addr)
|
||||||
_, err = shards[i].Delete(deletePrm)
|
_, err = shards[i].Delete(ctx, deletePrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,10 +49,10 @@ func TestRebalance(t *testing.T) {
|
||||||
te.ng.mtx.RLock()
|
te.ng.mtx.RLock()
|
||||||
// Every 3rd object (i%3 == 0) is put to both shards, others are distributed.
|
// Every 3rd object (i%3 == 0) is put to both shards, others are distributed.
|
||||||
if i%3 != 1 {
|
if i%3 != 1 {
|
||||||
_, err1 = te.ng.shards[te.shards[0].id.String()].Shard.Put(prm)
|
_, err1 = te.ng.shards[te.shards[0].id.String()].Shard.Put(context.Background(), prm)
|
||||||
}
|
}
|
||||||
if i%3 != 2 {
|
if i%3 != 2 {
|
||||||
_, err2 = te.ng.shards[te.shards[1].id.String()].Shard.Put(prm)
|
_, err2 = te.ng.shards[te.shards[1].id.String()].Shard.Put(context.Background(), prm)
|
||||||
}
|
}
|
||||||
te.ng.mtx.RUnlock()
|
te.ng.mtx.RUnlock()
|
||||||
|
|
||||||
|
@ -109,8 +109,8 @@ func TestRebalanceSingleThread(t *testing.T) {
|
||||||
var prm shard.PutPrm
|
var prm shard.PutPrm
|
||||||
prm.SetObject(obj)
|
prm.SetObject(obj)
|
||||||
te.ng.mtx.RLock()
|
te.ng.mtx.RLock()
|
||||||
_, err1 := te.ng.shards[te.shards[0].id.String()].Shard.Put(prm)
|
_, err1 := te.ng.shards[te.shards[0].id.String()].Shard.Put(context.Background(), prm)
|
||||||
_, err2 := te.ng.shards[te.shards[1].id.String()].Shard.Put(prm)
|
_, err2 := te.ng.shards[te.shards[1].id.String()].Shard.Put(context.Background(), prm)
|
||||||
te.ng.mtx.RUnlock()
|
te.ng.mtx.RUnlock()
|
||||||
require.NoError(t, err1)
|
require.NoError(t, err1)
|
||||||
require.NoError(t, err2)
|
require.NoError(t, err2)
|
||||||
|
@ -162,8 +162,8 @@ func TestRebalanceExitByContext(t *testing.T) {
|
||||||
prm.SetObject(objects[i])
|
prm.SetObject(objects[i])
|
||||||
|
|
||||||
te.ng.mtx.RLock()
|
te.ng.mtx.RLock()
|
||||||
_, err1 := te.ng.shards[te.shards[0].id.String()].Shard.Put(prm)
|
_, err1 := te.ng.shards[te.shards[0].id.String()].Shard.Put(context.Background(), prm)
|
||||||
_, err2 := te.ng.shards[te.shards[1].id.String()].Shard.Put(prm)
|
_, err2 := te.ng.shards[te.shards[1].id.String()].Shard.Put(context.Background(), prm)
|
||||||
te.ng.mtx.RUnlock()
|
te.ng.mtx.RUnlock()
|
||||||
|
|
||||||
require.NoError(t, err1)
|
require.NoError(t, err1)
|
||||||
|
|
|
@ -1,11 +1,24 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
// RestoreShard restores objects from dump to the shard with provided identifier.
|
// RestoreShard restores objects from dump to the shard with provided identifier.
|
||||||
//
|
//
|
||||||
// Returns an error if shard is not read-only.
|
// Returns an error if shard is not read-only.
|
||||||
func (e *StorageEngine) RestoreShard(id *shard.ID, prm shard.RestorePrm) error {
|
func (e *StorageEngine) RestoreShard(ctx context.Context, id *shard.ID, prm shard.RestorePrm) error {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.RestoreShard",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("shard_id", id.String()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
e.mtx.RLock()
|
e.mtx.RLock()
|
||||||
defer e.mtx.RUnlock()
|
defer e.mtx.RUnlock()
|
||||||
|
|
||||||
|
@ -14,6 +27,6 @@ func (e *StorageEngine) RestoreShard(id *shard.ID, prm shard.RestorePrm) error {
|
||||||
return errShardNotFound
|
return errShardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := sh.Restore(prm)
|
_, err := sh.Restore(ctx, prm)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SelectPrm groups the parameters of Select operation.
|
// SelectPrm groups the parameters of Select operation.
|
||||||
|
@ -38,16 +43,22 @@ func (r SelectRes) AddressList() []oid.Address {
|
||||||
// Returns any error encountered that did not allow to completely select the objects.
|
// Returns any error encountered that did not allow to completely select the objects.
|
||||||
//
|
//
|
||||||
// Returns an error if executions are blocked (see BlockExecution).
|
// Returns an error if executions are blocked (see BlockExecution).
|
||||||
func (e *StorageEngine) Select(prm SelectPrm) (res SelectRes, err error) {
|
func (e *StorageEngine) Select(ctx context.Context, prm SelectPrm) (res SelectRes, err error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.Select",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", prm.cnr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
err = e.execIfNotBlocked(func() error {
|
err = e.execIfNotBlocked(func() error {
|
||||||
res, err = e._select(prm)
|
res, err = e._select(ctx, prm)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *StorageEngine) _select(prm SelectPrm) (SelectRes, error) {
|
func (e *StorageEngine) _select(ctx context.Context, prm SelectPrm) (SelectRes, error) {
|
||||||
if e.metrics != nil {
|
if e.metrics != nil {
|
||||||
defer elapsed(e.metrics.AddSearchDuration)()
|
defer elapsed(e.metrics.AddSearchDuration)()
|
||||||
}
|
}
|
||||||
|
@ -62,7 +73,7 @@ func (e *StorageEngine) _select(prm SelectPrm) (SelectRes, error) {
|
||||||
shPrm.SetFilters(prm.filters)
|
shPrm.SetFilters(prm.filters)
|
||||||
|
|
||||||
e.iterateOverUnsortedShards(func(sh hashedShard) (stop bool) {
|
e.iterateOverUnsortedShards(func(sh hashedShard) (stop bool) {
|
||||||
res, err := sh.Select(shPrm)
|
res, err := sh.Select(ctx, shPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.reportShardError(sh, "could not select objects from shard", err)
|
e.reportShardError(sh, "could not select objects from shard", err)
|
||||||
return false
|
return false
|
||||||
|
@ -133,12 +144,12 @@ func (e *StorageEngine) list(limit uint64) (SelectRes, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select selects objects from local storage using provided filters.
|
// Select selects objects from local storage using provided filters.
|
||||||
func Select(storage *StorageEngine, cnr cid.ID, fs object.SearchFilters) ([]oid.Address, error) {
|
func Select(ctx context.Context, storage *StorageEngine, cnr cid.ID, fs object.SearchFilters) ([]oid.Address, error) {
|
||||||
var selectPrm SelectPrm
|
var selectPrm SelectPrm
|
||||||
selectPrm.WithContainerID(cnr)
|
selectPrm.WithContainerID(cnr)
|
||||||
selectPrm.WithFilters(fs)
|
selectPrm.WithFilters(fs)
|
||||||
|
|
||||||
res, err := storage.Select(selectPrm)
|
res, err := storage.Select(ctx, selectPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,39 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/pilorama"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/pilorama"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
||||||
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ pilorama.Forest = (*StorageEngine)(nil)
|
var _ pilorama.Forest = (*StorageEngine)(nil)
|
||||||
|
|
||||||
// TreeMove implements the pilorama.Forest interface.
|
// TreeMove implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeMove(d pilorama.CIDDescriptor, treeID string, m *pilorama.Move) (*pilorama.Move, error) {
|
func (e *StorageEngine) TreeMove(ctx context.Context, d pilorama.CIDDescriptor, treeID string, m *pilorama.Move) (*pilorama.Move, error) {
|
||||||
index, lst, err := e.getTreeShard(d.CID, treeID)
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeMove",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", d.CID.EncodeToString()),
|
||||||
|
attribute.Int("position", d.Position),
|
||||||
|
attribute.Int("size", d.Size),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
index, lst, err := e.getTreeShard(ctx, d.CID, treeID)
|
||||||
if err != nil && !errors.Is(err, pilorama.ErrTreeNotFound) {
|
if err != nil && !errors.Is(err, pilorama.ErrTreeNotFound) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lm, err := lst[index].TreeMove(d, treeID, m)
|
lm, err := lst[index].TreeMove(ctx, d, treeID, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, shard.ErrReadOnlyMode) && err != shard.ErrPiloramaDisabled {
|
if !errors.Is(err, shard.ErrReadOnlyMode) && err != shard.ErrPiloramaDisabled {
|
||||||
e.reportShardError(lst[index], "can't perform `TreeMove`", err,
|
e.reportShardError(lst[index], "can't perform `TreeMove`", err,
|
||||||
|
@ -32,13 +47,26 @@ func (e *StorageEngine) TreeMove(d pilorama.CIDDescriptor, treeID string, m *pil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeAddByPath implements the pilorama.Forest interface.
|
// TreeAddByPath implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeAddByPath(d pilorama.CIDDescriptor, treeID string, attr string, path []string, m []pilorama.KeyValue) ([]pilorama.Move, error) {
|
func (e *StorageEngine) TreeAddByPath(ctx context.Context, d pilorama.CIDDescriptor, treeID string, attr string, path []string, m []pilorama.KeyValue) ([]pilorama.Move, error) {
|
||||||
index, lst, err := e.getTreeShard(d.CID, treeID)
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeAddByPath",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", d.CID.EncodeToString()),
|
||||||
|
attribute.Int("position", d.Position),
|
||||||
|
attribute.Int("size", d.Size),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("attr", attr),
|
||||||
|
attribute.Int("path_count", len(path)),
|
||||||
|
attribute.Int("meta_count", len(m)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
index, lst, err := e.getTreeShard(ctx, d.CID, treeID)
|
||||||
if err != nil && !errors.Is(err, pilorama.ErrTreeNotFound) {
|
if err != nil && !errors.Is(err, pilorama.ErrTreeNotFound) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lm, err := lst[index].TreeAddByPath(d, treeID, attr, path, m)
|
lm, err := lst[index].TreeAddByPath(ctx, d, treeID, attr, path, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, shard.ErrReadOnlyMode) && err != shard.ErrPiloramaDisabled {
|
if !errors.Is(err, shard.ErrReadOnlyMode) && err != shard.ErrPiloramaDisabled {
|
||||||
e.reportShardError(lst[index], "can't perform `TreeAddByPath`", err,
|
e.reportShardError(lst[index], "can't perform `TreeAddByPath`", err,
|
||||||
|
@ -51,13 +79,22 @@ func (e *StorageEngine) TreeAddByPath(d pilorama.CIDDescriptor, treeID string, a
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeApply implements the pilorama.Forest interface.
|
// TreeApply implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeApply(cnr cidSDK.ID, treeID string, m *pilorama.Move, backgroundSync bool) error {
|
func (e *StorageEngine) TreeApply(ctx context.Context, cnr cidSDK.ID, treeID string, m *pilorama.Move, backgroundSync bool) error {
|
||||||
index, lst, err := e.getTreeShard(cnr, treeID)
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeApply",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cnr.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.Bool("background", backgroundSync),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
index, lst, err := e.getTreeShard(ctx, cnr, treeID)
|
||||||
if err != nil && !errors.Is(err, pilorama.ErrTreeNotFound) {
|
if err != nil && !errors.Is(err, pilorama.ErrTreeNotFound) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = lst[index].TreeApply(cnr, treeID, m, backgroundSync)
|
err = lst[index].TreeApply(ctx, cnr, treeID, m, backgroundSync)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, shard.ErrReadOnlyMode) && err != shard.ErrPiloramaDisabled {
|
if !errors.Is(err, shard.ErrReadOnlyMode) && err != shard.ErrPiloramaDisabled {
|
||||||
e.reportShardError(lst[index], "can't perform `TreeApply`", err,
|
e.reportShardError(lst[index], "can't perform `TreeApply`", err,
|
||||||
|
@ -70,11 +107,22 @@ func (e *StorageEngine) TreeApply(cnr cidSDK.ID, treeID string, m *pilorama.Move
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetByPath implements the pilorama.Forest interface.
|
// TreeGetByPath implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeGetByPath(cid cidSDK.ID, treeID string, attr string, path []string, latest bool) ([]pilorama.Node, error) {
|
func (e *StorageEngine) TreeGetByPath(ctx context.Context, cid cidSDK.ID, treeID string, attr string, path []string, latest bool) ([]pilorama.Node, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeGetByPath",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("attr", attr),
|
||||||
|
attribute.Int("path_count", len(path)),
|
||||||
|
attribute.Bool("latest", latest),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var nodes []pilorama.Node
|
var nodes []pilorama.Node
|
||||||
for _, sh := range e.sortShardsByWeight(cid) {
|
for _, sh := range e.sortShardsByWeight(cid) {
|
||||||
nodes, err = sh.TreeGetByPath(cid, treeID, attr, path, latest)
|
nodes, err = sh.TreeGetByPath(ctx, cid, treeID, attr, path, latest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == shard.ErrPiloramaDisabled {
|
if err == shard.ErrPiloramaDisabled {
|
||||||
break
|
break
|
||||||
|
@ -92,12 +140,21 @@ func (e *StorageEngine) TreeGetByPath(cid cidSDK.ID, treeID string, attr string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetMeta implements the pilorama.Forest interface.
|
// TreeGetMeta implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeGetMeta(cid cidSDK.ID, treeID string, nodeID pilorama.Node) (pilorama.Meta, uint64, error) {
|
func (e *StorageEngine) TreeGetMeta(ctx context.Context, cid cidSDK.ID, treeID string, nodeID pilorama.Node) (pilorama.Meta, uint64, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeGetMeta",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("node_id", fmt.Sprintf("%d", nodeID)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var m pilorama.Meta
|
var m pilorama.Meta
|
||||||
var p uint64
|
var p uint64
|
||||||
for _, sh := range e.sortShardsByWeight(cid) {
|
for _, sh := range e.sortShardsByWeight(cid) {
|
||||||
m, p, err = sh.TreeGetMeta(cid, treeID, nodeID)
|
m, p, err = sh.TreeGetMeta(ctx, cid, treeID, nodeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == shard.ErrPiloramaDisabled {
|
if err == shard.ErrPiloramaDisabled {
|
||||||
break
|
break
|
||||||
|
@ -115,11 +172,20 @@ func (e *StorageEngine) TreeGetMeta(cid cidSDK.ID, treeID string, nodeID piloram
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetChildren implements the pilorama.Forest interface.
|
// TreeGetChildren implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeGetChildren(cid cidSDK.ID, treeID string, nodeID pilorama.Node) ([]uint64, error) {
|
func (e *StorageEngine) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID pilorama.Node) ([]uint64, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeGetChildren",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("node_id", fmt.Sprintf("%d", nodeID)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var nodes []uint64
|
var nodes []uint64
|
||||||
for _, sh := range e.sortShardsByWeight(cid) {
|
for _, sh := range e.sortShardsByWeight(cid) {
|
||||||
nodes, err = sh.TreeGetChildren(cid, treeID, nodeID)
|
nodes, err = sh.TreeGetChildren(ctx, cid, treeID, nodeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == shard.ErrPiloramaDisabled {
|
if err == shard.ErrPiloramaDisabled {
|
||||||
break
|
break
|
||||||
|
@ -137,11 +203,20 @@ func (e *StorageEngine) TreeGetChildren(cid cidSDK.ID, treeID string, nodeID pil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetOpLog implements the pilorama.Forest interface.
|
// TreeGetOpLog implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeGetOpLog(cid cidSDK.ID, treeID string, height uint64) (pilorama.Move, error) {
|
func (e *StorageEngine) TreeGetOpLog(ctx context.Context, cid cidSDK.ID, treeID string, height uint64) (pilorama.Move, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeGetOpLog",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("height", fmt.Sprintf("%d", height)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var lm pilorama.Move
|
var lm pilorama.Move
|
||||||
for _, sh := range e.sortShardsByWeight(cid) {
|
for _, sh := range e.sortShardsByWeight(cid) {
|
||||||
lm, err = sh.TreeGetOpLog(cid, treeID, height)
|
lm, err = sh.TreeGetOpLog(ctx, cid, treeID, height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == shard.ErrPiloramaDisabled {
|
if err == shard.ErrPiloramaDisabled {
|
||||||
break
|
break
|
||||||
|
@ -159,10 +234,18 @@ func (e *StorageEngine) TreeGetOpLog(cid cidSDK.ID, treeID string, height uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeDrop implements the pilorama.Forest interface.
|
// TreeDrop implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeDrop(cid cidSDK.ID, treeID string) error {
|
func (e *StorageEngine) TreeDrop(ctx context.Context, cid cidSDK.ID, treeID string) error {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeDrop",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
for _, sh := range e.sortShardsByWeight(cid) {
|
for _, sh := range e.sortShardsByWeight(cid) {
|
||||||
err = sh.TreeDrop(cid, treeID)
|
err = sh.TreeDrop(ctx, cid, treeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == shard.ErrPiloramaDisabled {
|
if err == shard.ErrPiloramaDisabled {
|
||||||
break
|
break
|
||||||
|
@ -180,11 +263,18 @@ func (e *StorageEngine) TreeDrop(cid cidSDK.ID, treeID string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeList implements the pilorama.Forest interface.
|
// TreeList implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeList(cid cidSDK.ID) ([]string, error) {
|
func (e *StorageEngine) TreeList(ctx context.Context, cid cidSDK.ID) ([]string, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeList",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
var resIDs []string
|
var resIDs []string
|
||||||
|
|
||||||
for _, sh := range e.unsortedShards() {
|
for _, sh := range e.unsortedShards() {
|
||||||
ids, err := sh.TreeList(cid)
|
ids, err := sh.TreeList(ctx, cid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, shard.ErrPiloramaDisabled) || errors.Is(err, shard.ErrReadOnlyMode) {
|
if errors.Is(err, shard.ErrPiloramaDisabled) || errors.Is(err, shard.ErrReadOnlyMode) {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -205,8 +295,16 @@ func (e *StorageEngine) TreeList(cid cidSDK.ID) ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeExists implements the pilorama.Forest interface.
|
// TreeExists implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeExists(cid cidSDK.ID, treeID string) (bool, error) {
|
func (e *StorageEngine) TreeExists(ctx context.Context, cid cidSDK.ID, treeID string) (bool, error) {
|
||||||
_, _, err := e.getTreeShard(cid, treeID)
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeExists",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
_, _, err := e.getTreeShard(ctx, cid, treeID)
|
||||||
if errors.Is(err, pilorama.ErrTreeNotFound) {
|
if errors.Is(err, pilorama.ErrTreeNotFound) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -214,13 +312,22 @@ func (e *StorageEngine) TreeExists(cid cidSDK.ID, treeID string) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeUpdateLastSyncHeight implements the pilorama.Forest interface.
|
// TreeUpdateLastSyncHeight implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeUpdateLastSyncHeight(cid cidSDK.ID, treeID string, height uint64) error {
|
func (e *StorageEngine) TreeUpdateLastSyncHeight(ctx context.Context, cid cidSDK.ID, treeID string, height uint64) error {
|
||||||
index, lst, err := e.getTreeShard(cid, treeID)
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeUpdateLastSyncHeight",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("height", fmt.Sprintf("%d", height)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
index, lst, err := e.getTreeShard(ctx, cid, treeID)
|
||||||
if err != nil && !errors.Is(err, pilorama.ErrTreeNotFound) {
|
if err != nil && !errors.Is(err, pilorama.ErrTreeNotFound) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = lst[index].TreeUpdateLastSyncHeight(cid, treeID, height)
|
err = lst[index].TreeUpdateLastSyncHeight(ctx, cid, treeID, height)
|
||||||
if err != nil && !errors.Is(err, shard.ErrReadOnlyMode) && err != shard.ErrPiloramaDisabled {
|
if err != nil && !errors.Is(err, shard.ErrReadOnlyMode) && err != shard.ErrPiloramaDisabled {
|
||||||
e.reportShardError(lst[index], "can't update tree synchronization height", err,
|
e.reportShardError(lst[index], "can't update tree synchronization height", err,
|
||||||
zap.Stringer("cid", cid),
|
zap.Stringer("cid", cid),
|
||||||
|
@ -230,11 +337,19 @@ func (e *StorageEngine) TreeUpdateLastSyncHeight(cid cidSDK.ID, treeID string, h
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeLastSyncHeight implements the pilorama.Forest interface.
|
// TreeLastSyncHeight implements the pilorama.Forest interface.
|
||||||
func (e *StorageEngine) TreeLastSyncHeight(cid cidSDK.ID, treeID string) (uint64, error) {
|
func (e *StorageEngine) TreeLastSyncHeight(ctx context.Context, cid cidSDK.ID, treeID string) (uint64, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeLastSyncHeight",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var height uint64
|
var height uint64
|
||||||
for _, sh := range e.sortShardsByWeight(cid) {
|
for _, sh := range e.sortShardsByWeight(cid) {
|
||||||
height, err = sh.TreeLastSyncHeight(cid, treeID)
|
height, err = sh.TreeLastSyncHeight(ctx, cid, treeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == shard.ErrPiloramaDisabled {
|
if err == shard.ErrPiloramaDisabled {
|
||||||
break
|
break
|
||||||
|
@ -251,10 +366,10 @@ func (e *StorageEngine) TreeLastSyncHeight(cid cidSDK.ID, treeID string) (uint64
|
||||||
return height, err
|
return height, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *StorageEngine) getTreeShard(cid cidSDK.ID, treeID string) (int, []hashedShard, error) {
|
func (e *StorageEngine) getTreeShard(ctx context.Context, cid cidSDK.ID, treeID string) (int, []hashedShard, error) {
|
||||||
lst := e.sortShardsByWeight(cid)
|
lst := e.sortShardsByWeight(cid)
|
||||||
for i, sh := range lst {
|
for i, sh := range lst {
|
||||||
exists, err := sh.TreeExists(cid, treeID)
|
exists, err := sh.TreeExists(ctx, cid, treeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -31,11 +32,11 @@ func benchmarkTreeVsSearch(b *testing.B, objCount int) {
|
||||||
for i := 0; i < objCount; i++ {
|
for i := 0; i < objCount; i++ {
|
||||||
obj := testutil.GenerateObjectWithCID(cid)
|
obj := testutil.GenerateObjectWithCID(cid)
|
||||||
testutil.AddAttribute(obj, pilorama.AttributeFilename, strconv.Itoa(i))
|
testutil.AddAttribute(obj, pilorama.AttributeFilename, strconv.Itoa(i))
|
||||||
err := Put(te.ng, obj)
|
err := Put(context.Background(), te.ng, obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = te.ng.TreeAddByPath(d, treeID, pilorama.AttributeFilename, nil,
|
_, err = te.ng.TreeAddByPath(context.Background(), d, treeID, pilorama.AttributeFilename, nil,
|
||||||
[]pilorama.KeyValue{{pilorama.AttributeFilename, []byte(strconv.Itoa(i))}})
|
[]pilorama.KeyValue{{pilorama.AttributeFilename, []byte(strconv.Itoa(i))}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
|
@ -51,7 +52,7 @@ func benchmarkTreeVsSearch(b *testing.B, objCount int) {
|
||||||
prm.WithFilters(fs)
|
prm.WithFilters(fs)
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
res, err := te.ng.Select(prm)
|
res, err := te.ng.Select(context.Background(), prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -62,7 +63,7 @@ func benchmarkTreeVsSearch(b *testing.B, objCount int) {
|
||||||
})
|
})
|
||||||
b.Run("TreeGetByPath", func(b *testing.B) {
|
b.Run("TreeGetByPath", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
nodes, err := te.ng.TreeGetByPath(cid, treeID, pilorama.AttributeFilename, []string{strconv.Itoa(objCount / 2)}, true)
|
nodes, err := te.ng.TreeGetByPath(context.Background(), cid, treeID, pilorama.AttributeFilename, []string{strconv.Itoa(objCount / 2)}, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FlushWriteCachePrm groups the parameters of FlushWriteCache operation.
|
// FlushWriteCachePrm groups the parameters of FlushWriteCache operation.
|
||||||
|
@ -26,7 +31,14 @@ func (p *FlushWriteCachePrm) SetIgnoreErrors(ignore bool) {
|
||||||
type FlushWriteCacheRes struct{}
|
type FlushWriteCacheRes struct{}
|
||||||
|
|
||||||
// FlushWriteCache flushes write-cache on a single shard.
|
// FlushWriteCache flushes write-cache on a single shard.
|
||||||
func (e *StorageEngine) FlushWriteCache(p FlushWriteCachePrm) (FlushWriteCacheRes, error) {
|
func (e *StorageEngine) FlushWriteCache(ctx context.Context, p FlushWriteCachePrm) (FlushWriteCacheRes, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.FlushWriteCache",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("shard)id", p.shardID.String()),
|
||||||
|
attribute.Bool("ignore_errors", p.ignoreErrors),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
e.mtx.RLock()
|
e.mtx.RLock()
|
||||||
sh, ok := e.shards[p.shardID.String()]
|
sh, ok := e.shards[p.shardID.String()]
|
||||||
e.mtx.RUnlock()
|
e.mtx.RUnlock()
|
||||||
|
@ -38,5 +50,5 @@ func (e *StorageEngine) FlushWriteCache(p FlushWriteCachePrm) (FlushWriteCacheRe
|
||||||
var prm shard.FlushWriteCachePrm
|
var prm shard.FlushWriteCachePrm
|
||||||
prm.SetIgnoreErrors(p.ignoreErrors)
|
prm.SetIgnoreErrors(p.ignoreErrors)
|
||||||
|
|
||||||
return FlushWriteCacheRes{}, sh.FlushWriteCache(prm)
|
return FlushWriteCacheRes{}, sh.FlushWriteCache(ctx, prm)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
|
@ -55,6 +56,6 @@ func metaExists(db *meta.DB, addr oid.Address) (bool, error) {
|
||||||
var existsPrm meta.ExistsPrm
|
var existsPrm meta.ExistsPrm
|
||||||
existsPrm.SetAddress(addr)
|
existsPrm.SetAddress(addr)
|
||||||
|
|
||||||
res, err := db.Exists(existsPrm)
|
res, err := db.Exists(context.Background(), existsPrm)
|
||||||
return res.Exists(), err
|
return res.Exists(), err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
objectcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
objectcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
|
@ -38,7 +39,7 @@ func TestCounters(t *testing.T) {
|
||||||
for i := 0; i < objCount; i++ {
|
for i := 0; i < objCount; i++ {
|
||||||
prm.SetObject(oo[i])
|
prm.SetObject(oo[i])
|
||||||
|
|
||||||
_, err = db.Put(prm)
|
_, err = db.Put(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c, err = db.ObjectCounters()
|
c, err = db.ObjectCounters()
|
||||||
|
@ -58,7 +59,7 @@ func TestCounters(t *testing.T) {
|
||||||
for i := objCount - 1; i >= 0; i-- {
|
for i := objCount - 1; i >= 0; i-- {
|
||||||
prm.SetAddresses(objectcore.AddressOf(oo[i]))
|
prm.SetAddresses(objectcore.AddressOf(oo[i]))
|
||||||
|
|
||||||
res, err := db.Delete(prm)
|
res, err := db.Delete(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, uint64(1), res.AvailableObjectsRemoved())
|
require.Equal(t, uint64(1), res.AvailableObjectsRemoved())
|
||||||
|
|
||||||
|
@ -89,7 +90,7 @@ func TestCounters(t *testing.T) {
|
||||||
prm.SetTombstoneAddress(oidtest.Address())
|
prm.SetTombstoneAddress(oidtest.Address())
|
||||||
prm.SetAddresses(inhumedObjs...)
|
prm.SetAddresses(inhumedObjs...)
|
||||||
|
|
||||||
res, err := db.Inhume(prm)
|
res, err := db.Inhume(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, uint64(len(inhumedObjs)), res.AvailableInhumed())
|
require.Equal(t, uint64(len(inhumedObjs)), res.AvailableInhumed())
|
||||||
|
|
||||||
|
@ -159,7 +160,7 @@ func TestCounters(t *testing.T) {
|
||||||
prm.SetTombstoneAddress(oidtest.Address())
|
prm.SetTombstoneAddress(oidtest.Address())
|
||||||
prm.SetAddresses(inhumedObjs...)
|
prm.SetAddresses(inhumedObjs...)
|
||||||
|
|
||||||
_, err = db.Inhume(prm)
|
_, err = db.Inhume(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c, err = db.ObjectCounters()
|
c, err = db.ObjectCounters()
|
||||||
|
@ -223,7 +224,7 @@ func TestCounters_Expired(t *testing.T) {
|
||||||
inhumePrm.SetGCMark()
|
inhumePrm.SetGCMark()
|
||||||
inhumePrm.SetAddresses(oo[0])
|
inhumePrm.SetAddresses(oo[0])
|
||||||
|
|
||||||
inhumeRes, err := db.Inhume(inhumePrm)
|
inhumeRes, err := db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, uint64(1), inhumeRes.AvailableInhumed())
|
require.Equal(t, uint64(1), inhumeRes.AvailableInhumed())
|
||||||
|
|
||||||
|
@ -240,7 +241,7 @@ func TestCounters_Expired(t *testing.T) {
|
||||||
var deletePrm meta.DeletePrm
|
var deletePrm meta.DeletePrm
|
||||||
deletePrm.SetAddresses(oo[0])
|
deletePrm.SetAddresses(oo[0])
|
||||||
|
|
||||||
deleteRes, err := db.Delete(deletePrm)
|
deleteRes, err := db.Delete(context.Background(), deletePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Zero(t, deleteRes.AvailableObjectsRemoved())
|
require.Zero(t, deleteRes.AvailableObjectsRemoved())
|
||||||
|
|
||||||
|
@ -257,7 +258,7 @@ func TestCounters_Expired(t *testing.T) {
|
||||||
|
|
||||||
deletePrm.SetAddresses(oo[0])
|
deletePrm.SetAddresses(oo[0])
|
||||||
|
|
||||||
deleteRes, err = db.Delete(deletePrm)
|
deleteRes, err = db.Delete(context.Background(), deletePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, uint64(1), deleteRes.AvailableObjectsRemoved())
|
require.Equal(t, uint64(1), deleteRes.AvailableObjectsRemoved())
|
||||||
|
|
||||||
|
@ -284,7 +285,7 @@ func putObjs(t *testing.T, db *meta.DB, count int, withParent bool) []*object.Ob
|
||||||
oo = append(oo, o)
|
oo = append(oo, o)
|
||||||
|
|
||||||
prm.SetObject(o)
|
prm.SetObject(o)
|
||||||
_, err = db.Put(prm)
|
_, err = db.Put(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c, err := db.ObjectCounters()
|
c, err := db.ObjectCounters()
|
||||||
|
|
|
@ -2,15 +2,19 @@ package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
storagelog "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/log"
|
storagelog "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/log"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeletePrm groups the parameters of Delete operation.
|
// DeletePrm groups the parameters of Delete operation.
|
||||||
|
@ -65,7 +69,13 @@ type referenceNumber struct {
|
||||||
type referenceCounter map[string]*referenceNumber
|
type referenceCounter map[string]*referenceNumber
|
||||||
|
|
||||||
// Delete removed object records from metabase indexes.
|
// Delete removed object records from metabase indexes.
|
||||||
func (db *DB) Delete(prm DeletePrm) (DeleteRes, error) {
|
func (db *DB) Delete(ctx context.Context, prm DeletePrm) (DeleteRes, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "metabase.Delete",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.Int("addr_count", len(prm.addrs)),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
db.modeMtx.RLock()
|
db.modeMtx.RLock()
|
||||||
defer db.modeMtx.RUnlock()
|
defer db.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -139,6 +140,6 @@ func metaDelete(db *meta.DB, addrs ...oid.Address) error {
|
||||||
var deletePrm meta.DeletePrm
|
var deletePrm meta.DeletePrm
|
||||||
deletePrm.SetAddresses(addrs...)
|
deletePrm.SetAddresses(addrs...)
|
||||||
|
|
||||||
_, err := db.Delete(deletePrm)
|
_, err := db.Delete(context.Background(), deletePrm)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
package meta
|
package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExistsPrm groups the parameters of Exists operation.
|
// ExistsPrm groups the parameters of Exists operation.
|
||||||
|
@ -39,7 +43,13 @@ func (p ExistsRes) Exists() bool {
|
||||||
//
|
//
|
||||||
// Returns an error of type apistatus.ObjectAlreadyRemoved if object has been placed in graveyard.
|
// Returns an error of type apistatus.ObjectAlreadyRemoved if object has been placed in graveyard.
|
||||||
// Returns the object.ErrObjectIsExpired if the object is presented but already expired.
|
// Returns the object.ErrObjectIsExpired if the object is presented but already expired.
|
||||||
func (db *DB) Exists(prm ExistsPrm) (res ExistsRes, err error) {
|
func (db *DB) Exists(ctx context.Context, prm ExistsPrm) (res ExistsRes, err error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "metabase.Exists",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.addr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
db.modeMtx.RLock()
|
db.modeMtx.RLock()
|
||||||
defer db.modeMtx.RUnlock()
|
defer db.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
package meta
|
package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetPrm groups the parameters of Get operation.
|
// GetPrm groups the parameters of Get operation.
|
||||||
|
@ -46,7 +50,14 @@ func (r GetRes) Header() *objectSDK.Object {
|
||||||
// Returns an error of type apistatus.ObjectNotFound if object is missing in DB.
|
// Returns an error of type apistatus.ObjectNotFound if object is missing in DB.
|
||||||
// Returns an error of type apistatus.ObjectAlreadyRemoved if object has been placed in graveyard.
|
// Returns an error of type apistatus.ObjectAlreadyRemoved if object has been placed in graveyard.
|
||||||
// Returns the object.ErrObjectIsExpired if the object is presented but already expired.
|
// Returns the object.ErrObjectIsExpired if the object is presented but already expired.
|
||||||
func (db *DB) Get(prm GetPrm) (res GetRes, err error) {
|
func (db *DB) Get(ctx context.Context, prm GetPrm) (res GetRes, err error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "metabase.Get",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.addr.EncodeToString()),
|
||||||
|
attribute.Bool("raw", prm.raw),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
db.modeMtx.RLock()
|
db.modeMtx.RLock()
|
||||||
defer db.modeMtx.RUnlock()
|
defer db.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -132,7 +133,7 @@ func TestDB_Get(t *testing.T) {
|
||||||
var prm meta.InhumePrm
|
var prm meta.InhumePrm
|
||||||
prm.SetAddresses(obj)
|
prm.SetAddresses(obj)
|
||||||
|
|
||||||
_, err = db.Inhume(prm)
|
_, err = db.Inhume(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, err = metaGet(db, obj, false)
|
_, err = metaGet(db, obj, false)
|
||||||
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
|
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
|
||||||
|
@ -216,7 +217,7 @@ func benchmarkGet(b *testing.B, numOfObj int) {
|
||||||
getPrm.SetAddress(addrs[counter%len(addrs)])
|
getPrm.SetAddress(addrs[counter%len(addrs)])
|
||||||
counter++
|
counter++
|
||||||
|
|
||||||
_, err := db.Get(getPrm)
|
_, err := db.Get(context.Background(), getPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -235,7 +236,7 @@ func benchmarkGet(b *testing.B, numOfObj int) {
|
||||||
var getPrm meta.GetPrm
|
var getPrm meta.GetPrm
|
||||||
getPrm.SetAddress(addrs[i%len(addrs)])
|
getPrm.SetAddress(addrs[i%len(addrs)])
|
||||||
|
|
||||||
_, err := db.Get(getPrm)
|
_, err := db.Get(context.Background(), getPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -248,6 +249,6 @@ func metaGet(db *meta.DB, addr oid.Address, raw bool) (*objectSDK.Object, error)
|
||||||
prm.SetAddress(addr)
|
prm.SetAddress(addr)
|
||||||
prm.SetRaw(raw)
|
prm.SetRaw(raw)
|
||||||
|
|
||||||
res, err := db.Get(prm)
|
res, err := db.Get(context.Background(), prm)
|
||||||
return res.Header(), err
|
return res.Header(), err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
|
@ -68,7 +69,7 @@ func TestDB_Iterate_OffsetNotFound(t *testing.T) {
|
||||||
inhumePrm.SetAddresses(object.AddressOf(obj1))
|
inhumePrm.SetAddresses(object.AddressOf(obj1))
|
||||||
inhumePrm.SetGCMark()
|
inhumePrm.SetGCMark()
|
||||||
|
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var counter int
|
var counter int
|
||||||
|
@ -138,14 +139,14 @@ func TestDB_IterateDeletedObjects(t *testing.T) {
|
||||||
inhumePrm.SetAddresses(object.AddressOf(obj1), object.AddressOf(obj2))
|
inhumePrm.SetAddresses(object.AddressOf(obj1), object.AddressOf(obj2))
|
||||||
inhumePrm.SetTombstoneAddress(addrTombstone)
|
inhumePrm.SetTombstoneAddress(addrTombstone)
|
||||||
|
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
inhumePrm.SetAddresses(object.AddressOf(obj3), object.AddressOf(obj4))
|
inhumePrm.SetAddresses(object.AddressOf(obj3), object.AddressOf(obj4))
|
||||||
inhumePrm.SetGCMark()
|
inhumePrm.SetGCMark()
|
||||||
|
|
||||||
// inhume with GC mark
|
// inhume with GC mark
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -225,7 +226,7 @@ func TestDB_IterateOverGraveyard_Offset(t *testing.T) {
|
||||||
object.AddressOf(obj3), object.AddressOf(obj4))
|
object.AddressOf(obj3), object.AddressOf(obj4))
|
||||||
inhumePrm.SetTombstoneAddress(addrTombstone)
|
inhumePrm.SetTombstoneAddress(addrTombstone)
|
||||||
|
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expectedGraveyard := []oid.Address{
|
expectedGraveyard := []oid.Address{
|
||||||
|
@ -320,7 +321,7 @@ func TestDB_IterateOverGarbage_Offset(t *testing.T) {
|
||||||
object.AddressOf(obj3), object.AddressOf(obj4))
|
object.AddressOf(obj3), object.AddressOf(obj4))
|
||||||
inhumePrm.SetGCMark()
|
inhumePrm.SetGCMark()
|
||||||
|
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expectedGarbage := []oid.Address{
|
expectedGarbage := []oid.Address{
|
||||||
|
@ -404,7 +405,7 @@ func TestDB_DropGraves(t *testing.T) {
|
||||||
inhumePrm.SetAddresses(object.AddressOf(obj1), object.AddressOf(obj2))
|
inhumePrm.SetAddresses(object.AddressOf(obj1), object.AddressOf(obj2))
|
||||||
inhumePrm.SetTombstoneAddress(addrTombstone)
|
inhumePrm.SetTombstoneAddress(addrTombstone)
|
||||||
|
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
buriedTS := make([]meta.TombstonedObject, 0)
|
buriedTS := make([]meta.TombstonedObject, 0)
|
||||||
|
|
|
@ -2,9 +2,11 @@ package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
@ -118,7 +120,10 @@ var ErrLockObjectRemoval = logicerr.New("lock object removal")
|
||||||
//
|
//
|
||||||
// NOTE: Marks any object with GC mark (despite any prohibitions on operations
|
// NOTE: Marks any object with GC mark (despite any prohibitions on operations
|
||||||
// with that object) if WithForceGCMark option has been provided.
|
// with that object) if WithForceGCMark option has been provided.
|
||||||
func (db *DB) Inhume(prm InhumePrm) (res InhumeRes, err error) {
|
func (db *DB) Inhume(ctx context.Context, prm InhumePrm) (res InhumeRes, err error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "metabase.Inhume")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
db.modeMtx.RLock()
|
db.modeMtx.RLock()
|
||||||
defer db.modeMtx.RUnlock()
|
defer db.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
|
@ -50,40 +51,40 @@ func TestInhumeTombOnTomb(t *testing.T) {
|
||||||
inhumePrm.SetTombstoneAddress(addr2)
|
inhumePrm.SetTombstoneAddress(addr2)
|
||||||
|
|
||||||
// inhume addr1 via addr2
|
// inhume addr1 via addr2
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
existsPrm.SetAddress(addr1)
|
existsPrm.SetAddress(addr1)
|
||||||
|
|
||||||
// addr1 should become inhumed {addr1:addr2}
|
// addr1 should become inhumed {addr1:addr2}
|
||||||
_, err = db.Exists(existsPrm)
|
_, err = db.Exists(context.Background(), existsPrm)
|
||||||
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
|
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
|
||||||
|
|
||||||
inhumePrm.SetAddresses(addr3)
|
inhumePrm.SetAddresses(addr3)
|
||||||
inhumePrm.SetTombstoneAddress(addr1)
|
inhumePrm.SetTombstoneAddress(addr1)
|
||||||
|
|
||||||
// try to inhume addr3 via addr1
|
// try to inhume addr3 via addr1
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// record with {addr1:addr2} should be removed from graveyard
|
// record with {addr1:addr2} should be removed from graveyard
|
||||||
// as a tomb-on-tomb; metabase should return ObjectNotFound
|
// as a tomb-on-tomb; metabase should return ObjectNotFound
|
||||||
// NOT ObjectAlreadyRemoved since that record has been removed
|
// NOT ObjectAlreadyRemoved since that record has been removed
|
||||||
// from graveyard but addr1 is still marked with GC
|
// from graveyard but addr1 is still marked with GC
|
||||||
_, err = db.Exists(existsPrm)
|
_, err = db.Exists(context.Background(), existsPrm)
|
||||||
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
|
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
|
||||||
|
|
||||||
existsPrm.SetAddress(addr3)
|
existsPrm.SetAddress(addr3)
|
||||||
|
|
||||||
// addr3 should be inhumed {addr3: addr1}
|
// addr3 should be inhumed {addr3: addr1}
|
||||||
_, err = db.Exists(existsPrm)
|
_, err = db.Exists(context.Background(), existsPrm)
|
||||||
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
|
require.ErrorAs(t, err, new(apistatus.ObjectAlreadyRemoved))
|
||||||
|
|
||||||
inhumePrm.SetAddresses(addr1)
|
inhumePrm.SetAddresses(addr1)
|
||||||
inhumePrm.SetTombstoneAddress(oidtest.Address())
|
inhumePrm.SetTombstoneAddress(oidtest.Address())
|
||||||
|
|
||||||
// try to inhume addr1 (which is already a tombstone in graveyard)
|
// try to inhume addr1 (which is already a tombstone in graveyard)
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
existsPrm.SetAddress(addr1)
|
existsPrm.SetAddress(addr1)
|
||||||
|
@ -91,7 +92,7 @@ func TestInhumeTombOnTomb(t *testing.T) {
|
||||||
// record with addr1 key should not appear in graveyard
|
// record with addr1 key should not appear in graveyard
|
||||||
// (tomb can not be inhumed) but should be kept as object
|
// (tomb can not be inhumed) but should be kept as object
|
||||||
// with GC mark
|
// with GC mark
|
||||||
_, err = db.Exists(existsPrm)
|
_, err = db.Exists(context.Background(), existsPrm)
|
||||||
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
|
require.ErrorAs(t, err, new(apistatus.ObjectNotFound))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,13 +101,13 @@ func TestInhumeLocked(t *testing.T) {
|
||||||
|
|
||||||
locked := oidtest.Address()
|
locked := oidtest.Address()
|
||||||
|
|
||||||
err := db.Lock(locked.Container(), oidtest.ID(), []oid.ID{locked.Object()})
|
err := db.Lock(context.Background(), locked.Container(), oidtest.ID(), []oid.ID{locked.Object()})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var prm meta.InhumePrm
|
var prm meta.InhumePrm
|
||||||
prm.SetAddresses(locked)
|
prm.SetAddresses(locked)
|
||||||
|
|
||||||
_, err = db.Inhume(prm)
|
_, err = db.Inhume(context.Background(), prm)
|
||||||
|
|
||||||
var e apistatus.ObjectLocked
|
var e apistatus.ObjectLocked
|
||||||
require.ErrorAs(t, err, &e)
|
require.ErrorAs(t, err, &e)
|
||||||
|
@ -117,6 +118,6 @@ func metaInhume(db *meta.DB, target, tomb oid.Address) error {
|
||||||
inhumePrm.SetAddresses(target)
|
inhumePrm.SetAddresses(target)
|
||||||
inhumePrm.SetTombstoneAddress(tomb)
|
inhumePrm.SetTombstoneAddress(tomb)
|
||||||
|
|
||||||
_, err := db.Inhume(inhumePrm)
|
_, err := db.Inhume(context.Background(), inhumePrm)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ func TestDB_IterateExpired(t *testing.T) {
|
||||||
|
|
||||||
expiredLocked := putWithExpiration(t, db, object.TypeRegular, epoch-1)
|
expiredLocked := putWithExpiration(t, db, object.TypeRegular, epoch-1)
|
||||||
|
|
||||||
require.NoError(t, db.Lock(expiredLocked.Container(), oidtest.ID(), []oid.ID{expiredLocked.Object()}))
|
require.NoError(t, db.Lock(context.Background(), expiredLocked.Container(), oidtest.ID(), []oid.ID{expiredLocked.Object()}))
|
||||||
|
|
||||||
err := db.IterateExpired(epoch, func(exp *meta.ExpiredObject) error {
|
err := db.IterateExpired(epoch, func(exp *meta.ExpiredObject) error {
|
||||||
if addr, ok := mAlive[exp.Type()]; ok {
|
if addr, ok := mAlive[exp.Type()]; ok {
|
||||||
|
@ -81,13 +82,13 @@ func TestDB_IterateCoveredByTombstones(t *testing.T) {
|
||||||
prm.SetAddresses(protected1, protected2, protectedLocked)
|
prm.SetAddresses(protected1, protected2, protectedLocked)
|
||||||
prm.SetTombstoneAddress(ts)
|
prm.SetTombstoneAddress(ts)
|
||||||
|
|
||||||
_, err = db.Inhume(prm)
|
_, err = db.Inhume(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
prm.SetAddresses(garbage)
|
prm.SetAddresses(garbage)
|
||||||
prm.SetGCMark()
|
prm.SetGCMark()
|
||||||
|
|
||||||
_, err = db.Inhume(prm)
|
_, err = db.Inhume(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var handled []oid.Address
|
var handled []oid.Address
|
||||||
|
@ -107,7 +108,7 @@ func TestDB_IterateCoveredByTombstones(t *testing.T) {
|
||||||
require.Contains(t, handled, protected2)
|
require.Contains(t, handled, protected2)
|
||||||
require.Contains(t, handled, protectedLocked)
|
require.Contains(t, handled, protectedLocked)
|
||||||
|
|
||||||
err = db.Lock(protectedLocked.Container(), oidtest.ID(), []oid.ID{protectedLocked.Object()})
|
err = db.Lock(context.Background(), protectedLocked.Container(), oidtest.ID(), []oid.ID{protectedLocked.Object()})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
handled = handled[:0]
|
handled = handled[:0]
|
||||||
|
|
|
@ -2,14 +2,18 @@ package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bucketNameLocked = []byte{lockedPrefix}
|
var bucketNameLocked = []byte{lockedPrefix}
|
||||||
|
@ -30,7 +34,15 @@ func bucketNameLockers(idCnr cid.ID, key []byte) []byte {
|
||||||
// Allows locking regular objects only (otherwise returns apistatus.LockNonRegularObject).
|
// Allows locking regular objects only (otherwise returns apistatus.LockNonRegularObject).
|
||||||
//
|
//
|
||||||
// Locked list should be unique. Panics if it is empty.
|
// Locked list should be unique. Panics if it is empty.
|
||||||
func (db *DB) Lock(cnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
func (db *DB) Lock(ctx context.Context, cnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "metabase.Lock",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cnr.EncodeToString()),
|
||||||
|
attribute.String("locker", locker.EncodeToString()),
|
||||||
|
attribute.Int("locked_count", len(locked)),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
db.modeMtx.RLock()
|
db.modeMtx.RLock()
|
||||||
defer db.modeMtx.RUnlock()
|
defer db.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
@ -266,7 +278,13 @@ func (i IsLockedRes) Locked() bool {
|
||||||
// object is considered as non-locked.
|
// object is considered as non-locked.
|
||||||
//
|
//
|
||||||
// Returns only non-logical errors related to underlying database.
|
// Returns only non-logical errors related to underlying database.
|
||||||
func (db *DB) IsLocked(prm IsLockedPrm) (res IsLockedRes, err error) {
|
func (db *DB) IsLocked(ctx context.Context, prm IsLockedPrm) (res IsLockedRes, err error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "metabase.IsLocked",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.addr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
db.modeMtx.RLock()
|
db.modeMtx.RLock()
|
||||||
defer db.modeMtx.RUnlock()
|
defer db.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
objectcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
objectcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
|
@ -20,8 +21,8 @@ func TestDB_Lock(t *testing.T) {
|
||||||
db := newDB(t)
|
db := newDB(t)
|
||||||
|
|
||||||
t.Run("empty locked list", func(t *testing.T) {
|
t.Run("empty locked list", func(t *testing.T) {
|
||||||
require.Panics(t, func() { _ = db.Lock(cnr, oid.ID{}, nil) })
|
require.Panics(t, func() { _ = db.Lock(context.Background(), cnr, oid.ID{}, nil) })
|
||||||
require.Panics(t, func() { _ = db.Lock(cnr, oid.ID{}, []oid.ID{}) })
|
require.Panics(t, func() { _ = db.Lock(context.Background(), cnr, oid.ID{}, []oid.ID{}) })
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("(ir)regular", func(t *testing.T) {
|
t.Run("(ir)regular", func(t *testing.T) {
|
||||||
|
@ -44,7 +45,7 @@ func TestDB_Lock(t *testing.T) {
|
||||||
id, _ := obj.ID()
|
id, _ := obj.ID()
|
||||||
|
|
||||||
// try to lock it
|
// try to lock it
|
||||||
err = db.Lock(cnr, oidtest.ID(), []oid.ID{id})
|
err = db.Lock(context.Background(), cnr, oidtest.ID(), []oid.ID{id})
|
||||||
if typ == object.TypeRegular {
|
if typ == object.TypeRegular {
|
||||||
require.NoError(t, err, typ)
|
require.NoError(t, err, typ)
|
||||||
} else {
|
} else {
|
||||||
|
@ -65,27 +66,27 @@ func TestDB_Lock(t *testing.T) {
|
||||||
// check locking relation
|
// check locking relation
|
||||||
|
|
||||||
inhumePrm.SetAddresses(objAddr)
|
inhumePrm.SetAddresses(objAddr)
|
||||||
_, err := db.Inhume(inhumePrm)
|
_, err := db.Inhume(context.Background(), inhumePrm)
|
||||||
require.ErrorAs(t, err, new(apistatus.ObjectLocked))
|
require.ErrorAs(t, err, new(apistatus.ObjectLocked))
|
||||||
|
|
||||||
inhumePrm.SetTombstoneAddress(oidtest.Address())
|
inhumePrm.SetTombstoneAddress(oidtest.Address())
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.ErrorAs(t, err, new(apistatus.ObjectLocked))
|
require.ErrorAs(t, err, new(apistatus.ObjectLocked))
|
||||||
|
|
||||||
// try to remove lock object
|
// try to remove lock object
|
||||||
inhumePrm.SetAddresses(lockAddr)
|
inhumePrm.SetAddresses(lockAddr)
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
// check that locking relation has not been
|
// check that locking relation has not been
|
||||||
// dropped
|
// dropped
|
||||||
|
|
||||||
inhumePrm.SetAddresses(objAddr)
|
inhumePrm.SetAddresses(objAddr)
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.ErrorAs(t, err, new(apistatus.ObjectLocked))
|
require.ErrorAs(t, err, new(apistatus.ObjectLocked))
|
||||||
|
|
||||||
inhumePrm.SetTombstoneAddress(oidtest.Address())
|
inhumePrm.SetTombstoneAddress(oidtest.Address())
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.ErrorAs(t, err, new(apistatus.ObjectLocked))
|
require.ErrorAs(t, err, new(apistatus.ObjectLocked))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ func TestDB_Lock(t *testing.T) {
|
||||||
inhumePrm.SetForceGCMark()
|
inhumePrm.SetForceGCMark()
|
||||||
inhumePrm.SetLockObjectHandling()
|
inhumePrm.SetLockObjectHandling()
|
||||||
|
|
||||||
res, err := db.Inhume(inhumePrm)
|
res, err := db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, res.DeletedLockObjects(), 1)
|
require.Len(t, res.DeletedLockObjects(), 1)
|
||||||
require.Equal(t, objectcore.AddressOf(lockObj), res.DeletedLockObjects()[0])
|
require.Equal(t, objectcore.AddressOf(lockObj), res.DeletedLockObjects()[0])
|
||||||
|
@ -117,7 +118,7 @@ func TestDB_Lock(t *testing.T) {
|
||||||
inhumePrm.SetGCMark()
|
inhumePrm.SetGCMark()
|
||||||
|
|
||||||
// now we can inhume the object
|
// now we can inhume the object
|
||||||
_, err = db.Inhume(inhumePrm)
|
_, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -134,7 +135,7 @@ func TestDB_Lock(t *testing.T) {
|
||||||
inhumePrm.SetAddresses(objectcore.AddressOf(lockObj))
|
inhumePrm.SetAddresses(objectcore.AddressOf(lockObj))
|
||||||
inhumePrm.SetLockObjectHandling()
|
inhumePrm.SetLockObjectHandling()
|
||||||
|
|
||||||
res, err := db.Inhume(inhumePrm)
|
res, err := db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, res.DeletedLockObjects(), 1)
|
require.Len(t, res.DeletedLockObjects(), 1)
|
||||||
require.Equal(t, objectcore.AddressOf(lockObj), res.DeletedLockObjects()[0])
|
require.Equal(t, objectcore.AddressOf(lockObj), res.DeletedLockObjects()[0])
|
||||||
|
@ -151,7 +152,7 @@ func TestDB_Lock(t *testing.T) {
|
||||||
for i := 0; i < objsNum; i++ {
|
for i := 0; i < objsNum; i++ {
|
||||||
inhumePrm.SetAddresses(objectcore.AddressOf(objs[i]))
|
inhumePrm.SetAddresses(objectcore.AddressOf(objs[i]))
|
||||||
|
|
||||||
res, err = db.Inhume(inhumePrm)
|
res, err = db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, res.DeletedLockObjects(), 0)
|
require.Len(t, res.DeletedLockObjects(), 0)
|
||||||
}
|
}
|
||||||
|
@ -164,7 +165,7 @@ func TestDB_Lock(t *testing.T) {
|
||||||
inhumePrm.SetForceGCMark()
|
inhumePrm.SetForceGCMark()
|
||||||
inhumePrm.SetAddresses(objectcore.AddressOf(lockObj))
|
inhumePrm.SetAddresses(objectcore.AddressOf(lockObj))
|
||||||
|
|
||||||
res, err := db.Inhume(inhumePrm)
|
res, err := db.Inhume(context.Background(), inhumePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, res.DeletedLockObjects(), 0)
|
require.Len(t, res.DeletedLockObjects(), 0)
|
||||||
})
|
})
|
||||||
|
@ -184,7 +185,7 @@ func TestDB_Lock_Expired(t *testing.T) {
|
||||||
require.ErrorIs(t, err, meta.ErrObjectIsExpired)
|
require.ErrorIs(t, err, meta.ErrObjectIsExpired)
|
||||||
|
|
||||||
// lock the obj
|
// lock the obj
|
||||||
require.NoError(t, db.Lock(addr.Container(), oidtest.ID(), []oid.ID{addr.Object()}))
|
require.NoError(t, db.Lock(context.Background(), addr.Container(), oidtest.ID(), []oid.ID{addr.Object()}))
|
||||||
|
|
||||||
// object is expired but locked, thus, must be available
|
// object is expired but locked, thus, must be available
|
||||||
_, err = metaGet(db, addr, false)
|
_, err = metaGet(db, addr, false)
|
||||||
|
@ -202,7 +203,7 @@ func TestDB_IsLocked(t *testing.T) {
|
||||||
for _, obj := range objs {
|
for _, obj := range objs {
|
||||||
prm.SetAddress(objectcore.AddressOf(obj))
|
prm.SetAddress(objectcore.AddressOf(obj))
|
||||||
|
|
||||||
res, err := db.IsLocked(prm)
|
res, err := db.IsLocked(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.True(t, res.Locked())
|
require.True(t, res.Locked())
|
||||||
|
@ -212,7 +213,7 @@ func TestDB_IsLocked(t *testing.T) {
|
||||||
|
|
||||||
prm.SetAddress(oidtest.Address())
|
prm.SetAddress(oidtest.Address())
|
||||||
|
|
||||||
res, err := db.IsLocked(prm)
|
res, err := db.IsLocked(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.False(t, res.Locked())
|
require.False(t, res.Locked())
|
||||||
|
@ -224,12 +225,12 @@ func TestDB_IsLocked(t *testing.T) {
|
||||||
var putPrm meta.PutPrm
|
var putPrm meta.PutPrm
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
|
|
||||||
_, err = db.Put(putPrm)
|
_, err = db.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
prm.SetAddress(objectcore.AddressOf(obj))
|
prm.SetAddress(objectcore.AddressOf(obj))
|
||||||
|
|
||||||
res, err = db.IsLocked(prm)
|
res, err = db.IsLocked(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.False(t, res.Locked())
|
require.False(t, res.Locked())
|
||||||
|
@ -260,7 +261,7 @@ func putAndLockObj(t *testing.T, db *meta.DB, numOfLockedObjs int) ([]*object.Ob
|
||||||
err := putBig(db, lockObj)
|
err := putBig(db, lockObj)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = db.Lock(cnr, lockID, lockedObjIDs)
|
err = db.Lock(context.Background(), cnr, lockID, lockedObjIDs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return lockedObjs, lockObj
|
return lockedObjs, lockObj
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package meta
|
package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToMoveItPrm groups the parameters of ToMoveIt operation.
|
// ToMoveItPrm groups the parameters of ToMoveIt operation.
|
||||||
|
@ -48,7 +52,13 @@ func (p MovableRes) AddressList() []oid.Address {
|
||||||
|
|
||||||
// ToMoveIt marks objects to move it into another shard. This useful for
|
// ToMoveIt marks objects to move it into another shard. This useful for
|
||||||
// faster HRW fetching.
|
// faster HRW fetching.
|
||||||
func (db *DB) ToMoveIt(prm ToMoveItPrm) (res ToMoveItRes, err error) {
|
func (db *DB) ToMoveIt(ctx context.Context, prm ToMoveItPrm) (res ToMoveItRes, err error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "metabase.ToMoveIt",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.addr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
db.modeMtx.RLock()
|
db.modeMtx.RLock()
|
||||||
defer db.modeMtx.RUnlock()
|
defer db.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
|
@ -61,7 +62,7 @@ func metaToMoveIt(db *meta.DB, addr oid.Address) error {
|
||||||
var toMovePrm meta.ToMoveItPrm
|
var toMovePrm meta.ToMoveItPrm
|
||||||
toMovePrm.SetAddress(addr)
|
toMovePrm.SetAddress(addr)
|
||||||
|
|
||||||
_, err := db.ToMoveIt(toMovePrm)
|
_, err := db.ToMoveIt(context.Background(), toMovePrm)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package meta
|
package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
gio "io"
|
gio "io"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
objectCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
objectCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
storagelog "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/log"
|
storagelog "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/log"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util"
|
||||||
|
@ -14,6 +16,8 @@ import (
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -52,7 +56,13 @@ var (
|
||||||
//
|
//
|
||||||
// Returns an error of type apistatus.ObjectAlreadyRemoved if object has been placed in graveyard.
|
// Returns an error of type apistatus.ObjectAlreadyRemoved if object has been placed in graveyard.
|
||||||
// Returns the object.ErrObjectIsExpired if the object is presented but already expired.
|
// Returns the object.ErrObjectIsExpired if the object is presented but already expired.
|
||||||
func (db *DB) Put(prm PutPrm) (res PutRes, err error) {
|
func (db *DB) Put(ctx context.Context, prm PutPrm) (res PutRes, err error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "metabase.Put",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", objectCore.AddressOf(prm.obj).EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
db.modeMtx.RLock()
|
db.modeMtx.RLock()
|
||||||
defer db.modeMtx.RUnlock()
|
defer db.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -117,7 +118,7 @@ func metaPut(db *meta.DB, obj *objectSDK.Object, id []byte) error {
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
putPrm.SetStorageID(id)
|
putPrm.SetStorageID(id)
|
||||||
|
|
||||||
_, err := db.Put(putPrm)
|
_, err := db.Put(context.Background(), putPrm)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
package meta
|
package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,7 +60,13 @@ func (r SelectRes) AddressList() []oid.Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select returns list of addresses of objects that match search filters.
|
// Select returns list of addresses of objects that match search filters.
|
||||||
func (db *DB) Select(prm SelectPrm) (res SelectRes, err error) {
|
func (db *DB) Select(ctx context.Context, prm SelectPrm) (res SelectRes, err error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "metabase.Select",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", prm.cnr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
db.modeMtx.RLock()
|
db.modeMtx.RLock()
|
||||||
defer db.modeMtx.RUnlock()
|
defer db.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -829,7 +830,7 @@ func benchmarkSelect(b *testing.B, db *meta.DB, cid cidSDK.ID, fs objectSDK.Sear
|
||||||
prm.SetFilters(fs)
|
prm.SetFilters(fs)
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
res, err := db.Select(prm)
|
res, err := db.Select(context.Background(), prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -844,6 +845,6 @@ func metaSelect(db *meta.DB, cnr cidSDK.ID, fs objectSDK.SearchFilters) ([]oid.A
|
||||||
prm.SetFilters(fs)
|
prm.SetFilters(fs)
|
||||||
prm.SetContainerID(cnr)
|
prm.SetContainerID(cnr)
|
||||||
|
|
||||||
res, err := db.Select(prm)
|
res, err := db.Select(context.Background(), prm)
|
||||||
return res.AddressList(), err
|
return res.AddressList(), err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package meta
|
package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StorageIDPrm groups the parameters of StorageID operation.
|
// StorageIDPrm groups the parameters of StorageID operation.
|
||||||
|
@ -30,7 +34,13 @@ func (r StorageIDRes) StorageID() []byte {
|
||||||
|
|
||||||
// StorageID returns storage descriptor for objects from the blobstor.
|
// StorageID returns storage descriptor for objects from the blobstor.
|
||||||
// It is put together with the object can makes get/delete operation faster.
|
// It is put together with the object can makes get/delete operation faster.
|
||||||
func (db *DB) StorageID(prm StorageIDPrm) (res StorageIDRes, err error) {
|
func (db *DB) StorageID(ctx context.Context, prm StorageIDPrm) (res StorageIDRes, err error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "metabase.StorageID",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("address", prm.addr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
db.modeMtx.RLock()
|
db.modeMtx.RLock()
|
||||||
defer db.modeMtx.RUnlock()
|
defer db.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
|
@ -63,6 +64,6 @@ func metaStorageID(db *meta.DB, addr oid.Address) ([]byte, error) {
|
||||||
var sidPrm meta.StorageIDPrm
|
var sidPrm meta.StorageIDPrm
|
||||||
sidPrm.SetAddress(addr)
|
sidPrm.SetAddress(addr)
|
||||||
|
|
||||||
r, err := db.StorageID(sidPrm)
|
r, err := db.StorageID(context.Background(), sidPrm)
|
||||||
return r.StorageID(), err
|
return r.StorageID(), err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package pilorama
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -11,12 +12,15 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util"
|
||||||
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
type boltForest struct {
|
type boltForest struct {
|
||||||
|
@ -144,7 +148,17 @@ func (t *boltForest) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeMove implements the Forest interface.
|
// TreeMove implements the Forest interface.
|
||||||
func (t *boltForest) TreeMove(d CIDDescriptor, treeID string, m *Move) (*Move, error) {
|
func (t *boltForest) TreeMove(ctx context.Context, d CIDDescriptor, treeID string, m *Move) (*Move, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeMove",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", d.CID.EncodeToString()),
|
||||||
|
attribute.Int("position", d.Position),
|
||||||
|
attribute.Int("size", d.Size),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
if !d.checkValid() {
|
if !d.checkValid() {
|
||||||
return nil, ErrInvalidCIDDescriptor
|
return nil, ErrInvalidCIDDescriptor
|
||||||
}
|
}
|
||||||
|
@ -175,7 +189,15 @@ func (t *boltForest) TreeMove(d CIDDescriptor, treeID string, m *Move) (*Move, e
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeExists implements the Forest interface.
|
// TreeExists implements the Forest interface.
|
||||||
func (t *boltForest) TreeExists(cid cidSDK.ID, treeID string) (bool, error) {
|
func (t *boltForest) TreeExists(ctx context.Context, cid cidSDK.ID, treeID string) (bool, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeExists",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
t.modeMtx.RLock()
|
t.modeMtx.RLock()
|
||||||
defer t.modeMtx.RUnlock()
|
defer t.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
@ -197,7 +219,16 @@ func (t *boltForest) TreeExists(cid cidSDK.ID, treeID string) (bool, error) {
|
||||||
var syncHeightKey = []byte{'h'}
|
var syncHeightKey = []byte{'h'}
|
||||||
|
|
||||||
// TreeUpdateLastSyncHeight implements the pilorama.Forest interface.
|
// TreeUpdateLastSyncHeight implements the pilorama.Forest interface.
|
||||||
func (t *boltForest) TreeUpdateLastSyncHeight(cid cidSDK.ID, treeID string, height uint64) error {
|
func (t *boltForest) TreeUpdateLastSyncHeight(ctx context.Context, cid cidSDK.ID, treeID string, height uint64) error {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeUpdateLastSyncHeight",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("height", fmt.Sprintf("%d", height)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
rawHeight := make([]byte, 8)
|
rawHeight := make([]byte, 8)
|
||||||
binary.LittleEndian.PutUint64(rawHeight, height)
|
binary.LittleEndian.PutUint64(rawHeight, height)
|
||||||
|
|
||||||
|
@ -214,7 +245,15 @@ func (t *boltForest) TreeUpdateLastSyncHeight(cid cidSDK.ID, treeID string, heig
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeLastSyncHeight implements the pilorama.Forest interface.
|
// TreeLastSyncHeight implements the pilorama.Forest interface.
|
||||||
func (t *boltForest) TreeLastSyncHeight(cid cidSDK.ID, treeID string) (uint64, error) {
|
func (t *boltForest) TreeLastSyncHeight(ctx context.Context, cid cidSDK.ID, treeID string) (uint64, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeLastSyncHeight",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
var height uint64
|
var height uint64
|
||||||
|
|
||||||
buck := bucketName(cid, treeID)
|
buck := bucketName(cid, treeID)
|
||||||
|
@ -235,7 +274,20 @@ func (t *boltForest) TreeLastSyncHeight(cid cidSDK.ID, treeID string) (uint64, e
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeAddByPath implements the Forest interface.
|
// TreeAddByPath implements the Forest interface.
|
||||||
func (t *boltForest) TreeAddByPath(d CIDDescriptor, treeID string, attr string, path []string, meta []KeyValue) ([]Move, error) {
|
func (t *boltForest) TreeAddByPath(ctx context.Context, d CIDDescriptor, treeID string, attr string, path []string, meta []KeyValue) ([]Move, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeAddByPath",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", d.CID.EncodeToString()),
|
||||||
|
attribute.Int("position", d.Position),
|
||||||
|
attribute.Int("size", d.Size),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("attr", attr),
|
||||||
|
attribute.Int("path_count", len(path)),
|
||||||
|
attribute.Int("meta_count", len(meta)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
if !d.checkValid() {
|
if !d.checkValid() {
|
||||||
return nil, ErrInvalidCIDDescriptor
|
return nil, ErrInvalidCIDDescriptor
|
||||||
}
|
}
|
||||||
|
@ -329,7 +381,16 @@ func (t *boltForest) findSpareID(bTree *bbolt.Bucket) uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeApply implements the Forest interface.
|
// TreeApply implements the Forest interface.
|
||||||
func (t *boltForest) TreeApply(cnr cidSDK.ID, treeID string, m *Move, backgroundSync bool) error {
|
func (t *boltForest) TreeApply(ctx context.Context, cnr cidSDK.ID, treeID string, m *Move, backgroundSync bool) error {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeApply",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cnr.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.Bool("background", backgroundSync),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
t.modeMtx.RLock()
|
t.modeMtx.RLock()
|
||||||
defer t.modeMtx.RUnlock()
|
defer t.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
@ -627,7 +688,18 @@ func (t *boltForest) isAncestor(b *bbolt.Bucket, parent, child Node) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetByPath implements the Forest interface.
|
// TreeGetByPath implements the Forest interface.
|
||||||
func (t *boltForest) TreeGetByPath(cid cidSDK.ID, treeID string, attr string, path []string, latest bool) ([]Node, error) {
|
func (t *boltForest) TreeGetByPath(ctx context.Context, cid cidSDK.ID, treeID string, attr string, path []string, latest bool) ([]Node, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeGetByPath",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("attr", attr),
|
||||||
|
attribute.Int("path_count", len(path)),
|
||||||
|
attribute.Bool("latest", latest),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
if !isAttributeInternal(attr) {
|
if !isAttributeInternal(attr) {
|
||||||
return nil, ErrNotPathAttribute
|
return nil, ErrNotPathAttribute
|
||||||
}
|
}
|
||||||
|
@ -686,7 +758,16 @@ func (t *boltForest) TreeGetByPath(cid cidSDK.ID, treeID string, attr string, pa
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetMeta implements the forest interface.
|
// TreeGetMeta implements the forest interface.
|
||||||
func (t *boltForest) TreeGetMeta(cid cidSDK.ID, treeID string, nodeID Node) (Meta, Node, error) {
|
func (t *boltForest) TreeGetMeta(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node) (Meta, Node, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeGetMeta",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("node_id", fmt.Sprintf("%d", nodeID)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
t.modeMtx.RLock()
|
t.modeMtx.RLock()
|
||||||
defer t.modeMtx.RUnlock()
|
defer t.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
@ -717,7 +798,16 @@ func (t *boltForest) TreeGetMeta(cid cidSDK.ID, treeID string, nodeID Node) (Met
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetChildren implements the Forest interface.
|
// TreeGetChildren implements the Forest interface.
|
||||||
func (t *boltForest) TreeGetChildren(cid cidSDK.ID, treeID string, nodeID Node) ([]uint64, error) {
|
func (t *boltForest) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node) ([]uint64, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeGetChildren",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("node_id", fmt.Sprintf("%d", nodeID)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
t.modeMtx.RLock()
|
t.modeMtx.RLock()
|
||||||
defer t.modeMtx.RUnlock()
|
defer t.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
@ -749,7 +839,14 @@ func (t *boltForest) TreeGetChildren(cid cidSDK.ID, treeID string, nodeID Node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeList implements the Forest interface.
|
// TreeList implements the Forest interface.
|
||||||
func (t *boltForest) TreeList(cid cidSDK.ID) ([]string, error) {
|
func (t *boltForest) TreeList(ctx context.Context, cid cidSDK.ID) ([]string, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeList",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
t.modeMtx.RLock()
|
t.modeMtx.RLock()
|
||||||
defer t.modeMtx.RUnlock()
|
defer t.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
@ -783,7 +880,16 @@ func (t *boltForest) TreeList(cid cidSDK.ID) ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetOpLog implements the pilorama.Forest interface.
|
// TreeGetOpLog implements the pilorama.Forest interface.
|
||||||
func (t *boltForest) TreeGetOpLog(cid cidSDK.ID, treeID string, height uint64) (Move, error) {
|
func (t *boltForest) TreeGetOpLog(ctx context.Context, cid cidSDK.ID, treeID string, height uint64) (Move, error) {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeGetOpLog",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
attribute.String("height", fmt.Sprintf("%d", height)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
t.modeMtx.RLock()
|
t.modeMtx.RLock()
|
||||||
defer t.modeMtx.RUnlock()
|
defer t.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
@ -813,7 +919,15 @@ func (t *boltForest) TreeGetOpLog(cid cidSDK.ID, treeID string, height uint64) (
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeDrop implements the pilorama.Forest interface.
|
// TreeDrop implements the pilorama.Forest interface.
|
||||||
func (t *boltForest) TreeDrop(cid cidSDK.ID, treeID string) error {
|
func (t *boltForest) TreeDrop(ctx context.Context, cid cidSDK.ID, treeID string) error {
|
||||||
|
_, span := tracing.StartSpanFromContext(ctx, "boltForest.TreeDrop",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("container_id", cid.EncodeToString()),
|
||||||
|
attribute.String("tree_id", treeID),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
t.modeMtx.RLock()
|
t.modeMtx.RLock()
|
||||||
defer t.modeMtx.RUnlock()
|
defer t.modeMtx.RUnlock()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package pilorama
|
package pilorama
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ func NewMemoryForest() ForestStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeMove implements the Forest interface.
|
// TreeMove implements the Forest interface.
|
||||||
func (f *memoryForest) TreeMove(d CIDDescriptor, treeID string, op *Move) (*Move, error) {
|
func (f *memoryForest) TreeMove(_ context.Context, d CIDDescriptor, treeID string, op *Move) (*Move, error) {
|
||||||
if !d.checkValid() {
|
if !d.checkValid() {
|
||||||
return nil, ErrInvalidCIDDescriptor
|
return nil, ErrInvalidCIDDescriptor
|
||||||
}
|
}
|
||||||
|
@ -48,7 +49,7 @@ func (f *memoryForest) TreeMove(d CIDDescriptor, treeID string, op *Move) (*Move
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeAddByPath implements the Forest interface.
|
// TreeAddByPath implements the Forest interface.
|
||||||
func (f *memoryForest) TreeAddByPath(d CIDDescriptor, treeID string, attr string, path []string, m []KeyValue) ([]Move, error) {
|
func (f *memoryForest) TreeAddByPath(_ context.Context, d CIDDescriptor, treeID string, attr string, path []string, m []KeyValue) ([]Move, error) {
|
||||||
if !d.checkValid() {
|
if !d.checkValid() {
|
||||||
return nil, ErrInvalidCIDDescriptor
|
return nil, ErrInvalidCIDDescriptor
|
||||||
}
|
}
|
||||||
|
@ -93,7 +94,7 @@ func (f *memoryForest) TreeAddByPath(d CIDDescriptor, treeID string, attr string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeApply implements the Forest interface.
|
// TreeApply implements the Forest interface.
|
||||||
func (f *memoryForest) TreeApply(cnr cid.ID, treeID string, op *Move, _ bool) error {
|
func (f *memoryForest) TreeApply(_ context.Context, cnr cid.ID, treeID string, op *Move, _ bool) error {
|
||||||
fullID := cnr.String() + "/" + treeID
|
fullID := cnr.String() + "/" + treeID
|
||||||
s, ok := f.treeMap[fullID]
|
s, ok := f.treeMap[fullID]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -119,7 +120,7 @@ func (f *memoryForest) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetByPath implements the Forest interface.
|
// TreeGetByPath implements the Forest interface.
|
||||||
func (f *memoryForest) TreeGetByPath(cid cid.ID, treeID string, attr string, path []string, latest bool) ([]Node, error) {
|
func (f *memoryForest) TreeGetByPath(_ context.Context, cid cid.ID, treeID string, attr string, path []string, latest bool) ([]Node, error) {
|
||||||
if !isAttributeInternal(attr) {
|
if !isAttributeInternal(attr) {
|
||||||
return nil, ErrNotPathAttribute
|
return nil, ErrNotPathAttribute
|
||||||
}
|
}
|
||||||
|
@ -134,7 +135,7 @@ func (f *memoryForest) TreeGetByPath(cid cid.ID, treeID string, attr string, pat
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetMeta implements the Forest interface.
|
// TreeGetMeta implements the Forest interface.
|
||||||
func (f *memoryForest) TreeGetMeta(cid cid.ID, treeID string, nodeID Node) (Meta, Node, error) {
|
func (f *memoryForest) TreeGetMeta(_ context.Context, cid cid.ID, treeID string, nodeID Node) (Meta, Node, error) {
|
||||||
fullID := cid.String() + "/" + treeID
|
fullID := cid.String() + "/" + treeID
|
||||||
s, ok := f.treeMap[fullID]
|
s, ok := f.treeMap[fullID]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -145,7 +146,7 @@ func (f *memoryForest) TreeGetMeta(cid cid.ID, treeID string, nodeID Node) (Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetChildren implements the Forest interface.
|
// TreeGetChildren implements the Forest interface.
|
||||||
func (f *memoryForest) TreeGetChildren(cid cid.ID, treeID string, nodeID Node) ([]uint64, error) {
|
func (f *memoryForest) TreeGetChildren(_ context.Context, cid cid.ID, treeID string, nodeID Node) ([]uint64, error) {
|
||||||
fullID := cid.String() + "/" + treeID
|
fullID := cid.String() + "/" + treeID
|
||||||
s, ok := f.treeMap[fullID]
|
s, ok := f.treeMap[fullID]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -163,7 +164,7 @@ func (f *memoryForest) TreeGetChildren(cid cid.ID, treeID string, nodeID Node) (
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetOpLog implements the pilorama.Forest interface.
|
// TreeGetOpLog implements the pilorama.Forest interface.
|
||||||
func (f *memoryForest) TreeGetOpLog(cid cid.ID, treeID string, height uint64) (Move, error) {
|
func (f *memoryForest) TreeGetOpLog(_ context.Context, cid cid.ID, treeID string, height uint64) (Move, error) {
|
||||||
fullID := cid.String() + "/" + treeID
|
fullID := cid.String() + "/" + treeID
|
||||||
s, ok := f.treeMap[fullID]
|
s, ok := f.treeMap[fullID]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -180,7 +181,7 @@ func (f *memoryForest) TreeGetOpLog(cid cid.ID, treeID string, height uint64) (M
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeDrop implements the pilorama.Forest interface.
|
// TreeDrop implements the pilorama.Forest interface.
|
||||||
func (f *memoryForest) TreeDrop(cid cid.ID, treeID string) error {
|
func (f *memoryForest) TreeDrop(_ context.Context, cid cid.ID, treeID string) error {
|
||||||
cidStr := cid.String()
|
cidStr := cid.String()
|
||||||
if treeID == "" {
|
if treeID == "" {
|
||||||
for k := range f.treeMap {
|
for k := range f.treeMap {
|
||||||
|
@ -200,7 +201,7 @@ func (f *memoryForest) TreeDrop(cid cid.ID, treeID string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeList implements the pilorama.Forest interface.
|
// TreeList implements the pilorama.Forest interface.
|
||||||
func (f *memoryForest) TreeList(cid cid.ID) ([]string, error) {
|
func (f *memoryForest) TreeList(_ context.Context, cid cid.ID) ([]string, error) {
|
||||||
var res []string
|
var res []string
|
||||||
cidStr := cid.EncodeToString()
|
cidStr := cid.EncodeToString()
|
||||||
|
|
||||||
|
@ -217,14 +218,14 @@ func (f *memoryForest) TreeList(cid cid.ID) ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeExists implements the pilorama.Forest interface.
|
// TreeExists implements the pilorama.Forest interface.
|
||||||
func (f *memoryForest) TreeExists(cid cid.ID, treeID string) (bool, error) {
|
func (f *memoryForest) TreeExists(_ context.Context, cid cid.ID, treeID string) (bool, error) {
|
||||||
fullID := cid.EncodeToString() + "/" + treeID
|
fullID := cid.EncodeToString() + "/" + treeID
|
||||||
_, ok := f.treeMap[fullID]
|
_, ok := f.treeMap[fullID]
|
||||||
return ok, nil
|
return ok, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeUpdateLastSyncHeight implements the pilorama.Forest interface.
|
// TreeUpdateLastSyncHeight implements the pilorama.Forest interface.
|
||||||
func (f *memoryForest) TreeUpdateLastSyncHeight(cid cid.ID, treeID string, height uint64) error {
|
func (f *memoryForest) TreeUpdateLastSyncHeight(_ context.Context, cid cid.ID, treeID string, height uint64) error {
|
||||||
fullID := cid.EncodeToString() + "/" + treeID
|
fullID := cid.EncodeToString() + "/" + treeID
|
||||||
t, ok := f.treeMap[fullID]
|
t, ok := f.treeMap[fullID]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -235,7 +236,7 @@ func (f *memoryForest) TreeUpdateLastSyncHeight(cid cid.ID, treeID string, heigh
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeLastSyncHeight implements the pilorama.Forest interface.
|
// TreeLastSyncHeight implements the pilorama.Forest interface.
|
||||||
func (f *memoryForest) TreeLastSyncHeight(cid cid.ID, treeID string) (uint64, error) {
|
func (f *memoryForest) TreeLastSyncHeight(_ context.Context, cid cid.ID, treeID string) (uint64, error) {
|
||||||
fullID := cid.EncodeToString() + "/" + treeID
|
fullID := cid.EncodeToString() + "/" + treeID
|
||||||
t, ok := f.treeMap[fullID]
|
t, ok := f.treeMap[fullID]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package pilorama
|
package pilorama
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
@ -49,7 +50,7 @@ var providers = []struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testMeta(t *testing.T, f Forest, cid cidSDK.ID, treeID string, nodeID, parentID Node, expected Meta) {
|
func testMeta(t *testing.T, f Forest, cid cidSDK.ID, treeID string, nodeID, parentID Node, expected Meta) {
|
||||||
actualMeta, actualParent, err := f.TreeGetMeta(cid, treeID, nodeID)
|
actualMeta, actualParent, err := f.TreeGetMeta(context.Background(), cid, treeID, nodeID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, parentID, actualParent)
|
require.Equal(t, parentID, actualParent)
|
||||||
require.Equal(t, expected, actualMeta)
|
require.Equal(t, expected, actualMeta)
|
||||||
|
@ -71,13 +72,13 @@ func testForestTreeMove(t *testing.T, s Forest) {
|
||||||
meta := []KeyValue{
|
meta := []KeyValue{
|
||||||
{Key: AttributeVersion, Value: []byte("XXX")},
|
{Key: AttributeVersion, Value: []byte("XXX")},
|
||||||
{Key: AttributeFilename, Value: []byte("file.txt")}}
|
{Key: AttributeFilename, Value: []byte("file.txt")}}
|
||||||
lm, err := s.TreeAddByPath(d, treeID, AttributeFilename, []string{"path", "to"}, meta)
|
lm, err := s.TreeAddByPath(context.Background(), d, treeID, AttributeFilename, []string{"path", "to"}, meta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 3, len(lm))
|
require.Equal(t, 3, len(lm))
|
||||||
|
|
||||||
nodeID := lm[2].Child
|
nodeID := lm[2].Child
|
||||||
t.Run("invalid descriptor", func(t *testing.T) {
|
t.Run("invalid descriptor", func(t *testing.T) {
|
||||||
_, err = s.TreeMove(CIDDescriptor{cid, 0, 0}, treeID, &Move{
|
_, err = s.TreeMove(context.Background(), CIDDescriptor{cid, 0, 0}, treeID, &Move{
|
||||||
Parent: lm[1].Child,
|
Parent: lm[1].Child,
|
||||||
Meta: Meta{Items: append(meta, KeyValue{Key: "NewKey", Value: []byte("NewValue")})},
|
Meta: Meta{Items: append(meta, KeyValue{Key: "NewKey", Value: []byte("NewValue")})},
|
||||||
Child: nodeID,
|
Child: nodeID,
|
||||||
|
@ -85,7 +86,7 @@ func testForestTreeMove(t *testing.T, s Forest) {
|
||||||
require.ErrorIs(t, err, ErrInvalidCIDDescriptor)
|
require.ErrorIs(t, err, ErrInvalidCIDDescriptor)
|
||||||
})
|
})
|
||||||
t.Run("same parent, update meta", func(t *testing.T) {
|
t.Run("same parent, update meta", func(t *testing.T) {
|
||||||
res, err := s.TreeMove(d, treeID, &Move{
|
res, err := s.TreeMove(context.Background(), d, treeID, &Move{
|
||||||
Parent: lm[1].Child,
|
Parent: lm[1].Child,
|
||||||
Meta: Meta{Items: append(meta, KeyValue{Key: "NewKey", Value: []byte("NewValue")})},
|
Meta: Meta{Items: append(meta, KeyValue{Key: "NewKey", Value: []byte("NewValue")})},
|
||||||
Child: nodeID,
|
Child: nodeID,
|
||||||
|
@ -93,12 +94,12 @@ func testForestTreeMove(t *testing.T, s Forest) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, res.Child, nodeID)
|
require.Equal(t, res.Child, nodeID)
|
||||||
|
|
||||||
nodes, err := s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"path", "to", "file.txt"}, false)
|
nodes, err := s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"path", "to", "file.txt"}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.ElementsMatch(t, []Node{nodeID}, nodes)
|
require.ElementsMatch(t, []Node{nodeID}, nodes)
|
||||||
})
|
})
|
||||||
t.Run("different parent", func(t *testing.T) {
|
t.Run("different parent", func(t *testing.T) {
|
||||||
res, err := s.TreeMove(d, treeID, &Move{
|
res, err := s.TreeMove(context.Background(), d, treeID, &Move{
|
||||||
Parent: RootID,
|
Parent: RootID,
|
||||||
Meta: Meta{Items: append(meta, KeyValue{Key: "NewKey", Value: []byte("NewValue")})},
|
Meta: Meta{Items: append(meta, KeyValue{Key: "NewKey", Value: []byte("NewValue")})},
|
||||||
Child: nodeID,
|
Child: nodeID,
|
||||||
|
@ -106,11 +107,11 @@ func testForestTreeMove(t *testing.T, s Forest) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, res.Child, nodeID)
|
require.Equal(t, res.Child, nodeID)
|
||||||
|
|
||||||
nodes, err := s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"path", "to", "file.txt"}, false)
|
nodes, err := s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"path", "to", "file.txt"}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, len(nodes) == 0)
|
require.True(t, len(nodes) == 0)
|
||||||
|
|
||||||
nodes, err = s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"file.txt"}, false)
|
nodes, err = s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"file.txt"}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.ElementsMatch(t, []Node{nodeID}, nodes)
|
require.ElementsMatch(t, []Node{nodeID}, nodes)
|
||||||
})
|
})
|
||||||
|
@ -130,7 +131,7 @@ func testForestTreeGetChildren(t *testing.T, s Forest) {
|
||||||
treeID := "version"
|
treeID := "version"
|
||||||
|
|
||||||
treeAdd := func(t *testing.T, child, parent Node) {
|
treeAdd := func(t *testing.T, child, parent Node) {
|
||||||
_, err := s.TreeMove(d, treeID, &Move{
|
_, err := s.TreeMove(context.Background(), d, treeID, &Move{
|
||||||
Parent: parent,
|
Parent: parent,
|
||||||
Child: child,
|
Child: child,
|
||||||
})
|
})
|
||||||
|
@ -152,7 +153,7 @@ func testForestTreeGetChildren(t *testing.T, s Forest) {
|
||||||
treeAdd(t, 7, 0)
|
treeAdd(t, 7, 0)
|
||||||
|
|
||||||
testGetChildren := func(t *testing.T, nodeID Node, expected []Node) {
|
testGetChildren := func(t *testing.T, nodeID Node, expected []Node) {
|
||||||
actual, err := s.TreeGetChildren(cid, treeID, nodeID)
|
actual, err := s.TreeGetChildren(context.Background(), cid, treeID, nodeID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.ElementsMatch(t, expected, actual)
|
require.ElementsMatch(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -168,7 +169,7 @@ func testForestTreeGetChildren(t *testing.T, s Forest) {
|
||||||
testGetChildren(t, 42, nil)
|
testGetChildren(t, 42, nil)
|
||||||
})
|
})
|
||||||
t.Run("missing tree", func(t *testing.T) {
|
t.Run("missing tree", func(t *testing.T) {
|
||||||
_, err := s.TreeGetChildren(cid, treeID+"123", 0)
|
_, err := s.TreeGetChildren(context.Background(), cid, treeID+"123", 0)
|
||||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -191,10 +192,10 @@ func testForestTreeDrop(t *testing.T, s Forest) {
|
||||||
cid := cids[0]
|
cid := cids[0]
|
||||||
|
|
||||||
t.Run("return nil if not found", func(t *testing.T) {
|
t.Run("return nil if not found", func(t *testing.T) {
|
||||||
require.ErrorIs(t, s.TreeDrop(cid, "123"), ErrTreeNotFound)
|
require.ErrorIs(t, s.TreeDrop(context.Background(), cid, "123"), ErrTreeNotFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
require.NoError(t, s.TreeDrop(cid, ""))
|
require.NoError(t, s.TreeDrop(context.Background(), cid, ""))
|
||||||
|
|
||||||
trees := []string{"tree1", "tree2"}
|
trees := []string{"tree1", "tree2"}
|
||||||
var descs [cidsSize]CIDDescriptor
|
var descs [cidsSize]CIDDescriptor
|
||||||
|
@ -203,39 +204,39 @@ func testForestTreeDrop(t *testing.T, s Forest) {
|
||||||
}
|
}
|
||||||
d := descs[0]
|
d := descs[0]
|
||||||
for i := range trees {
|
for i := range trees {
|
||||||
_, err := s.TreeAddByPath(d, trees[i], AttributeFilename, []string{"path"},
|
_, err := s.TreeAddByPath(context.Background(), d, trees[i], AttributeFilename, []string{"path"},
|
||||||
[]KeyValue{{Key: "TreeName", Value: []byte(trees[i])}})
|
[]KeyValue{{Key: "TreeName", Value: []byte(trees[i])}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.TreeDrop(cid, trees[0])
|
err := s.TreeDrop(context.Background(), cid, trees[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = s.TreeGetByPath(cid, trees[0], AttributeFilename, []string{"path"}, true)
|
_, err = s.TreeGetByPath(context.Background(), cid, trees[0], AttributeFilename, []string{"path"}, true)
|
||||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||||
|
|
||||||
_, err = s.TreeGetByPath(cid, trees[1], AttributeFilename, []string{"path"}, true)
|
_, err = s.TreeGetByPath(context.Background(), cid, trees[1], AttributeFilename, []string{"path"}, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for j := range descs {
|
for j := range descs {
|
||||||
for i := range trees {
|
for i := range trees {
|
||||||
_, err := s.TreeAddByPath(descs[j], trees[i], AttributeFilename, []string{"path"},
|
_, err := s.TreeAddByPath(context.Background(), descs[j], trees[i], AttributeFilename, []string{"path"},
|
||||||
[]KeyValue{{Key: "TreeName", Value: []byte(trees[i])}})
|
[]KeyValue{{Key: "TreeName", Value: []byte(trees[i])}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list, err := s.TreeList(cid)
|
list, err := s.TreeList(context.Background(), cid)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEmpty(t, list)
|
require.NotEmpty(t, list)
|
||||||
|
|
||||||
require.NoError(t, s.TreeDrop(cid, ""))
|
require.NoError(t, s.TreeDrop(context.Background(), cid, ""))
|
||||||
|
|
||||||
list, err = s.TreeList(cid)
|
list, err = s.TreeList(context.Background(), cid)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Empty(t, list)
|
require.Empty(t, list)
|
||||||
|
|
||||||
for j := 1; j < len(cids); j++ {
|
for j := 1; j < len(cids); j++ {
|
||||||
list, err = s.TreeList(cids[j])
|
list, err = s.TreeList(context.Background(), cids[j])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, len(list), len(trees))
|
require.Equal(t, len(list), len(trees))
|
||||||
}
|
}
|
||||||
|
@ -264,24 +265,24 @@ func testForestTreeAdd(t *testing.T, s Forest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("invalid descriptor", func(t *testing.T) {
|
t.Run("invalid descriptor", func(t *testing.T) {
|
||||||
_, err := s.TreeMove(CIDDescriptor{cid, 0, 0}, treeID, m)
|
_, err := s.TreeMove(context.Background(), CIDDescriptor{cid, 0, 0}, treeID, m)
|
||||||
require.ErrorIs(t, err, ErrInvalidCIDDescriptor)
|
require.ErrorIs(t, err, ErrInvalidCIDDescriptor)
|
||||||
})
|
})
|
||||||
|
|
||||||
lm, err := s.TreeMove(d, treeID, m)
|
lm, err := s.TreeMove(context.Background(), d, treeID, m)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testMeta(t, s, cid, treeID, lm.Child, lm.Parent, Meta{Time: lm.Time, Items: meta})
|
testMeta(t, s, cid, treeID, lm.Child, lm.Parent, Meta{Time: lm.Time, Items: meta})
|
||||||
|
|
||||||
nodes, err := s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"file.txt"}, false)
|
nodes, err := s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"file.txt"}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.ElementsMatch(t, []Node{lm.Child}, nodes)
|
require.ElementsMatch(t, []Node{lm.Child}, nodes)
|
||||||
|
|
||||||
t.Run("other trees are unaffected", func(t *testing.T) {
|
t.Run("other trees are unaffected", func(t *testing.T) {
|
||||||
_, err := s.TreeGetByPath(cid, treeID+"123", AttributeFilename, []string{"file.txt"}, false)
|
_, err := s.TreeGetByPath(context.Background(), cid, treeID+"123", AttributeFilename, []string{"file.txt"}, false)
|
||||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||||
|
|
||||||
_, _, err = s.TreeGetMeta(cid, treeID+"123", 0)
|
_, _, err = s.TreeGetMeta(context.Background(), cid, treeID+"123", 0)
|
||||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -304,15 +305,15 @@ func testForestTreeAddByPath(t *testing.T, s Forest) {
|
||||||
{Key: AttributeFilename, Value: []byte("file.txt")}}
|
{Key: AttributeFilename, Value: []byte("file.txt")}}
|
||||||
|
|
||||||
t.Run("invalid descriptor", func(t *testing.T) {
|
t.Run("invalid descriptor", func(t *testing.T) {
|
||||||
_, err := s.TreeAddByPath(CIDDescriptor{cid, 0, 0}, treeID, AttributeFilename, []string{"yyy"}, meta)
|
_, err := s.TreeAddByPath(context.Background(), CIDDescriptor{cid, 0, 0}, treeID, AttributeFilename, []string{"yyy"}, meta)
|
||||||
require.ErrorIs(t, err, ErrInvalidCIDDescriptor)
|
require.ErrorIs(t, err, ErrInvalidCIDDescriptor)
|
||||||
})
|
})
|
||||||
t.Run("invalid attribute", func(t *testing.T) {
|
t.Run("invalid attribute", func(t *testing.T) {
|
||||||
_, err := s.TreeAddByPath(d, treeID, AttributeVersion, []string{"yyy"}, meta)
|
_, err := s.TreeAddByPath(context.Background(), d, treeID, AttributeVersion, []string{"yyy"}, meta)
|
||||||
require.ErrorIs(t, err, ErrNotPathAttribute)
|
require.ErrorIs(t, err, ErrNotPathAttribute)
|
||||||
})
|
})
|
||||||
|
|
||||||
lm, err := s.TreeAddByPath(d, treeID, AttributeFilename, []string{"path", "to"}, meta)
|
lm, err := s.TreeAddByPath(context.Background(), d, treeID, AttributeFilename, []string{"path", "to"}, meta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 3, len(lm))
|
require.Equal(t, 3, len(lm))
|
||||||
testMeta(t, s, cid, treeID, lm[0].Child, lm[0].Parent, Meta{Time: lm[0].Time, Items: []KeyValue{{AttributeFilename, []byte("path")}}})
|
testMeta(t, s, cid, treeID, lm[0].Child, lm[0].Parent, Meta{Time: lm[0].Time, Items: []KeyValue{{AttributeFilename, []byte("path")}}})
|
||||||
|
@ -322,7 +323,7 @@ func testForestTreeAddByPath(t *testing.T, s Forest) {
|
||||||
testMeta(t, s, cid, treeID, firstID, lm[2].Parent, Meta{Time: lm[2].Time, Items: meta})
|
testMeta(t, s, cid, treeID, firstID, lm[2].Parent, Meta{Time: lm[2].Time, Items: meta})
|
||||||
|
|
||||||
meta[0].Value = []byte("YYY")
|
meta[0].Value = []byte("YYY")
|
||||||
lm, err = s.TreeAddByPath(d, treeID, AttributeFilename, []string{"path", "to"}, meta)
|
lm, err = s.TreeAddByPath(context.Background(), d, treeID, AttributeFilename, []string{"path", "to"}, meta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(lm))
|
require.Equal(t, 1, len(lm))
|
||||||
|
|
||||||
|
@ -331,19 +332,19 @@ func testForestTreeAddByPath(t *testing.T, s Forest) {
|
||||||
|
|
||||||
t.Run("get versions", func(t *testing.T) {
|
t.Run("get versions", func(t *testing.T) {
|
||||||
// All versions.
|
// All versions.
|
||||||
nodes, err := s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"path", "to", "file.txt"}, false)
|
nodes, err := s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"path", "to", "file.txt"}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.ElementsMatch(t, []Node{firstID, secondID}, nodes)
|
require.ElementsMatch(t, []Node{firstID, secondID}, nodes)
|
||||||
|
|
||||||
// Latest version.
|
// Latest version.
|
||||||
nodes, err = s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"path", "to", "file.txt"}, true)
|
nodes, err = s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"path", "to", "file.txt"}, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []Node{secondID}, nodes)
|
require.Equal(t, []Node{secondID}, nodes)
|
||||||
})
|
})
|
||||||
|
|
||||||
meta[0].Value = []byte("ZZZ")
|
meta[0].Value = []byte("ZZZ")
|
||||||
meta[1].Value = []byte("cat.jpg")
|
meta[1].Value = []byte("cat.jpg")
|
||||||
lm, err = s.TreeAddByPath(d, treeID, AttributeFilename, []string{"path", "dir"}, meta)
|
lm, err = s.TreeAddByPath(context.Background(), d, treeID, AttributeFilename, []string{"path", "dir"}, meta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 2, len(lm))
|
require.Equal(t, 2, len(lm))
|
||||||
testMeta(t, s, cid, treeID, lm[0].Child, lm[0].Parent, Meta{Time: lm[0].Time, Items: []KeyValue{{AttributeFilename, []byte("dir")}}})
|
testMeta(t, s, cid, treeID, lm[0].Child, lm[0].Parent, Meta{Time: lm[0].Time, Items: []KeyValue{{AttributeFilename, []byte("dir")}}})
|
||||||
|
@ -352,7 +353,7 @@ func testForestTreeAddByPath(t *testing.T, s Forest) {
|
||||||
t.Run("create internal nodes", func(t *testing.T) {
|
t.Run("create internal nodes", func(t *testing.T) {
|
||||||
meta[0].Value = []byte("SomeValue")
|
meta[0].Value = []byte("SomeValue")
|
||||||
meta[1].Value = []byte("another")
|
meta[1].Value = []byte("another")
|
||||||
lm, err = s.TreeAddByPath(d, treeID, AttributeFilename, []string{"path"}, meta)
|
lm, err = s.TreeAddByPath(context.Background(), d, treeID, AttributeFilename, []string{"path"}, meta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(lm))
|
require.Equal(t, 1, len(lm))
|
||||||
|
|
||||||
|
@ -360,7 +361,7 @@ func testForestTreeAddByPath(t *testing.T, s Forest) {
|
||||||
|
|
||||||
meta[0].Value = []byte("Leaf")
|
meta[0].Value = []byte("Leaf")
|
||||||
meta[1].Value = []byte("file.txt")
|
meta[1].Value = []byte("file.txt")
|
||||||
lm, err = s.TreeAddByPath(d, treeID, AttributeFilename, []string{"path", "another"}, meta)
|
lm, err = s.TreeAddByPath(context.Background(), d, treeID, AttributeFilename, []string{"path", "another"}, meta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 2, len(lm))
|
require.Equal(t, 2, len(lm))
|
||||||
|
|
||||||
|
@ -375,12 +376,12 @@ func testForestTreeAddByPath(t *testing.T, s Forest) {
|
||||||
{AttributeFilename, []byte("another")}}})
|
{AttributeFilename, []byte("another")}}})
|
||||||
|
|
||||||
t.Run("get by path", func(t *testing.T) {
|
t.Run("get by path", func(t *testing.T) {
|
||||||
nodes, err := s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"path", "another"}, false)
|
nodes, err := s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"path", "another"}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 2, len(nodes))
|
require.Equal(t, 2, len(nodes))
|
||||||
require.ElementsMatch(t, []Node{lm[0].Child, oldMove.Child}, nodes)
|
require.ElementsMatch(t, []Node{lm[0].Child, oldMove.Child}, nodes)
|
||||||
|
|
||||||
nodes, err = s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"path", "another", "file.txt"}, false)
|
nodes, err = s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"path", "another", "file.txt"}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(nodes))
|
require.Equal(t, 1, len(nodes))
|
||||||
require.Equal(t, lm[1].Child, nodes[0])
|
require.Equal(t, lm[1].Child, nodes[0])
|
||||||
|
@ -391,11 +392,11 @@ func testForestTreeAddByPath(t *testing.T, s Forest) {
|
||||||
meta := []KeyValue{
|
meta := []KeyValue{
|
||||||
{Key: AttributeVersion, Value: []byte("XXX")},
|
{Key: AttributeVersion, Value: []byte("XXX")},
|
||||||
{Key: AttributeFilename, Value: []byte{}}}
|
{Key: AttributeFilename, Value: []byte{}}}
|
||||||
lm, err := s.TreeAddByPath(d, treeID, AttributeFilename, []string{"path", "to"}, meta)
|
lm, err := s.TreeAddByPath(context.Background(), d, treeID, AttributeFilename, []string{"path", "to"}, meta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(lm))
|
require.Equal(t, 1, len(lm))
|
||||||
|
|
||||||
nodes, err := s.TreeGetByPath(d.CID, treeID, AttributeFilename, []string{"path", "to", ""}, false)
|
nodes, err := s.TreeGetByPath(context.Background(), d.CID, treeID, AttributeFilename, []string{"path", "to", ""}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(nodes))
|
require.Equal(t, 1, len(nodes))
|
||||||
require.Equal(t, lm[0].Child, nodes[0])
|
require.Equal(t, lm[0].Child, nodes[0])
|
||||||
|
@ -415,7 +416,7 @@ func testForestTreeApply(t *testing.T, constructor func(t testing.TB, _ ...Optio
|
||||||
treeID := "version"
|
treeID := "version"
|
||||||
|
|
||||||
testApply := func(t *testing.T, s Forest, child, parent Node, meta Meta) {
|
testApply := func(t *testing.T, s Forest, child, parent Node, meta Meta) {
|
||||||
require.NoError(t, s.TreeApply(cid, treeID, &Move{
|
require.NoError(t, s.TreeApply(context.Background(), cid, treeID, &Move{
|
||||||
Child: child,
|
Child: child,
|
||||||
Parent: parent,
|
Parent: parent,
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
|
@ -475,16 +476,16 @@ func testForestTreeGetOpLog(t *testing.T, constructor func(t testing.TB, _ ...Op
|
||||||
s := constructor(t)
|
s := constructor(t)
|
||||||
|
|
||||||
t.Run("empty log, no panic", func(t *testing.T) {
|
t.Run("empty log, no panic", func(t *testing.T) {
|
||||||
_, err := s.TreeGetOpLog(cid, treeID, 0)
|
_, err := s.TreeGetOpLog(context.Background(), cid, treeID, 0)
|
||||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
for i := range logs {
|
for i := range logs {
|
||||||
require.NoError(t, s.TreeApply(cid, treeID, &logs[i], false))
|
require.NoError(t, s.TreeApply(context.Background(), cid, treeID, &logs[i], false))
|
||||||
}
|
}
|
||||||
|
|
||||||
testGetOpLog := func(t *testing.T, height uint64, m Move) {
|
testGetOpLog := func(t *testing.T, height uint64, m Move) {
|
||||||
lm, err := s.TreeGetOpLog(cid, treeID, height)
|
lm, err := s.TreeGetOpLog(context.Background(), cid, treeID, height)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, m, lm)
|
require.Equal(t, m, lm)
|
||||||
}
|
}
|
||||||
|
@ -498,7 +499,7 @@ func testForestTreeGetOpLog(t *testing.T, constructor func(t testing.TB, _ ...Op
|
||||||
testGetOpLog(t, 261, Move{})
|
testGetOpLog(t, 261, Move{})
|
||||||
})
|
})
|
||||||
t.Run("missing tree", func(t *testing.T) {
|
t.Run("missing tree", func(t *testing.T) {
|
||||||
_, err := s.TreeGetOpLog(cid, treeID+"123", 4)
|
_, err := s.TreeGetOpLog(context.Background(), cid, treeID+"123", 4)
|
||||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -515,7 +516,7 @@ func testForestTreeExists(t *testing.T, constructor func(t testing.TB, opts ...O
|
||||||
s := constructor(t)
|
s := constructor(t)
|
||||||
|
|
||||||
checkExists := func(t *testing.T, expected bool, cid cidSDK.ID, treeID string) {
|
checkExists := func(t *testing.T, expected bool, cid cidSDK.ID, treeID string) {
|
||||||
actual, err := s.TreeExists(cid, treeID)
|
actual, err := s.TreeExists(context.Background(), cid, treeID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -527,13 +528,13 @@ func testForestTreeExists(t *testing.T, constructor func(t testing.TB, opts ...O
|
||||||
checkExists(t, false, cid, treeID)
|
checkExists(t, false, cid, treeID)
|
||||||
})
|
})
|
||||||
|
|
||||||
require.NoError(t, s.TreeApply(cid, treeID, &Move{Parent: 0, Child: 1}, false))
|
require.NoError(t, s.TreeApply(context.Background(), cid, treeID, &Move{Parent: 0, Child: 1}, false))
|
||||||
checkExists(t, true, cid, treeID)
|
checkExists(t, true, cid, treeID)
|
||||||
checkExists(t, false, cidtest.ID(), treeID) // different CID, same tree
|
checkExists(t, false, cidtest.ID(), treeID) // different CID, same tree
|
||||||
checkExists(t, false, cid, "another tree") // same CID, different tree
|
checkExists(t, false, cid, "another tree") // same CID, different tree
|
||||||
|
|
||||||
t.Run("can be removed", func(t *testing.T) {
|
t.Run("can be removed", func(t *testing.T) {
|
||||||
require.NoError(t, s.TreeDrop(cid, treeID))
|
require.NoError(t, s.TreeDrop(context.Background(), cid, treeID))
|
||||||
checkExists(t, false, cid, treeID)
|
checkExists(t, false, cid, treeID)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -563,11 +564,11 @@ func TestApplyTricky1(t *testing.T) {
|
||||||
t.Run(providers[i].name, func(t *testing.T) {
|
t.Run(providers[i].name, func(t *testing.T) {
|
||||||
s := providers[i].construct(t)
|
s := providers[i].construct(t)
|
||||||
for i := range ops {
|
for i := range ops {
|
||||||
require.NoError(t, s.TreeApply(cid, treeID, &ops[i], false))
|
require.NoError(t, s.TreeApply(context.Background(), cid, treeID, &ops[i], false))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range expected {
|
for i := range expected {
|
||||||
_, parent, err := s.TreeGetMeta(cid, treeID, expected[i].child)
|
_, parent, err := s.TreeGetMeta(context.Background(), cid, treeID, expected[i].child)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expected[i].parent, parent)
|
require.Equal(t, expected[i].parent, parent)
|
||||||
}
|
}
|
||||||
|
@ -624,11 +625,11 @@ func TestApplyTricky2(t *testing.T) {
|
||||||
t.Run(providers[i].name, func(t *testing.T) {
|
t.Run(providers[i].name, func(t *testing.T) {
|
||||||
s := providers[i].construct(t)
|
s := providers[i].construct(t)
|
||||||
for i := range ops {
|
for i := range ops {
|
||||||
require.NoError(t, s.TreeApply(cid, treeID, &ops[i], false))
|
require.NoError(t, s.TreeApply(context.Background(), cid, treeID, &ops[i], false))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range expected {
|
for i := range expected {
|
||||||
_, parent, err := s.TreeGetMeta(cid, treeID, expected[i].child)
|
_, parent, err := s.TreeGetMeta(context.Background(), cid, treeID, expected[i].child)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expected[i].parent, parent)
|
require.Equal(t, expected[i].parent, parent)
|
||||||
}
|
}
|
||||||
|
@ -697,9 +698,9 @@ func prepareRandomTree(nodeCount, opCount int) []Move {
|
||||||
|
|
||||||
func compareForests(t *testing.T, expected, actual Forest, cid cidSDK.ID, treeID string, nodeCount int) {
|
func compareForests(t *testing.T, expected, actual Forest, cid cidSDK.ID, treeID string, nodeCount int) {
|
||||||
for i := uint64(0); i < uint64(nodeCount); i++ {
|
for i := uint64(0); i < uint64(nodeCount); i++ {
|
||||||
expectedMeta, expectedParent, err := expected.TreeGetMeta(cid, treeID, i)
|
expectedMeta, expectedParent, err := expected.TreeGetMeta(context.Background(), cid, treeID, i)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
actualMeta, actualParent, err := actual.TreeGetMeta(cid, treeID, i)
|
actualMeta, actualParent, err := actual.TreeGetMeta(context.Background(), cid, treeID, i)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expectedParent, actualParent, "node id: %d", i)
|
require.Equal(t, expectedParent, actualParent, "node id: %d", i)
|
||||||
require.Equal(t, expectedMeta, actualMeta, "node id: %d", i)
|
require.Equal(t, expectedMeta, actualMeta, "node id: %d", i)
|
||||||
|
@ -738,7 +739,7 @@ func testForestTreeParallelApply(t *testing.T, constructor func(t testing.TB, _
|
||||||
|
|
||||||
expected := constructor(t)
|
expected := constructor(t)
|
||||||
for i := range ops {
|
for i := range ops {
|
||||||
require.NoError(t, expected.TreeApply(cid, treeID, &ops[i], false))
|
require.NoError(t, expected.TreeApply(context.Background(), cid, treeID, &ops[i], false))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < iterCount; i++ {
|
for i := 0; i < iterCount; i++ {
|
||||||
|
@ -753,7 +754,7 @@ func testForestTreeParallelApply(t *testing.T, constructor func(t testing.TB, _
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for op := range ch {
|
for op := range ch {
|
||||||
require.NoError(t, actual.TreeApply(cid, treeID, op, false))
|
require.NoError(t, actual.TreeApply(context.Background(), cid, treeID, op, false))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -783,7 +784,7 @@ func testForestTreeApplyRandom(t *testing.T, constructor func(t testing.TB, _ ..
|
||||||
|
|
||||||
expected := constructor(t)
|
expected := constructor(t)
|
||||||
for i := range ops {
|
for i := range ops {
|
||||||
require.NoError(t, expected.TreeApply(cid, treeID, &ops[i], false))
|
require.NoError(t, expected.TreeApply(context.Background(), cid, treeID, &ops[i], false))
|
||||||
}
|
}
|
||||||
|
|
||||||
const iterCount = 200
|
const iterCount = 200
|
||||||
|
@ -793,7 +794,7 @@ func testForestTreeApplyRandom(t *testing.T, constructor func(t testing.TB, _ ..
|
||||||
|
|
||||||
actual := constructor(t)
|
actual := constructor(t)
|
||||||
for i := range ops {
|
for i := range ops {
|
||||||
require.NoError(t, actual.TreeApply(cid, treeID, &ops[i], false))
|
require.NoError(t, actual.TreeApply(context.Background(), cid, treeID, &ops[i], false))
|
||||||
}
|
}
|
||||||
compareForests(t, expected, actual, cid, treeID, nodeCount)
|
compareForests(t, expected, actual, cid, treeID, nodeCount)
|
||||||
}
|
}
|
||||||
|
@ -886,7 +887,7 @@ func benchmarkApply(b *testing.B, s Forest, genFunc func(int) []Move) {
|
||||||
b.SetParallelism(10)
|
b.SetParallelism(10)
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
if err := s.TreeApply(cid, treeID, &ops[<-ch], false); err != nil {
|
if err := s.TreeApply(context.Background(), cid, treeID, &ops[<-ch], false); err != nil {
|
||||||
b.Fatalf("error in `Apply`: %v", err)
|
b.Fatalf("error in `Apply`: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -929,27 +930,27 @@ func testTreeGetByPath(t *testing.T, s Forest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("invalid attribute", func(t *testing.T) {
|
t.Run("invalid attribute", func(t *testing.T) {
|
||||||
_, err := s.TreeGetByPath(cid, treeID, AttributeVersion, []string{"", "TTT"}, false)
|
_, err := s.TreeGetByPath(context.Background(), cid, treeID, AttributeVersion, []string{"", "TTT"}, false)
|
||||||
require.ErrorIs(t, err, ErrNotPathAttribute)
|
require.ErrorIs(t, err, ErrNotPathAttribute)
|
||||||
})
|
})
|
||||||
|
|
||||||
nodes, err := s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"b", "cat1.jpg"}, false)
|
nodes, err := s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"b", "cat1.jpg"}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []Node{4, 5}, nodes)
|
require.Equal(t, []Node{4, 5}, nodes)
|
||||||
|
|
||||||
nodes, err = s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"a", "cat1.jpg"}, false)
|
nodes, err = s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"a", "cat1.jpg"}, false)
|
||||||
require.Equal(t, []Node{3}, nodes)
|
require.Equal(t, []Node{3}, nodes)
|
||||||
|
|
||||||
t.Run("missing child", func(t *testing.T) {
|
t.Run("missing child", func(t *testing.T) {
|
||||||
nodes, err = s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"a", "cat3.jpg"}, false)
|
nodes, err = s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"a", "cat3.jpg"}, false)
|
||||||
require.True(t, len(nodes) == 0)
|
require.True(t, len(nodes) == 0)
|
||||||
})
|
})
|
||||||
t.Run("missing parent", func(t *testing.T) {
|
t.Run("missing parent", func(t *testing.T) {
|
||||||
nodes, err = s.TreeGetByPath(cid, treeID, AttributeFilename, []string{"xyz", "cat1.jpg"}, false)
|
nodes, err = s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, []string{"xyz", "cat1.jpg"}, false)
|
||||||
require.True(t, len(nodes) == 0)
|
require.True(t, len(nodes) == 0)
|
||||||
})
|
})
|
||||||
t.Run("empty path", func(t *testing.T) {
|
t.Run("empty path", func(t *testing.T) {
|
||||||
nodes, err = s.TreeGetByPath(cid, treeID, AttributeFilename, nil, false)
|
nodes, err = s.TreeGetByPath(context.Background(), cid, treeID, AttributeFilename, nil, false)
|
||||||
require.True(t, len(nodes) == 0)
|
require.True(t, len(nodes) == 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -961,7 +962,7 @@ func testMove(t *testing.T, s Forest, ts int, node, parent Node, cid cidSDK.ID,
|
||||||
items = append(items, KeyValue{AttributeVersion, []byte(version)})
|
items = append(items, KeyValue{AttributeVersion, []byte(version)})
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, s.TreeApply(cid, treeID, &Move{
|
require.NoError(t, s.TreeApply(context.Background(), cid, treeID, &Move{
|
||||||
Parent: parent,
|
Parent: parent,
|
||||||
Child: node,
|
Child: node,
|
||||||
Meta: Meta{
|
Meta: Meta{
|
||||||
|
@ -1000,7 +1001,7 @@ func testTreeGetTrees(t *testing.T, s Forest) {
|
||||||
d.CID = cid
|
d.CID = cid
|
||||||
|
|
||||||
for _, treeID := range treeIDs[cid] {
|
for _, treeID := range treeIDs[cid] {
|
||||||
_, err := s.TreeAddByPath(d, treeID, objectSDK.AttributeFileName, []string{"path"}, nil)
|
_, err := s.TreeAddByPath(context.Background(), d, treeID, objectSDK.AttributeFileName, []string{"path"}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1008,7 +1009,7 @@ func testTreeGetTrees(t *testing.T, s Forest) {
|
||||||
for _, cid := range cids {
|
for _, cid := range cids {
|
||||||
d.CID = cid
|
d.CID = cid
|
||||||
|
|
||||||
trees, err := s.TreeList(cid)
|
trees, err := s.TreeList(context.Background(), cid)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.ElementsMatch(t, treeIDs[cid], trees)
|
require.ElementsMatch(t, treeIDs[cid], trees)
|
||||||
|
@ -1028,38 +1029,38 @@ func testTreeLastSyncHeight(t *testing.T, f Forest) {
|
||||||
treeID := "someTree"
|
treeID := "someTree"
|
||||||
|
|
||||||
t.Run("ErrNotFound if no log operations are stored for a tree", func(t *testing.T) {
|
t.Run("ErrNotFound if no log operations are stored for a tree", func(t *testing.T) {
|
||||||
_, err := f.TreeLastSyncHeight(cnr, treeID)
|
_, err := f.TreeLastSyncHeight(context.Background(), cnr, treeID)
|
||||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||||
|
|
||||||
err = f.TreeUpdateLastSyncHeight(cnr, treeID, 1)
|
err = f.TreeUpdateLastSyncHeight(context.Background(), cnr, treeID, 1)
|
||||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := f.TreeMove(CIDDescriptor{CID: cnr, Size: 1}, treeID, &Move{
|
_, err := f.TreeMove(context.Background(), CIDDescriptor{CID: cnr, Size: 1}, treeID, &Move{
|
||||||
Parent: RootID,
|
Parent: RootID,
|
||||||
Child: 1,
|
Child: 1,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
h, err := f.TreeLastSyncHeight(cnr, treeID)
|
h, err := f.TreeLastSyncHeight(context.Background(), cnr, treeID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.EqualValues(t, 0, h)
|
require.EqualValues(t, 0, h)
|
||||||
|
|
||||||
t.Run("separate storages for separate containers", func(t *testing.T) {
|
t.Run("separate storages for separate containers", func(t *testing.T) {
|
||||||
_, err := f.TreeLastSyncHeight(cidtest.ID(), treeID)
|
_, err := f.TreeLastSyncHeight(context.Background(), cidtest.ID(), treeID)
|
||||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
require.NoError(t, f.TreeUpdateLastSyncHeight(cnr, treeID, 10))
|
require.NoError(t, f.TreeUpdateLastSyncHeight(context.Background(), cnr, treeID, 10))
|
||||||
|
|
||||||
h, err = f.TreeLastSyncHeight(cnr, treeID)
|
h, err = f.TreeLastSyncHeight(context.Background(), cnr, treeID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.EqualValues(t, 10, h)
|
require.EqualValues(t, 10, h)
|
||||||
|
|
||||||
t.Run("removed correctly", func(t *testing.T) {
|
t.Run("removed correctly", func(t *testing.T) {
|
||||||
require.NoError(t, f.TreeDrop(cnr, treeID))
|
require.NoError(t, f.TreeDrop(context.Background(), cnr, treeID))
|
||||||
|
|
||||||
_, err := f.TreeLastSyncHeight(cnr, treeID)
|
_, err := f.TreeLastSyncHeight(context.Background(), cnr, treeID)
|
||||||
require.ErrorIs(t, err, ErrTreeNotFound)
|
require.ErrorIs(t, err, ErrTreeNotFound)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package pilorama
|
package pilorama
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
||||||
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
@ -11,43 +13,43 @@ type Forest interface {
|
||||||
// TreeMove moves node in the tree.
|
// TreeMove moves node in the tree.
|
||||||
// If the parent of the move operation is TrashID, the node is removed.
|
// If the parent of the move operation is TrashID, the node is removed.
|
||||||
// If the child of the move operation is RootID, new ID is generated and added to a tree.
|
// If the child of the move operation is RootID, new ID is generated and added to a tree.
|
||||||
TreeMove(d CIDDescriptor, treeID string, m *Move) (*Move, error)
|
TreeMove(ctx context.Context, d CIDDescriptor, treeID string, m *Move) (*Move, error)
|
||||||
// TreeAddByPath adds new node in the tree using provided path.
|
// TreeAddByPath adds new node in the tree using provided path.
|
||||||
// The path is constructed by descending from the root using the values of the attr in meta.
|
// The path is constructed by descending from the root using the values of the attr in meta.
|
||||||
// Internal nodes in path should have exactly one attribute, otherwise a new node is created.
|
// Internal nodes in path should have exactly one attribute, otherwise a new node is created.
|
||||||
TreeAddByPath(d CIDDescriptor, treeID string, attr string, path []string, meta []KeyValue) ([]Move, error)
|
TreeAddByPath(ctx context.Context, d CIDDescriptor, treeID string, attr string, path []string, meta []KeyValue) ([]Move, error)
|
||||||
// TreeApply applies replicated operation from another node.
|
// TreeApply applies replicated operation from another node.
|
||||||
// If background is true, TreeApply will first check whether an operation exists.
|
// If background is true, TreeApply will first check whether an operation exists.
|
||||||
TreeApply(cnr cidSDK.ID, treeID string, m *Move, backgroundSync bool) error
|
TreeApply(ctx context.Context, cnr cidSDK.ID, treeID string, m *Move, backgroundSync bool) error
|
||||||
// TreeGetByPath returns all nodes corresponding to the path.
|
// TreeGetByPath returns all nodes corresponding to the path.
|
||||||
// The path is constructed by descending from the root using the values of the
|
// The path is constructed by descending from the root using the values of the
|
||||||
// AttributeFilename in meta.
|
// AttributeFilename in meta.
|
||||||
// The last argument determines whether only the node with the latest timestamp is returned.
|
// The last argument determines whether only the node with the latest timestamp is returned.
|
||||||
// Should return ErrTreeNotFound if the tree is not found, and empty result if the path is not in the tree.
|
// Should return ErrTreeNotFound if the tree is not found, and empty result if the path is not in the tree.
|
||||||
TreeGetByPath(cid cidSDK.ID, treeID string, attr string, path []string, latest bool) ([]Node, error)
|
TreeGetByPath(ctx context.Context, cid cidSDK.ID, treeID string, attr string, path []string, latest bool) ([]Node, error)
|
||||||
// TreeGetMeta returns meta information of the node with the specified ID.
|
// TreeGetMeta returns meta information of the node with the specified ID.
|
||||||
// Should return ErrTreeNotFound if the tree is not found, and empty result if the node is not in the tree.
|
// Should return ErrTreeNotFound if the tree is not found, and empty result if the node is not in the tree.
|
||||||
TreeGetMeta(cid cidSDK.ID, treeID string, nodeID Node) (Meta, Node, error)
|
TreeGetMeta(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node) (Meta, Node, error)
|
||||||
// TreeGetChildren returns children of the node with the specified ID. The order is arbitrary.
|
// TreeGetChildren returns children of the node with the specified ID. The order is arbitrary.
|
||||||
// Should return ErrTreeNotFound if the tree is not found, and empty result if the node is not in the tree.
|
// Should return ErrTreeNotFound if the tree is not found, and empty result if the node is not in the tree.
|
||||||
TreeGetChildren(cid cidSDK.ID, treeID string, nodeID Node) ([]uint64, error)
|
TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node) ([]uint64, error)
|
||||||
// TreeGetOpLog returns first log operation stored at or above the height.
|
// TreeGetOpLog returns first log operation stored at or above the height.
|
||||||
// In case no such operation is found, empty Move and nil error should be returned.
|
// In case no such operation is found, empty Move and nil error should be returned.
|
||||||
TreeGetOpLog(cid cidSDK.ID, treeID string, height uint64) (Move, error)
|
TreeGetOpLog(ctx context.Context, cid cidSDK.ID, treeID string, height uint64) (Move, error)
|
||||||
// TreeDrop drops a tree from the database.
|
// TreeDrop drops a tree from the database.
|
||||||
// If the tree is not found, ErrTreeNotFound should be returned.
|
// If the tree is not found, ErrTreeNotFound should be returned.
|
||||||
// In case of empty treeID drops all trees related to container.
|
// In case of empty treeID drops all trees related to container.
|
||||||
TreeDrop(cid cidSDK.ID, treeID string) error
|
TreeDrop(ctx context.Context, cid cidSDK.ID, treeID string) error
|
||||||
// TreeList returns all the tree IDs that have been added to the
|
// TreeList returns all the tree IDs that have been added to the
|
||||||
// passed container ID. Nil slice should be returned if no tree found.
|
// passed container ID. Nil slice should be returned if no tree found.
|
||||||
TreeList(cid cidSDK.ID) ([]string, error)
|
TreeList(ctx context.Context, cid cidSDK.ID) ([]string, error)
|
||||||
// TreeExists checks if a tree exists locally.
|
// TreeExists checks if a tree exists locally.
|
||||||
// If the tree is not found, false and a nil error should be returned.
|
// If the tree is not found, false and a nil error should be returned.
|
||||||
TreeExists(cid cidSDK.ID, treeID string) (bool, error)
|
TreeExists(ctx context.Context, cid cidSDK.ID, treeID string) (bool, error)
|
||||||
// TreeUpdateLastSyncHeight updates last log height synchronized with _all_ container nodes.
|
// TreeUpdateLastSyncHeight updates last log height synchronized with _all_ container nodes.
|
||||||
TreeUpdateLastSyncHeight(cid cidSDK.ID, treeID string, height uint64) error
|
TreeUpdateLastSyncHeight(ctx context.Context, cid cidSDK.ID, treeID string, height uint64) error
|
||||||
// TreeLastSyncHeight returns last log height synchronized with _all_ container nodes.
|
// TreeLastSyncHeight returns last log height synchronized with _all_ container nodes.
|
||||||
TreeLastSyncHeight(cid cidSDK.ID, treeID string) (uint64, error)
|
TreeLastSyncHeight(ctx context.Context, cid cidSDK.ID, treeID string) (uint64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ForestStorage interface {
|
type ForestStorage interface {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor"
|
||||||
|
@ -80,7 +81,10 @@ func (s *Shard) Open() error {
|
||||||
type metabaseSynchronizer Shard
|
type metabaseSynchronizer Shard
|
||||||
|
|
||||||
func (x *metabaseSynchronizer) Init() error {
|
func (x *metabaseSynchronizer) Init() error {
|
||||||
return (*Shard)(x).refillMetabase()
|
ctx, span := tracing.StartSpanFromContext(context.TODO(), "metabaseSynchronizer.Init")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
return (*Shard)(x).refillMetabase(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes all Shard's components.
|
// Init initializes all Shard's components.
|
||||||
|
@ -158,7 +162,7 @@ func (s *Shard) Init(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Shard) refillMetabase() error {
|
func (s *Shard) refillMetabase(ctx context.Context) error {
|
||||||
err := s.metaBase.Reset()
|
err := s.metaBase.Reset()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not reset metabase: %w", err)
|
return fmt.Errorf("could not reset metabase: %w", err)
|
||||||
|
@ -177,9 +181,9 @@ func (s *Shard) refillMetabase() error {
|
||||||
var err error
|
var err error
|
||||||
switch obj.Type() {
|
switch obj.Type() {
|
||||||
case objectSDK.TypeTombstone:
|
case objectSDK.TypeTombstone:
|
||||||
err = s.refillTombstoneObject(obj)
|
err = s.refillTombstoneObject(ctx, obj)
|
||||||
case objectSDK.TypeLock:
|
case objectSDK.TypeLock:
|
||||||
err = s.refillLockObject(obj)
|
err = s.refillLockObject(ctx, obj)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -190,7 +194,7 @@ func (s *Shard) refillMetabase() error {
|
||||||
mPrm.SetObject(obj)
|
mPrm.SetObject(obj)
|
||||||
mPrm.SetStorageID(descriptor)
|
mPrm.SetStorageID(descriptor)
|
||||||
|
|
||||||
_, err = s.metaBase.Put(mPrm)
|
_, err = s.metaBase.Put(ctx, mPrm)
|
||||||
if err != nil && !meta.IsErrRemoved(err) && !errors.Is(err, meta.ErrObjectIsExpired) {
|
if err != nil && !meta.IsErrRemoved(err) && !errors.Is(err, meta.ErrObjectIsExpired) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -209,7 +213,7 @@ func (s *Shard) refillMetabase() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Shard) refillLockObject(obj *objectSDK.Object) error {
|
func (s *Shard) refillLockObject(ctx context.Context, obj *objectSDK.Object) error {
|
||||||
var lock objectSDK.Lock
|
var lock objectSDK.Lock
|
||||||
if err := lock.Unmarshal(obj.Payload()); err != nil {
|
if err := lock.Unmarshal(obj.Payload()); err != nil {
|
||||||
return fmt.Errorf("could not unmarshal lock content: %w", err)
|
return fmt.Errorf("could not unmarshal lock content: %w", err)
|
||||||
|
@ -220,14 +224,14 @@ func (s *Shard) refillLockObject(obj *objectSDK.Object) error {
|
||||||
|
|
||||||
cnr, _ := obj.ContainerID()
|
cnr, _ := obj.ContainerID()
|
||||||
id, _ := obj.ID()
|
id, _ := obj.ID()
|
||||||
err := s.metaBase.Lock(cnr, id, locked)
|
err := s.metaBase.Lock(ctx, cnr, id, locked)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not lock objects: %w", err)
|
return fmt.Errorf("could not lock objects: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Shard) refillTombstoneObject(obj *objectSDK.Object) error {
|
func (s *Shard) refillTombstoneObject(ctx context.Context, obj *objectSDK.Object) error {
|
||||||
tombstone := objectSDK.NewTombstone()
|
tombstone := objectSDK.NewTombstone()
|
||||||
|
|
||||||
if err := tombstone.Unmarshal(obj.Payload()); err != nil {
|
if err := tombstone.Unmarshal(obj.Payload()); err != nil {
|
||||||
|
@ -250,7 +254,7 @@ func (s *Shard) refillTombstoneObject(obj *objectSDK.Object) error {
|
||||||
inhumePrm.SetTombstoneAddress(tombAddr)
|
inhumePrm.SetTombstoneAddress(tombAddr)
|
||||||
inhumePrm.SetAddresses(tombMembers...)
|
inhumePrm.SetAddresses(tombMembers...)
|
||||||
|
|
||||||
_, err := s.metaBase.Inhume(inhumePrm)
|
_, err := s.metaBase.Inhume(ctx, inhumePrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not inhume objects: %w", err)
|
return fmt.Errorf("could not inhume objects: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -290,7 +294,10 @@ func (s *Shard) Close() error {
|
||||||
// Reload reloads configuration portions that are necessary.
|
// Reload reloads configuration portions that are necessary.
|
||||||
// If a config option is invalid, it logs an error and returns nil.
|
// If a config option is invalid, it logs an error and returns nil.
|
||||||
// If there was a problem with applying new configuration, an error is returned.
|
// If there was a problem with applying new configuration, an error is returned.
|
||||||
func (s *Shard) Reload(opts ...Option) error {
|
func (s *Shard) Reload(ctx context.Context, opts ...Option) error {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "Shard.Reload")
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
// Do not use defaultCfg here missing options need not be reloaded.
|
// Do not use defaultCfg here missing options need not be reloaded.
|
||||||
var c cfg
|
var c cfg
|
||||||
for i := range opts {
|
for i := range opts {
|
||||||
|
@ -314,7 +321,7 @@ func (s *Shard) Reload(opts ...Option) error {
|
||||||
// Here we refill metabase only if a new instance was opened. This is a feature,
|
// Here we refill metabase only if a new instance was opened. This is a feature,
|
||||||
// we don't want to hang for some time just because we forgot to change
|
// we don't want to hang for some time just because we forgot to change
|
||||||
// config after the node was updated.
|
// config after the node was updated.
|
||||||
err = s.refillMetabase()
|
err = s.refillMetabase(ctx)
|
||||||
} else {
|
} else {
|
||||||
err = s.metaBase.Init()
|
err = s.metaBase.Init()
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,7 @@ func TestRefillMetabaseCorrupted(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
sh := New(
|
sh := New(
|
||||||
|
WithID(NewIDFromBytes([]byte{})),
|
||||||
WithBlobStorOptions(blobOpts...),
|
WithBlobStorOptions(blobOpts...),
|
||||||
WithPiloramaOptions(pilorama.WithPath(filepath.Join(dir, "pilorama"))),
|
WithPiloramaOptions(pilorama.WithPath(filepath.Join(dir, "pilorama"))),
|
||||||
WithMetaBaseOptions(meta.WithPath(filepath.Join(dir, "meta")), meta.WithEpochState(epochState{})))
|
WithMetaBaseOptions(meta.WithPath(filepath.Join(dir, "meta")), meta.WithEpochState(epochState{})))
|
||||||
|
@ -138,12 +139,12 @@ func TestRefillMetabaseCorrupted(t *testing.T) {
|
||||||
|
|
||||||
var putPrm PutPrm
|
var putPrm PutPrm
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, sh.Close())
|
require.NoError(t, sh.Close())
|
||||||
|
|
||||||
addr := object.AddressOf(obj)
|
addr := object.AddressOf(obj)
|
||||||
_, err = fsTree.Put(common.PutPrm{Address: addr, RawData: []byte("not an object")})
|
_, err = fsTree.Put(context.Background(), common.PutPrm{Address: addr, RawData: []byte("not an object")})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
sh = New(
|
sh = New(
|
||||||
|
@ -245,13 +246,13 @@ func TestRefillMetabase(t *testing.T) {
|
||||||
for _, v := range mObjs {
|
for _, v := range mObjs {
|
||||||
putPrm.SetObject(v.obj)
|
putPrm.SetObject(v.obj)
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
putPrm.SetObject(tombObj)
|
putPrm.SetObject(tombObj)
|
||||||
|
|
||||||
_, err = sh.Put(putPrm)
|
_, err = sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// LOCK object handling
|
// LOCK object handling
|
||||||
|
@ -263,11 +264,11 @@ func TestRefillMetabase(t *testing.T) {
|
||||||
objectSDK.WriteLock(lockObj, lock)
|
objectSDK.WriteLock(lockObj, lock)
|
||||||
|
|
||||||
putPrm.SetObject(lockObj)
|
putPrm.SetObject(lockObj)
|
||||||
_, err = sh.Put(putPrm)
|
_, err = sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
lockID, _ := lockObj.ID()
|
lockID, _ := lockObj.ID()
|
||||||
require.NoError(t, sh.Lock(cnrLocked, lockID, locked))
|
require.NoError(t, sh.Lock(context.Background(), cnrLocked, lockID, locked))
|
||||||
|
|
||||||
var inhumePrm InhumePrm
|
var inhumePrm InhumePrm
|
||||||
inhumePrm.SetTarget(object.AddressOf(tombObj), tombMembers...)
|
inhumePrm.SetTarget(object.AddressOf(tombObj), tombMembers...)
|
||||||
|
@ -368,7 +369,7 @@ func TestRefillMetabase(t *testing.T) {
|
||||||
checkObj(object.AddressOf(tombObj), nil)
|
checkObj(object.AddressOf(tombObj), nil)
|
||||||
checkTombMembers(false)
|
checkTombMembers(false)
|
||||||
|
|
||||||
err = sh.refillMetabase()
|
err = sh.refillMetabase(context.Background())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c, err = sh.metaBase.ObjectCounters()
|
c, err = sh.metaBase.ObjectCounters()
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
package shard
|
package shard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/writecache"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/writecache"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,14 +32,21 @@ func (p *DeletePrm) SetAddresses(addr ...oid.Address) {
|
||||||
|
|
||||||
// Delete removes data from the shard's writeCache, metaBase and
|
// Delete removes data from the shard's writeCache, metaBase and
|
||||||
// blobStor.
|
// blobStor.
|
||||||
func (s *Shard) Delete(prm DeletePrm) (DeleteRes, error) {
|
func (s *Shard) Delete(ctx context.Context, prm DeletePrm) (DeleteRes, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "Shard.Delete",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("shard_id", s.ID().String()),
|
||||||
|
attribute.Int("addr_count", len(prm.addr)),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
s.m.RLock()
|
s.m.RLock()
|
||||||
defer s.m.RUnlock()
|
defer s.m.RUnlock()
|
||||||
|
|
||||||
return s.delete(prm)
|
return s.delete(ctx, prm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Shard) delete(prm DeletePrm) (DeleteRes, error) {
|
func (s *Shard) delete(ctx context.Context, prm DeletePrm) (DeleteRes, error) {
|
||||||
if s.info.Mode.ReadOnly() {
|
if s.info.Mode.ReadOnly() {
|
||||||
return DeleteRes{}, ErrReadOnlyMode
|
return DeleteRes{}, ErrReadOnlyMode
|
||||||
} else if s.info.Mode.NoMetabase() {
|
} else if s.info.Mode.NoMetabase() {
|
||||||
|
@ -48,7 +59,7 @@ func (s *Shard) delete(prm DeletePrm) (DeleteRes, error) {
|
||||||
|
|
||||||
for i := range prm.addr {
|
for i := range prm.addr {
|
||||||
if s.hasWriteCache() {
|
if s.hasWriteCache() {
|
||||||
err := s.writeCache.Delete(prm.addr[i])
|
err := s.writeCache.Delete(ctx, prm.addr[i])
|
||||||
if err != nil && !IsErrNotFound(err) && !errors.Is(err, writecache.ErrReadOnly) {
|
if err != nil && !IsErrNotFound(err) && !errors.Is(err, writecache.ErrReadOnly) {
|
||||||
s.log.Warn(logs.ShardCantDeleteObjectFromWriteCache, zap.String("error", err.Error()))
|
s.log.Warn(logs.ShardCantDeleteObjectFromWriteCache, zap.String("error", err.Error()))
|
||||||
}
|
}
|
||||||
|
@ -57,7 +68,7 @@ func (s *Shard) delete(prm DeletePrm) (DeleteRes, error) {
|
||||||
var sPrm meta.StorageIDPrm
|
var sPrm meta.StorageIDPrm
|
||||||
sPrm.SetAddress(prm.addr[i])
|
sPrm.SetAddress(prm.addr[i])
|
||||||
|
|
||||||
res, err := s.metaBase.StorageID(sPrm)
|
res, err := s.metaBase.StorageID(ctx, sPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Debug(logs.ShardCantGetStorageIDFromMetabase,
|
s.log.Debug(logs.ShardCantGetStorageIDFromMetabase,
|
||||||
zap.Stringer("object", prm.addr[i]),
|
zap.Stringer("object", prm.addr[i]),
|
||||||
|
@ -74,7 +85,7 @@ func (s *Shard) delete(prm DeletePrm) (DeleteRes, error) {
|
||||||
var delPrm meta.DeletePrm
|
var delPrm meta.DeletePrm
|
||||||
delPrm.SetAddresses(prm.addr...)
|
delPrm.SetAddresses(prm.addr...)
|
||||||
|
|
||||||
res, err := s.metaBase.Delete(delPrm)
|
res, err := s.metaBase.Delete(ctx, delPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return DeleteRes{}, err // stop on metabase error ?
|
return DeleteRes{}, err // stop on metabase error ?
|
||||||
}
|
}
|
||||||
|
@ -99,7 +110,7 @@ func (s *Shard) delete(prm DeletePrm) (DeleteRes, error) {
|
||||||
id := smalls[prm.addr[i]]
|
id := smalls[prm.addr[i]]
|
||||||
delPrm.StorageID = id
|
delPrm.StorageID = id
|
||||||
|
|
||||||
_, err = s.blobStor.Delete(delPrm)
|
_, err = s.blobStor.Delete(ctx, delPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Debug(logs.ShardCantRemoveObjectFromBlobStor,
|
s.log.Debug(logs.ShardCantRemoveObjectFromBlobStor,
|
||||||
zap.Stringer("object_address", prm.addr[i]),
|
zap.Stringer("object_address", prm.addr[i]),
|
||||||
|
|
|
@ -43,13 +43,13 @@ func testShardDelete(t *testing.T, hasWriteCache bool) {
|
||||||
var delPrm shard.DeletePrm
|
var delPrm shard.DeletePrm
|
||||||
delPrm.SetAddresses(object.AddressOf(obj))
|
delPrm.SetAddresses(object.AddressOf(obj))
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = testGet(t, sh, getPrm, hasWriteCache)
|
_, err = testGet(t, sh, getPrm, hasWriteCache)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = sh.Delete(delPrm)
|
_, err = sh.Delete(context.TODO(), delPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = sh.Get(context.Background(), getPrm)
|
_, err = sh.Get(context.Background(), getPrm)
|
||||||
|
@ -67,13 +67,13 @@ func testShardDelete(t *testing.T, hasWriteCache bool) {
|
||||||
var delPrm shard.DeletePrm
|
var delPrm shard.DeletePrm
|
||||||
delPrm.SetAddresses(object.AddressOf(obj))
|
delPrm.SetAddresses(object.AddressOf(obj))
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = sh.Get(context.Background(), getPrm)
|
_, err = sh.Get(context.Background(), getPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = sh.Delete(delPrm)
|
_, err = sh.Delete(context.Background(), delPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = sh.Get(context.Background(), getPrm)
|
_, err = sh.Get(context.Background(), getPrm)
|
||||||
|
|
|
@ -104,7 +104,7 @@ func testDump(t *testing.T, objCount int, hasWriteCache bool) {
|
||||||
|
|
||||||
var prm shard.PutPrm
|
var prm shard.PutPrm
|
||||||
prm.SetObject(objects[i])
|
prm.SetObject(objects[i])
|
||||||
_, err := sh.Put(prm)
|
_, err := sh.Put(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,13 +129,13 @@ func testDump(t *testing.T, objCount int, hasWriteCache bool) {
|
||||||
t.Run("empty dump", func(t *testing.T) {
|
t.Run("empty dump", func(t *testing.T) {
|
||||||
var restorePrm shard.RestorePrm
|
var restorePrm shard.RestorePrm
|
||||||
restorePrm.WithPath(outEmpty)
|
restorePrm.WithPath(outEmpty)
|
||||||
res, err := sh.Restore(restorePrm)
|
res, err := sh.Restore(context.Background(), restorePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 0, res.Count())
|
require.Equal(t, 0, res.Count())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid path", func(t *testing.T) {
|
t.Run("invalid path", func(t *testing.T) {
|
||||||
_, err := sh.Restore(*new(shard.RestorePrm))
|
_, err := sh.Restore(context.Background(), *new(shard.RestorePrm))
|
||||||
require.ErrorIs(t, err, os.ErrNotExist)
|
require.ErrorIs(t, err, os.ErrNotExist)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ func testDump(t *testing.T, objCount int, hasWriteCache bool) {
|
||||||
var restorePrm shard.RestorePrm
|
var restorePrm shard.RestorePrm
|
||||||
restorePrm.WithPath(out)
|
restorePrm.WithPath(out)
|
||||||
|
|
||||||
_, err := sh.Restore(restorePrm)
|
_, err := sh.Restore(context.Background(), restorePrm)
|
||||||
require.ErrorIs(t, err, shard.ErrInvalidMagic)
|
require.ErrorIs(t, err, shard.ErrInvalidMagic)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ func testDump(t *testing.T, objCount int, hasWriteCache bool) {
|
||||||
var restorePrm shard.RestorePrm
|
var restorePrm shard.RestorePrm
|
||||||
restorePrm.WithPath(out)
|
restorePrm.WithPath(out)
|
||||||
|
|
||||||
_, err := sh.Restore(restorePrm)
|
_, err := sh.Restore(context.Background(), restorePrm)
|
||||||
require.ErrorIs(t, err, io.ErrUnexpectedEOF)
|
require.ErrorIs(t, err, io.ErrUnexpectedEOF)
|
||||||
})
|
})
|
||||||
t.Run("incomplete object data", func(t *testing.T) {
|
t.Run("incomplete object data", func(t *testing.T) {
|
||||||
|
@ -173,7 +173,7 @@ func testDump(t *testing.T, objCount int, hasWriteCache bool) {
|
||||||
var restorePrm shard.RestorePrm
|
var restorePrm shard.RestorePrm
|
||||||
restorePrm.WithPath(out)
|
restorePrm.WithPath(out)
|
||||||
|
|
||||||
_, err := sh.Restore(restorePrm)
|
_, err := sh.Restore(context.Background(), restorePrm)
|
||||||
require.ErrorIs(t, err, io.EOF)
|
require.ErrorIs(t, err, io.EOF)
|
||||||
})
|
})
|
||||||
t.Run("invalid object", func(t *testing.T) {
|
t.Run("invalid object", func(t *testing.T) {
|
||||||
|
@ -184,7 +184,7 @@ func testDump(t *testing.T, objCount int, hasWriteCache bool) {
|
||||||
var restorePrm shard.RestorePrm
|
var restorePrm shard.RestorePrm
|
||||||
restorePrm.WithPath(out)
|
restorePrm.WithPath(out)
|
||||||
|
|
||||||
_, err := sh.Restore(restorePrm)
|
_, err := sh.Restore(context.Background(), restorePrm)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
t.Run("skip errors", func(t *testing.T) {
|
t.Run("skip errors", func(t *testing.T) {
|
||||||
|
@ -195,7 +195,7 @@ func testDump(t *testing.T, objCount int, hasWriteCache bool) {
|
||||||
restorePrm.WithPath(out)
|
restorePrm.WithPath(out)
|
||||||
restorePrm.WithIgnoreErrors(true)
|
restorePrm.WithIgnoreErrors(true)
|
||||||
|
|
||||||
res, err := sh.Restore(restorePrm)
|
res, err := sh.Restore(context.Background(), restorePrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, objCount, res.Count())
|
require.Equal(t, objCount, res.Count())
|
||||||
require.Equal(t, 2, res.FailCount())
|
require.Equal(t, 2, res.FailCount())
|
||||||
|
@ -208,7 +208,7 @@ func testDump(t *testing.T, objCount int, hasWriteCache bool) {
|
||||||
t.Run("must allow write", func(t *testing.T) {
|
t.Run("must allow write", func(t *testing.T) {
|
||||||
require.NoError(t, sh.SetMode(mode.ReadOnly))
|
require.NoError(t, sh.SetMode(mode.ReadOnly))
|
||||||
|
|
||||||
_, err := sh.Restore(prm)
|
_, err := sh.Restore(context.Background(), prm)
|
||||||
require.ErrorIs(t, err, shard.ErrReadOnlyMode)
|
require.ErrorIs(t, err, shard.ErrReadOnlyMode)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ func TestStream(t *testing.T) {
|
||||||
|
|
||||||
var prm shard.PutPrm
|
var prm shard.PutPrm
|
||||||
prm.SetObject(objects[i])
|
prm.SetObject(objects[i])
|
||||||
_, err := sh1.Put(prm)
|
_, err := sh1.Put(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ func TestStream(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkRestore(t *testing.T, sh *shard.Shard, prm shard.RestorePrm, objects []*objectSDK.Object) {
|
func checkRestore(t *testing.T, sh *shard.Shard, prm shard.RestorePrm, objects []*objectSDK.Object) {
|
||||||
res, err := sh.Restore(prm)
|
res, err := sh.Restore(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, len(objects), res.Count())
|
require.Equal(t, len(objects), res.Count())
|
||||||
|
|
||||||
|
@ -333,7 +333,7 @@ func TestDumpIgnoreErrors(t *testing.T) {
|
||||||
|
|
||||||
var prm shard.PutPrm
|
var prm shard.PutPrm
|
||||||
prm.SetObject(objects[i])
|
prm.SetObject(objects[i])
|
||||||
_, err := sh.Put(prm)
|
_, err := sh.Put(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,12 @@ package shard
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExistsPrm groups the parameters of Exists operation.
|
// ExistsPrm groups the parameters of Exists operation.
|
||||||
|
@ -36,6 +39,13 @@ func (p ExistsRes) Exists() bool {
|
||||||
// Returns an error of type apistatus.ObjectAlreadyRemoved if object has been marked as removed.
|
// Returns an error of type apistatus.ObjectAlreadyRemoved if object has been marked as removed.
|
||||||
// Returns the object.ErrObjectIsExpired if the object is presented but already expired.
|
// Returns the object.ErrObjectIsExpired if the object is presented but already expired.
|
||||||
func (s *Shard) Exists(ctx context.Context, prm ExistsPrm) (ExistsRes, error) {
|
func (s *Shard) Exists(ctx context.Context, prm ExistsPrm) (ExistsRes, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "Shard.Exists",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("shard_id", s.ID().String()),
|
||||||
|
attribute.String("address", prm.addr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
var exists bool
|
var exists bool
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -54,7 +64,7 @@ func (s *Shard) Exists(ctx context.Context, prm ExistsPrm) (ExistsRes, error) {
|
||||||
existsPrm.SetAddress(prm.addr)
|
existsPrm.SetAddress(prm.addr)
|
||||||
|
|
||||||
var res meta.ExistsRes
|
var res meta.ExistsRes
|
||||||
res, err = s.metaBase.Exists(existsPrm)
|
res, err = s.metaBase.Exists(ctx, existsPrm)
|
||||||
exists = res.Exists()
|
exists = res.Exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -234,7 +234,7 @@ func (s *Shard) removeGarbage() {
|
||||||
deletePrm.SetAddresses(buf...)
|
deletePrm.SetAddresses(buf...)
|
||||||
|
|
||||||
// delete accumulated objects
|
// delete accumulated objects
|
||||||
_, err = s.delete(deletePrm)
|
_, err = s.delete(context.TODO(), deletePrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warn(logs.ShardCouldNotDeleteTheObjects,
|
s.log.Warn(logs.ShardCouldNotDeleteTheObjects,
|
||||||
zap.String("error", err.Error()),
|
zap.String("error", err.Error()),
|
||||||
|
@ -320,7 +320,7 @@ func (s *Shard) handleExpiredObjects(ctx context.Context, expired []oid.Address)
|
||||||
inhumePrm.SetGCMark()
|
inhumePrm.SetGCMark()
|
||||||
|
|
||||||
// inhume the collected objects
|
// inhume the collected objects
|
||||||
res, err := s.metaBase.Inhume(inhumePrm)
|
res, err := s.metaBase.Inhume(ctx, inhumePrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warn(logs.ShardCouldNotInhumeTheObjects,
|
s.log.Warn(logs.ShardCouldNotInhumeTheObjects,
|
||||||
zap.String("error", err.Error()),
|
zap.String("error", err.Error()),
|
||||||
|
@ -485,7 +485,7 @@ func (s *Shard) selectExpired(ctx context.Context, epoch uint64, addresses []oid
|
||||||
// and clears up corresponding graveyard records.
|
// and clears up corresponding graveyard records.
|
||||||
//
|
//
|
||||||
// Does not modify tss.
|
// Does not modify tss.
|
||||||
func (s *Shard) HandleExpiredTombstones(tss []meta.TombstonedObject) {
|
func (s *Shard) HandleExpiredTombstones(ctx context.Context, tss []meta.TombstonedObject) {
|
||||||
if s.GetMode().NoMetabase() {
|
if s.GetMode().NoMetabase() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -502,7 +502,7 @@ func (s *Shard) HandleExpiredTombstones(tss []meta.TombstonedObject) {
|
||||||
pInhume.SetAddresses(tsAddrs...)
|
pInhume.SetAddresses(tsAddrs...)
|
||||||
|
|
||||||
// inhume tombstones
|
// inhume tombstones
|
||||||
res, err := s.metaBase.Inhume(pInhume)
|
res, err := s.metaBase.Inhume(ctx, pInhume)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warn(logs.ShardCouldNotMarkTombstonesAsGarbage,
|
s.log.Warn(logs.ShardCouldNotMarkTombstonesAsGarbage,
|
||||||
zap.String("error", err.Error()),
|
zap.String("error", err.Error()),
|
||||||
|
@ -547,7 +547,7 @@ func (s *Shard) HandleExpiredLocks(ctx context.Context, epoch uint64, lockers []
|
||||||
pInhume.SetAddresses(lockers...)
|
pInhume.SetAddresses(lockers...)
|
||||||
pInhume.SetForceGCMark()
|
pInhume.SetForceGCMark()
|
||||||
|
|
||||||
res, err := s.metaBase.Inhume(pInhume)
|
res, err := s.metaBase.Inhume(ctx, pInhume)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warn(logs.ShardFailureToMarkLockersAsGarbage,
|
s.log.Warn(logs.ShardFailureToMarkLockersAsGarbage,
|
||||||
zap.String("error", err.Error()),
|
zap.String("error", err.Error()),
|
||||||
|
|
|
@ -100,14 +100,14 @@ func Test_GCDropsLockedExpiredObject(t *testing.T) {
|
||||||
var putPrm shard.PutPrm
|
var putPrm shard.PutPrm
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = sh.Lock(cnr, lockID, []oid.ID{objID})
|
err = sh.Lock(context.Background(), cnr, lockID, []oid.ID{objID})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
putPrm.SetObject(lock)
|
putPrm.SetObject(lock)
|
||||||
_, err = sh.Put(putPrm)
|
_, err = sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
epoch.Value = 105
|
epoch.Value = 105
|
||||||
|
|
|
@ -96,7 +96,7 @@ func (s *Shard) Get(ctx context.Context, prm GetPrm) (GetRes, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
skipMeta := prm.skipMeta || s.info.Mode.NoMetabase()
|
skipMeta := prm.skipMeta || s.info.Mode.NoMetabase()
|
||||||
obj, hasMeta, err := s.fetchObjectData(prm.addr, skipMeta, cb, wc)
|
obj, hasMeta, err := s.fetchObjectData(ctx, prm.addr, skipMeta, cb, wc)
|
||||||
|
|
||||||
return GetRes{
|
return GetRes{
|
||||||
obj: obj,
|
obj: obj,
|
||||||
|
@ -109,7 +109,7 @@ func (s *Shard) Get(ctx context.Context, prm GetPrm) (GetRes, error) {
|
||||||
var emptyStorageID = make([]byte, 0)
|
var emptyStorageID = make([]byte, 0)
|
||||||
|
|
||||||
// fetchObjectData looks through writeCache and blobStor to find object.
|
// fetchObjectData looks through writeCache and blobStor to find object.
|
||||||
func (s *Shard) fetchObjectData(addr oid.Address, skipMeta bool, cb storFetcher, wc func(w writecache.Cache) (*objectSDK.Object, error)) (*objectSDK.Object, bool, error) {
|
func (s *Shard) fetchObjectData(ctx context.Context, addr oid.Address, skipMeta bool, cb storFetcher, wc func(w writecache.Cache) (*objectSDK.Object, error)) (*objectSDK.Object, bool, error) {
|
||||||
var (
|
var (
|
||||||
mErr error
|
mErr error
|
||||||
mRes meta.ExistsRes
|
mRes meta.ExistsRes
|
||||||
|
@ -118,7 +118,7 @@ func (s *Shard) fetchObjectData(addr oid.Address, skipMeta bool, cb storFetcher,
|
||||||
if !skipMeta {
|
if !skipMeta {
|
||||||
var mPrm meta.ExistsPrm
|
var mPrm meta.ExistsPrm
|
||||||
mPrm.SetAddress(addr)
|
mPrm.SetAddress(addr)
|
||||||
mRes, mErr = s.metaBase.Exists(mPrm)
|
mRes, mErr = s.metaBase.Exists(ctx, mPrm)
|
||||||
if mErr != nil && !s.info.Mode.NoMetabase() {
|
if mErr != nil && !s.info.Mode.NoMetabase() {
|
||||||
return nil, false, mErr
|
return nil, false, mErr
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ func (s *Shard) fetchObjectData(addr oid.Address, skipMeta bool, cb storFetcher,
|
||||||
var mPrm meta.StorageIDPrm
|
var mPrm meta.StorageIDPrm
|
||||||
mPrm.SetAddress(addr)
|
mPrm.SetAddress(addr)
|
||||||
|
|
||||||
mExRes, err := s.metaBase.StorageID(mPrm)
|
mExRes, err := s.metaBase.StorageID(ctx, mPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, true, fmt.Errorf("can't fetch blobovnicza id from metabase: %w", err)
|
return nil, true, fmt.Errorf("can't fetch blobovnicza id from metabase: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ func testShardGet(t *testing.T, hasWriteCache bool) {
|
||||||
|
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
getPrm.SetAddress(object.AddressOf(obj))
|
getPrm.SetAddress(object.AddressOf(obj))
|
||||||
|
@ -58,7 +58,7 @@ func testShardGet(t *testing.T, hasWriteCache bool) {
|
||||||
|
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
getPrm.SetAddress(object.AddressOf(obj))
|
getPrm.SetAddress(object.AddressOf(obj))
|
||||||
|
@ -86,7 +86,7 @@ func testShardGet(t *testing.T, hasWriteCache bool) {
|
||||||
|
|
||||||
putPrm.SetObject(child)
|
putPrm.SetObject(child)
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
getPrm.SetAddress(object.AddressOf(child))
|
getPrm.SetAddress(object.AddressOf(child))
|
||||||
|
|
|
@ -73,7 +73,7 @@ func (s *Shard) Head(ctx context.Context, prm HeadPrm) (HeadRes, error) {
|
||||||
headParams.SetRaw(prm.raw)
|
headParams.SetRaw(prm.raw)
|
||||||
|
|
||||||
var res meta.GetRes
|
var res meta.GetRes
|
||||||
res, err = s.metaBase.Get(headParams)
|
res, err = s.metaBase.Get(ctx, headParams)
|
||||||
obj = res.Header()
|
obj = res.Header()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ func testShardHead(t *testing.T, hasWriteCache bool) {
|
||||||
|
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
headPrm.SetAddress(object.AddressOf(obj))
|
headPrm.SetAddress(object.AddressOf(obj))
|
||||||
|
@ -62,7 +62,7 @@ func testShardHead(t *testing.T, hasWriteCache bool) {
|
||||||
|
|
||||||
putPrm.SetObject(child)
|
putPrm.SetObject(child)
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
headPrm.SetAddress(object.AddressOf(parent))
|
headPrm.SetAddress(object.AddressOf(parent))
|
||||||
|
|
|
@ -5,9 +5,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,6 +65,12 @@ var ErrLockObjectRemoval = meta.ErrLockObjectRemoval
|
||||||
//
|
//
|
||||||
// Returns ErrReadOnlyMode error if shard is in "read-only" mode.
|
// Returns ErrReadOnlyMode error if shard is in "read-only" mode.
|
||||||
func (s *Shard) Inhume(ctx context.Context, prm InhumePrm) (InhumeRes, error) {
|
func (s *Shard) Inhume(ctx context.Context, prm InhumePrm) (InhumeRes, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "Shard.Inhume",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("shard_id", s.ID().String()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
s.m.RLock()
|
s.m.RLock()
|
||||||
|
|
||||||
if s.info.Mode.ReadOnly() {
|
if s.info.Mode.ReadOnly() {
|
||||||
|
@ -74,7 +83,7 @@ func (s *Shard) Inhume(ctx context.Context, prm InhumePrm) (InhumeRes, error) {
|
||||||
|
|
||||||
if s.hasWriteCache() {
|
if s.hasWriteCache() {
|
||||||
for i := range prm.target {
|
for i := range prm.target {
|
||||||
_ = s.writeCache.Delete(prm.target[i])
|
_ = s.writeCache.Delete(ctx, prm.target[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +101,7 @@ func (s *Shard) Inhume(ctx context.Context, prm InhumePrm) (InhumeRes, error) {
|
||||||
metaPrm.SetForceGCMark()
|
metaPrm.SetForceGCMark()
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := s.metaBase.Inhume(metaPrm)
|
res, err := s.metaBase.Inhume(ctx, metaPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, meta.ErrLockObjectRemoval) {
|
if errors.Is(err, meta.ErrLockObjectRemoval) {
|
||||||
s.m.RUnlock()
|
s.m.RUnlock()
|
||||||
|
|
|
@ -42,7 +42,7 @@ func testShardInhume(t *testing.T, hasWriteCache bool) {
|
||||||
var getPrm shard.GetPrm
|
var getPrm shard.GetPrm
|
||||||
getPrm.SetAddress(object.AddressOf(obj))
|
getPrm.SetAddress(object.AddressOf(obj))
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = testGet(t, sh, getPrm, hasWriteCache)
|
_, err = testGet(t, sh, getPrm, hasWriteCache)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package shard
|
package shard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
|
@ -85,7 +86,7 @@ func (s *Shard) List() (res SelectRes, err error) {
|
||||||
sPrm.SetContainerID(lst[i])
|
sPrm.SetContainerID(lst[i])
|
||||||
sPrm.SetFilters(filters)
|
sPrm.SetFilters(filters)
|
||||||
|
|
||||||
sRes, err := s.metaBase.Select(sPrm) // consider making List in metabase
|
sRes, err := s.metaBase.Select(context.TODO(), sPrm) // consider making List in metabase
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Debug(logs.ShardCantSelectAllObjects,
|
s.log.Debug(logs.ShardCantSelectAllObjects,
|
||||||
zap.Stringer("cid", lst[i]),
|
zap.Stringer("cid", lst[i]),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package shard_test
|
package shard_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
|
@ -52,7 +53,7 @@ func testShardList(t *testing.T, sh *shard.Shard) {
|
||||||
|
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package shard
|
package shard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lock marks objects as locked with another object. All objects from the
|
// Lock marks objects as locked with another object. All objects from the
|
||||||
|
@ -14,7 +18,16 @@ import (
|
||||||
// Allows locking regular objects only (otherwise returns apistatus.LockNonRegularObject).
|
// Allows locking regular objects only (otherwise returns apistatus.LockNonRegularObject).
|
||||||
//
|
//
|
||||||
// Locked list should be unique. Panics if it is empty.
|
// Locked list should be unique. Panics if it is empty.
|
||||||
func (s *Shard) Lock(idCnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
func (s *Shard) Lock(ctx context.Context, idCnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "Shard.Lock",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("shard_id", s.ID().String()),
|
||||||
|
attribute.String("container_id", idCnr.EncodeToString()),
|
||||||
|
attribute.String("locker", locker.EncodeToString()),
|
||||||
|
attribute.Int("locked_count", len(locked)),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
s.m.RLock()
|
s.m.RLock()
|
||||||
defer s.m.RUnlock()
|
defer s.m.RUnlock()
|
||||||
|
|
||||||
|
@ -25,7 +38,7 @@ func (s *Shard) Lock(idCnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
||||||
return ErrDegradedMode
|
return ErrDegradedMode
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.metaBase.Lock(idCnr, locker, locked)
|
err := s.metaBase.Lock(ctx, idCnr, locker, locked)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("metabase lock: %w", err)
|
return fmt.Errorf("metabase lock: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -35,7 +48,14 @@ func (s *Shard) Lock(idCnr cid.ID, locker oid.ID, locked []oid.ID) error {
|
||||||
|
|
||||||
// IsLocked checks object locking relation of the provided object. Not found object is
|
// IsLocked checks object locking relation of the provided object. Not found object is
|
||||||
// considered as not locked. Requires healthy metabase, returns ErrDegradedMode otherwise.
|
// considered as not locked. Requires healthy metabase, returns ErrDegradedMode otherwise.
|
||||||
func (s *Shard) IsLocked(addr oid.Address) (bool, error) {
|
func (s *Shard) IsLocked(ctx context.Context, addr oid.Address) (bool, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "Shard.IsLocked",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("shard_id", s.ID().String()),
|
||||||
|
attribute.String("address", addr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
m := s.GetMode()
|
m := s.GetMode()
|
||||||
if m.NoMetabase() {
|
if m.NoMetabase() {
|
||||||
return false, ErrDegradedMode
|
return false, ErrDegradedMode
|
||||||
|
@ -44,7 +64,7 @@ func (s *Shard) IsLocked(addr oid.Address) (bool, error) {
|
||||||
var prm meta.IsLockedPrm
|
var prm meta.IsLockedPrm
|
||||||
prm.SetAddress(addr)
|
prm.SetAddress(addr)
|
||||||
|
|
||||||
res, err := s.metaBase.IsLocked(prm)
|
res, err := s.metaBase.IsLocked(ctx, prm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,16 +76,16 @@ func TestShard_Lock(t *testing.T) {
|
||||||
var putPrm shard.PutPrm
|
var putPrm shard.PutPrm
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// lock the object
|
// lock the object
|
||||||
|
|
||||||
err = sh.Lock(cnr, lockID, []oid.ID{objID})
|
err = sh.Lock(context.Background(), cnr, lockID, []oid.ID{objID})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
putPrm.SetObject(lock)
|
putPrm.SetObject(lock)
|
||||||
_, err = sh.Put(putPrm)
|
_, err = sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("inhuming locked objects", func(t *testing.T) {
|
t.Run("inhuming locked objects", func(t *testing.T) {
|
||||||
|
@ -158,21 +158,21 @@ func TestShard_IsLocked(t *testing.T) {
|
||||||
var putPrm shard.PutPrm
|
var putPrm shard.PutPrm
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// not locked object is not locked
|
// not locked object is not locked
|
||||||
|
|
||||||
locked, err := sh.IsLocked(objectcore.AddressOf(obj))
|
locked, err := sh.IsLocked(context.Background(), objectcore.AddressOf(obj))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.False(t, locked)
|
require.False(t, locked)
|
||||||
|
|
||||||
// locked object is locked
|
// locked object is locked
|
||||||
|
|
||||||
require.NoError(t, sh.Lock(cnrID, lockID, []oid.ID{objID}))
|
require.NoError(t, sh.Lock(context.Background(), cnrID, lockID, []oid.ID{objID}))
|
||||||
|
|
||||||
locked, err = sh.IsLocked(objectcore.AddressOf(obj))
|
locked, err = sh.IsLocked(context.Background(), objectcore.AddressOf(obj))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.True(t, locked)
|
require.True(t, locked)
|
||||||
|
|
|
@ -109,7 +109,7 @@ func TestCounters(t *testing.T) {
|
||||||
for i := 0; i < objNumber; i++ {
|
for i := 0; i < objNumber; i++ {
|
||||||
prm.SetObject(oo[i])
|
prm.SetObject(oo[i])
|
||||||
|
|
||||||
_, err := sh.Put(prm)
|
_, err := sh.Put(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ func TestCounters(t *testing.T) {
|
||||||
deletedNumber := int(phy / 4)
|
deletedNumber := int(phy / 4)
|
||||||
prm.SetAddresses(addrFromObjs(oo[:deletedNumber])...)
|
prm.SetAddresses(addrFromObjs(oo[:deletedNumber])...)
|
||||||
|
|
||||||
_, err := sh.Delete(prm)
|
_, err := sh.Delete(context.Background(), prm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, phy-uint64(deletedNumber), mm.objCounters[physical])
|
require.Equal(t, phy-uint64(deletedNumber), mm.objCounters[physical])
|
||||||
|
@ -207,6 +207,7 @@ func shardWithMetrics(t *testing.T, path string) (*shard.Shard, *metricsStore) {
|
||||||
}
|
}
|
||||||
|
|
||||||
sh := shard.New(
|
sh := shard.New(
|
||||||
|
shard.WithID(shard.NewIDFromBytes([]byte{})),
|
||||||
shard.WithBlobStorOptions(blobOpts...),
|
shard.WithBlobStorOptions(blobOpts...),
|
||||||
shard.WithPiloramaOptions(pilorama.WithPath(filepath.Join(path, "pilorama"))),
|
shard.WithPiloramaOptions(pilorama.WithPath(filepath.Join(path, "pilorama"))),
|
||||||
shard.WithMetaBaseOptions(
|
shard.WithMetaBaseOptions(
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
package shard
|
package shard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,7 +28,14 @@ func (p *ToMoveItPrm) SetAddress(addr oid.Address) {
|
||||||
|
|
||||||
// ToMoveIt calls metabase.ToMoveIt method to mark object as relocatable to
|
// ToMoveIt calls metabase.ToMoveIt method to mark object as relocatable to
|
||||||
// another shard.
|
// another shard.
|
||||||
func (s *Shard) ToMoveIt(prm ToMoveItPrm) (ToMoveItRes, error) {
|
func (s *Shard) ToMoveIt(ctx context.Context, prm ToMoveItPrm) (ToMoveItRes, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "Shard.ToMoveIt",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("shard_id", s.ID().String()),
|
||||||
|
attribute.String("address", prm.addr.EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
s.m.RLock()
|
s.m.RLock()
|
||||||
defer s.m.RUnlock()
|
defer s.m.RUnlock()
|
||||||
|
|
||||||
|
@ -37,7 +49,7 @@ func (s *Shard) ToMoveIt(prm ToMoveItPrm) (ToMoveItRes, error) {
|
||||||
var toMovePrm meta.ToMoveItPrm
|
var toMovePrm meta.ToMoveItPrm
|
||||||
toMovePrm.SetAddress(prm.addr)
|
toMovePrm.SetAddress(prm.addr)
|
||||||
|
|
||||||
_, err := s.metaBase.ToMoveIt(toMovePrm)
|
_, err := s.metaBase.ToMoveIt(ctx, toMovePrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Debug(logs.ShardCouldNotMarkObjectForShardRelocationInMetabase,
|
s.log.Debug(logs.ShardCouldNotMarkObjectForShardRelocationInMetabase,
|
||||||
zap.String("error", err.Error()),
|
zap.String("error", err.Error()),
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
package shard
|
package shard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
objectCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
objectCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,7 +34,14 @@ func (p *PutPrm) SetObject(obj *object.Object) {
|
||||||
// did not allow to completely save the object.
|
// did not allow to completely save the object.
|
||||||
//
|
//
|
||||||
// Returns ErrReadOnlyMode error if shard is in "read-only" mode.
|
// Returns ErrReadOnlyMode error if shard is in "read-only" mode.
|
||||||
func (s *Shard) Put(prm PutPrm) (PutRes, error) {
|
func (s *Shard) Put(ctx context.Context, prm PutPrm) (PutRes, error) {
|
||||||
|
ctx, span := tracing.StartSpanFromContext(ctx, "Shard.Put",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("shard_id", s.ID().String()),
|
||||||
|
attribute.String("address", objectCore.AddressOf(prm.obj).EncodeToString()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
s.m.RLock()
|
s.m.RLock()
|
||||||
defer s.m.RUnlock()
|
defer s.m.RUnlock()
|
||||||
|
|
||||||
|
@ -55,7 +66,7 @@ func (s *Shard) Put(prm PutPrm) (PutRes, error) {
|
||||||
// ahead of `Put` by storage engine
|
// ahead of `Put` by storage engine
|
||||||
tryCache := s.hasWriteCache() && !m.NoMetabase()
|
tryCache := s.hasWriteCache() && !m.NoMetabase()
|
||||||
if tryCache {
|
if tryCache {
|
||||||
res, err = s.writeCache.Put(putPrm)
|
res, err = s.writeCache.Put(ctx, putPrm)
|
||||||
}
|
}
|
||||||
if err != nil || !tryCache {
|
if err != nil || !tryCache {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -63,7 +74,7 @@ func (s *Shard) Put(prm PutPrm) (PutRes, error) {
|
||||||
zap.String("err", err.Error()))
|
zap.String("err", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = s.blobStor.Put(putPrm)
|
res, err = s.blobStor.Put(ctx, putPrm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PutRes{}, fmt.Errorf("could not put object to BLOB storage: %w", err)
|
return PutRes{}, fmt.Errorf("could not put object to BLOB storage: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -73,7 +84,7 @@ func (s *Shard) Put(prm PutPrm) (PutRes, error) {
|
||||||
var pPrm meta.PutPrm
|
var pPrm meta.PutPrm
|
||||||
pPrm.SetObject(prm.obj)
|
pPrm.SetObject(prm.obj)
|
||||||
pPrm.SetStorageID(res.StorageID)
|
pPrm.SetStorageID(res.StorageID)
|
||||||
if _, err := s.metaBase.Put(pPrm); err != nil {
|
if _, err := s.metaBase.Put(ctx, pPrm); err != nil {
|
||||||
// may we need to handle this case in a special way
|
// may we need to handle this case in a special way
|
||||||
// since the object has been successfully written to BlobStor
|
// since the object has been successfully written to BlobStor
|
||||||
return PutRes{}, fmt.Errorf("could not put object to metabase: %w", err)
|
return PutRes{}, fmt.Errorf("could not put object to metabase: %w", err)
|
||||||
|
|
|
@ -123,7 +123,7 @@ func (s *Shard) GetRange(ctx context.Context, prm RngPrm) (RngRes, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
skipMeta := prm.skipMeta || s.info.Mode.NoMetabase()
|
skipMeta := prm.skipMeta || s.info.Mode.NoMetabase()
|
||||||
obj, hasMeta, err := s.fetchObjectData(prm.addr, skipMeta, cb, wc)
|
obj, hasMeta, err := s.fetchObjectData(ctx, prm.addr, skipMeta, cb, wc)
|
||||||
|
|
||||||
return RngRes{
|
return RngRes{
|
||||||
obj: obj,
|
obj: obj,
|
||||||
|
|
|
@ -99,7 +99,7 @@ func testShardGetRange(t *testing.T, hasWriteCache bool) {
|
||||||
var putPrm shard.PutPrm
|
var putPrm shard.PutPrm
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
|
|
||||||
_, err := sh.Put(putPrm)
|
_, err := sh.Put(context.Background(), putPrm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var rngPrm shard.RngPrm
|
var rngPrm shard.RngPrm
|
||||||
|
|
|
@ -44,6 +44,7 @@ func TestShardReload(t *testing.T) {
|
||||||
meta.WithEpochState(epochState{})}
|
meta.WithEpochState(epochState{})}
|
||||||
|
|
||||||
opts := []Option{
|
opts := []Option{
|
||||||
|
WithID(NewIDFromBytes([]byte{})),
|
||||||
WithLogger(l),
|
WithLogger(l),
|
||||||
WithBlobStorOptions(blobOpts...),
|
WithBlobStorOptions(blobOpts...),
|
||||||
WithMetaBaseOptions(metaOpts...),
|
WithMetaBaseOptions(metaOpts...),
|
||||||
|
@ -75,7 +76,7 @@ func TestShardReload(t *testing.T) {
|
||||||
checkHasObjects(t, true)
|
checkHasObjects(t, true)
|
||||||
|
|
||||||
t.Run("same config, no-op", func(t *testing.T) {
|
t.Run("same config, no-op", func(t *testing.T) {
|
||||||
require.NoError(t, sh.Reload(opts...))
|
require.NoError(t, sh.Reload(context.Background(), opts...))
|
||||||
checkHasObjects(t, true)
|
checkHasObjects(t, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -86,7 +87,7 @@ func TestShardReload(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
newOpts := newShardOpts(filepath.Join(p, "meta1"), false)
|
newOpts := newShardOpts(filepath.Join(p, "meta1"), false)
|
||||||
require.NoError(t, sh.Reload(newOpts...))
|
require.NoError(t, sh.Reload(context.Background(), newOpts...))
|
||||||
|
|
||||||
checkHasObjects(t, false) // new path, but no resync
|
checkHasObjects(t, false) // new path, but no resync
|
||||||
|
|
||||||
|
@ -97,7 +98,7 @@ func TestShardReload(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
newOpts = newShardOpts(filepath.Join(p, "meta2"), true)
|
newOpts = newShardOpts(filepath.Join(p, "meta2"), true)
|
||||||
require.NoError(t, sh.Reload(newOpts...))
|
require.NoError(t, sh.Reload(context.Background(), newOpts...))
|
||||||
|
|
||||||
checkHasObjects(t, true) // all objects are restored, including the new one
|
checkHasObjects(t, true) // all objects are restored, including the new one
|
||||||
|
|
||||||
|
@ -106,7 +107,7 @@ func TestShardReload(t *testing.T) {
|
||||||
require.NoError(t, os.WriteFile(badPath, []byte{1}, 0))
|
require.NoError(t, os.WriteFile(badPath, []byte{1}, 0))
|
||||||
|
|
||||||
newOpts = newShardOpts(badPath, true)
|
newOpts = newShardOpts(badPath, true)
|
||||||
require.Error(t, sh.Reload(newOpts...))
|
require.Error(t, sh.Reload(context.Background(), newOpts...))
|
||||||
|
|
||||||
// Cleanup is done, no panic.
|
// Cleanup is done, no panic.
|
||||||
obj := newObject()
|
obj := newObject()
|
||||||
|
@ -117,7 +118,7 @@ func TestShardReload(t *testing.T) {
|
||||||
|
|
||||||
// Successive reload produces no undesired effects.
|
// Successive reload produces no undesired effects.
|
||||||
require.NoError(t, os.RemoveAll(badPath))
|
require.NoError(t, os.RemoveAll(badPath))
|
||||||
require.NoError(t, sh.Reload(newOpts...))
|
require.NoError(t, sh.Reload(context.Background(), newOpts...))
|
||||||
|
|
||||||
obj = newObject()
|
obj = newObject()
|
||||||
require.NoError(t, putObject(sh, obj))
|
require.NoError(t, putObject(sh, obj))
|
||||||
|
@ -132,7 +133,7 @@ func putObject(sh *Shard, obj *objectSDK.Object) error {
|
||||||
var prm PutPrm
|
var prm PutPrm
|
||||||
prm.SetObject(obj)
|
prm.SetObject(obj)
|
||||||
|
|
||||||
_, err := sh.Put(prm)
|
_, err := sh.Put(context.Background(), prm)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,17 @@ package shard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrInvalidMagic is returned when dump format is invalid.
|
// ErrInvalidMagic is returned when dump format is invalid.
|
||||||
|
@ -57,8 +61,15 @@ func (r RestoreRes) FailCount() int {
|
||||||
// Restore restores objects from the dump prepared by Dump.
|
// Restore restores objects from the dump prepared by Dump.
|
||||||
//
|
//
|
||||||
// Returns any error encountered.
|
// Returns any error encountered.
|
||||||
func (s *Shard) Restore(prm RestorePrm) (RestoreRes, error) {
|
func (s *Shard) Restore(ctx context.Context, prm RestorePrm) (RestoreRes, error) {
|
||||||
// Disallow changing mode during restore.
|
ctx, span := tracing.StartSpanFromContext(ctx, "Shard.Restore",
|
||||||
|
trace.WithAttributes(
|
||||||
|
attribute.String("shard_id", s.ID().String()),
|
||||||
|
attribute.String("path", prm.path),
|
||||||
|
attribute.Bool("ignore_errors", prm.ignoreErrors),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
s.m.RLock()
|
s.m.RLock()
|
||||||
defer s.m.RUnlock()
|
defer s.m.RUnlock()
|
||||||
|
|
||||||
|
@ -122,7 +133,7 @@ func (s *Shard) Restore(prm RestorePrm) (RestoreRes, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
putPrm.SetObject(obj)
|
putPrm.SetObject(obj)
|
||||||
_, err = s.Put(putPrm)
|
_, err = s.Put(ctx, putPrm)
|
||||||
if err != nil && !IsErrObjectExpired(err) && !IsErrRemoved(err) {
|
if err != nil && !IsErrObjectExpired(err) && !IsErrRemoved(err) {
|
||||||
return RestoreRes{}, err
|
return RestoreRes{}, err
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue