WIP: Process EC container for Get/GetRange/Head concurrently #1237
9 changed files with 232 additions and 90 deletions
|
@ -104,9 +104,8 @@ const (
|
||||||
GetTryingToAssembleTheECObject = "trying to assemble the ec object..."
|
GetTryingToAssembleTheECObject = "trying to assemble the ec object..."
|
||||||
GetAssemblingSplittedObject = "assembling splitted object..."
|
GetAssemblingSplittedObject = "assembling splitted object..."
|
||||||
GetAssemblingECObject = "assembling erasure-coded object..."
|
GetAssemblingECObject = "assembling erasure-coded object..."
|
||||||
GetUnableToGetAllPartsECObject = "unable to get all parts, continue to reconstruct with existed"
|
GetUnableToGetECPartLocal = "failed to get EC part from local storage"
|
||||||
GetUnableToGetPartECObject = "unable to get part of the erasure-encoded object"
|
GetUnableToGetECPartRemote = "failed to get EC part from remote node"
|
||||||
GetUnableToHeadPartECObject = "unable to head part of the erasure-encoded object"
|
|
||||||
GetECObjectInRepContainer = "found erasure-coded object in REP container"
|
GetECObjectInRepContainer = "found erasure-coded object in REP container"
|
||||||
GetAssemblingSplittedObjectCompleted = "assembling splitted object completed"
|
GetAssemblingSplittedObjectCompleted = "assembling splitted object completed"
|
||||||
GetAssemblingECObjectCompleted = "assembling erasure-coded object completed"
|
GetAssemblingECObjectCompleted = "assembling erasure-coded object completed"
|
||||||
|
|
|
@ -140,7 +140,8 @@ func (r *request) getObjectWithIndependentRequest(ctx context.Context, prm Reque
|
||||||
|
|
||||||
prm: prm,
|
prm: prm,
|
||||||
infoSplit: objectSDK.NewSplitInfo(),
|
infoSplit: objectSDK.NewSplitInfo(),
|
||||||
infoEC: objectSDK.NewECInfo(),
|
infoEC: make(map[uint32]objectSDK.ECChunk),
|
||||||
|
partsEC: make(map[uint32]*objectSDK.Object),
|
||||||
log: r.log,
|
log: r.log,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (r *request) assembleEC(ctx context.Context) {
|
||||||
r.log.Debug(logs.GetTryingToAssembleTheECObject)
|
r.log.Debug(logs.GetTryingToAssembleTheECObject)
|
||||||
|
|
||||||
r.prm.common = r.prm.common.WithLocalOnly(false)
|
r.prm.common = r.prm.common.WithLocalOnly(false)
|
||||||
assembler := newAssemblerEC(r.address(), r.infoEC, r.ctxRange(), r, r.containerSource, r.log)
|
assembler := newAssemblerEC(r.address(), r.partsEC, r.infoEC, r.ctxRange(), r.headOnly(), r.containerSource)
|
||||||
|
|
||||||
r.log.Debug(logs.GetAssemblingECObject,
|
r.log.Debug(logs.GetAssemblingECObject,
|
||||||
zap.Uint64("range_offset", r.ctxRange().GetOffset()),
|
zap.Uint64("range_offset", r.ctxRange().GetOffset()),
|
||||||
|
@ -47,7 +47,7 @@ func (r *request) assembleEC(ctx context.Context) {
|
||||||
zap.Uint64("range_length", r.ctxRange().GetLength()),
|
zap.Uint64("range_length", r.ctxRange().GetLength()),
|
||||||
)
|
)
|
||||||
|
|
||||||
obj, err := assembler.Assemble(ctx, r.prm.objWriter, r.headOnly())
|
obj, err := assembler.Assemble(ctx, r.prm.objWriter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.log.Warn(logs.GetFailedToAssembleECObject,
|
r.log.Warn(logs.GetFailedToAssembleECObject,
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
|
|
|
@ -2,51 +2,46 @@ package getsvc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/policy"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/policy"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
|
||||||
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"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/erasurecode"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/erasurecode"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"go.uber.org/zap"
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type assemblerec struct {
|
type assemblerec struct {
|
||||||
addr oid.Address
|
addr oid.Address
|
||||||
ecInfo *objectSDK.ECInfo
|
ecParts map[uint32]*objectSDK.Object
|
||||||
rng *objectSDK.Range
|
ecChunks map[uint32]objectSDK.ECChunk
|
||||||
objGetter objectGetter
|
rng *objectSDK.Range
|
||||||
cs container.Source
|
head bool
|
||||||
log *logger.Logger
|
cs container.Source
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAssemblerEC(
|
func newAssemblerEC(
|
||||||
addr oid.Address,
|
addr oid.Address,
|
||||||
ecInfo *objectSDK.ECInfo,
|
ecParts map[uint32]*objectSDK.Object,
|
||||||
|
ecChunks map[uint32]objectSDK.ECChunk,
|
||||||
rng *objectSDK.Range,
|
rng *objectSDK.Range,
|
||||||
objGetter objectGetter,
|
head bool,
|
||||||
cs container.Source,
|
cs container.Source,
|
||||||
log *logger.Logger,
|
|
||||||
) *assemblerec {
|
) *assemblerec {
|
||||||
return &assemblerec{
|
return &assemblerec{
|
||||||
addr: addr,
|
addr: addr,
|
||||||
rng: rng,
|
rng: rng,
|
||||||
ecInfo: ecInfo,
|
head: head,
|
||||||
objGetter: objGetter,
|
ecParts: ecParts,
|
||||||
cs: cs,
|
ecChunks: ecChunks,
|
||||||
log: log,
|
cs: cs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble assembles erasure-coded object and writes it's content to ObjectWriter.
|
// Assemble assembles erasure-coded object and writes it's content to ObjectWriter.
|
||||||
// It returns parent object.
|
// It returns parent object.
|
||||||
func (a *assemblerec) Assemble(ctx context.Context, writer ObjectWriter, headOnly bool) (*objectSDK.Object, error) {
|
func (a *assemblerec) Assemble(ctx context.Context, writer ObjectWriter) (*objectSDK.Object, error) {
|
||||||
if headOnly {
|
if a.head {
|
||||||
return a.reconstructHeader(ctx, writer)
|
return a.reconstructHeader(ctx, writer)
|
||||||
} else if a.rng != nil {
|
} else if a.rng != nil {
|
||||||
return a.reconstructRange(ctx, writer)
|
return a.reconstructRange(ctx, writer)
|
||||||
|
@ -65,7 +60,7 @@ func (a *assemblerec) getConstructor() (*erasurecode.Constructor, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *assemblerec) reconstructHeader(ctx context.Context, writer ObjectWriter) (*objectSDK.Object, error) {
|
func (a *assemblerec) reconstructHeader(ctx context.Context, writer ObjectWriter) (*objectSDK.Object, error) {
|
||||||
parts := a.retrieveParts(ctx, true)
|
parts := a.retrieveParts()
|
||||||
c, err := a.getConstructor()
|
c, err := a.getConstructor()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -78,7 +73,7 @@ func (a *assemblerec) reconstructHeader(ctx context.Context, writer ObjectWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *assemblerec) reconstructRange(ctx context.Context, writer ObjectWriter) (*objectSDK.Object, error) {
|
func (a *assemblerec) reconstructRange(ctx context.Context, writer ObjectWriter) (*objectSDK.Object, error) {
|
||||||
parts := a.retrieveParts(ctx, false)
|
parts := a.retrieveParts()
|
||||||
c, err := a.getConstructor()
|
c, err := a.getConstructor()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -101,7 +96,7 @@ func (a *assemblerec) reconstructRange(ctx context.Context, writer ObjectWriter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *assemblerec) reconstructObject(ctx context.Context, writer ObjectWriter) (*objectSDK.Object, error) {
|
func (a *assemblerec) reconstructObject(ctx context.Context, writer ObjectWriter) (*objectSDK.Object, error) {
|
||||||
parts := a.retrieveParts(ctx, false)
|
parts := a.retrieveParts()
|
||||||
c, err := a.getConstructor()
|
c, err := a.getConstructor()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -119,41 +114,17 @@ func (a *assemblerec) reconstructObject(ctx context.Context, writer ObjectWriter
|
||||||
return obj, err
|
return obj, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *assemblerec) retrieveParts(mainCtx context.Context, headOnly bool) []*objectSDK.Object {
|
func (a *assemblerec) retrieveParts() []*objectSDK.Object {
|
||||||
parts := make([]*objectSDK.Object, int(a.ecInfo.Chunks[0].Total))
|
parts := make([]*objectSDK.Object, a.getTotalCount())
|
||||||
errGroup, ctx := errgroup.WithContext(mainCtx)
|
for idx := range parts {
|
||||||
|
parts[idx] = a.ecParts[uint32(idx)]
|
||||||
for i := range a.ecInfo.Chunks {
|
|
||||||
chunk := a.ecInfo.Chunks[i]
|
|
||||||
errGroup.Go(func() error {
|
|
||||||
objID := new(oid.ID)
|
|
||||||
err := objID.ReadFromV2(chunk.ID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid object ID: %w", err)
|
|
||||||
}
|
|
||||||
var obj *objectSDK.Object
|
|
||||||
if headOnly {
|
|
||||||
obj, err = a.objGetter.HeadObject(ctx, *objID)
|
|
||||||
if err != nil {
|
|
||||||
a.log.Debug(logs.GetUnableToHeadPartECObject, zap.Stringer("part_id", objID), zap.Error(err))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sow := NewSimpleObjectWriter()
|
|
||||||
obj, err = a.objGetter.GetObjectAndWritePayload(ctx, *objID, nil, sow)
|
|
||||||
if err != nil {
|
|
||||||
a.log.Debug(logs.GetUnableToGetPartECObject, zap.Stringer("part_id", objID), zap.Error(err))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
obj.SetPayload(sow.pld)
|
|
||||||
}
|
|
||||||
parts[chunk.Index] = obj
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := errGroup.Wait(); err != nil {
|
|
||||||
a.log.Debug(logs.GetUnableToGetAllPartsECObject, zap.Error(err))
|
|
||||||
}
|
}
|
||||||
return parts
|
return parts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *assemblerec) getTotalCount() uint32 {
|
||||||
|
for _, ch := range a.ecChunks {
|
||||||
|
return ch.Total
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
|
@ -4,16 +4,17 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/client"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/policy"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/policy"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object_manager/placement"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object_manager/placement"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
||||||
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"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
@ -69,7 +70,7 @@ func (r *request) processCurrentEpoch(ctx context.Context) bool {
|
||||||
r.status = statusUndefined
|
r.status = statusUndefined
|
||||||
|
|
||||||
if policy.IsECPlacement(cnr.Value.PlacementPolicy()) {
|
if policy.IsECPlacement(cnr.Value.PlacementPolicy()) {
|
||||||
return r.processECNodes(ctx, traverser, policy.ECDataCount(cnr.Value.PlacementPolicy()))
|
return r.processECNodes(ctx, traverser, policy.ECDataCount(cnr.Value.PlacementPolicy()), policy.ECParityCount(cnr.Value.PlacementPolicy()))
|
||||||
}
|
}
|
||||||
return r.processRepNodes(ctx, traverser)
|
return r.processRepNodes(ctx, traverser)
|
||||||
}
|
}
|
||||||
|
@ -109,10 +110,17 @@ func (r *request) processRepNodes(ctx context.Context, traverser *placement.Trav
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *request) processECNodes(ctx context.Context, traverser *placement.Traverser, dataCount int) bool {
|
func (r *request) processECNodes(ctx context.Context, traverser *placement.Traverser, dataCount, parityCount int) bool {
|
||||||
ctx, span := tracing.StartSpanFromContext(ctx, "getService.processECNodes")
|
ctx, span := tracing.StartSpanFromContext(ctx, "getService.processECNodes")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
|
if !r.isRaw() && len(r.partsEC) >= dataCount {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if r.isRaw() && len(r.infoEC) == dataCount+parityCount {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
err := r.traverseECNodes(ctx, traverser, dataCount)
|
err := r.traverseECNodes(ctx, traverser, dataCount)
|
||||||
|
|
||||||
var errSplitInfo *objectSDK.SplitInfoError
|
var errSplitInfo *objectSDK.SplitInfoError
|
||||||
|
@ -123,9 +131,9 @@ func (r *request) processECNodes(ctx context.Context, traverser *placement.Trave
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case err == nil: // nil is returned if all nodes failed or incomplete EC info received
|
case err == nil: // nil is returned if all nodes failed or incomplete EC info received
|
||||||
if len(r.infoEC.Chunks) > 0 {
|
if len(r.infoEC) > 0 {
|
||||||
r.status = statusEC
|
r.status = statusEC
|
||||||
r.err = objectSDK.NewECInfoError(r.infoEC)
|
r.err = r.createECInfoError()
|
||||||
} else {
|
} else {
|
||||||
r.status = statusUndefined
|
r.status = statusUndefined
|
||||||
r.err = new(apistatus.ObjectNotFound)
|
r.err = new(apistatus.ObjectNotFound)
|
||||||
|
@ -187,7 +195,6 @@ func (r *request) traverseECNodes(ctx context.Context, traverser *placement.Trav
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *request) processECNodesRequests(ctx context.Context, nodes <-chan placement.Node, dataCount int) error {
|
func (r *request) processECNodesRequests(ctx context.Context, nodes <-chan placement.Node, dataCount int) error {
|
||||||
var ecInfoGuard sync.Mutex
|
|
||||||
eg, ctx := errgroup.WithContext(ctx)
|
eg, ctx := errgroup.WithContext(ctx)
|
||||||
eg.SetLimit(dataCount)
|
eg.SetLimit(dataCount)
|
||||||
for node := range nodes {
|
for node := range nodes {
|
||||||
|
@ -222,26 +229,82 @@ func (r *request) processECNodesRequests(ctx context.Context, nodes <-chan place
|
||||||
// non EC error found, stop
|
// non EC error found, stop
|
||||||
return err
|
return err
|
||||||
case errors.As(err, &errECInfo):
|
case errors.As(err, &errECInfo):
|
||||||
ecInfoGuard.Lock()
|
|
||||||
defer ecInfoGuard.Unlock()
|
|
||||||
r.infoEC = util.MergeECInfo(errECInfo.ECInfo(), r.infoEC)
|
|
||||||
if r.isRaw() {
|
if r.isRaw() {
|
||||||
if len(r.infoEC.Chunks) == int(r.infoEC.Chunks[0].Total) {
|
return r.appendECChunksWithCheck(errECInfo)
|
||||||
return objectSDK.NewECInfoError(r.infoEC)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
if len(r.infoEC.Chunks) >= dataCount {
|
return r.getECChunksRemote(ctx, errECInfo, info, dataCount)
|
||||||
return objectSDK.NewECInfoError(r.infoEC)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return eg.Wait()
|
return eg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *request) appendECChunksWithCheck(errECInfo *objectSDK.ECInfoError) error {
|
||||||
|
r.ecGuard.Lock()
|
||||||
|
defer r.ecGuard.Unlock()
|
||||||
|
for _, ch := range errECInfo.ECInfo().Chunks {
|
||||||
|
r.infoEC[ch.Index] = objectSDK.ECChunk(ch)
|
||||||
|
}
|
||||||
|
if len(r.infoEC) == int(errECInfo.ECInfo().Chunks[0].Total) {
|
||||||
|
return r.createECInfoError()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *request) getECChunksRemote(ctx context.Context, errECInfo *objectSDK.ECInfoError, info client.NodeInfo, dataCount int) error {
|
||||||
|
for _, ch := range errECInfo.ECInfo().Chunks {
|
||||||
|
var objID oid.ID
|
||||||
|
err := objID.ReadFromV2(ch.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid object ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var address oid.Address
|
||||||
|
address.SetContainer(r.containerID())
|
||||||
|
address.SetObject(objID)
|
||||||
|
var obj *objectSDK.Object
|
||||||
|
if r.headOnly() {
|
||||||
|
obj, err = r.headObjectFromNode(ctx, address, info)
|
||||||
|
} else {
|
||||||
|
obj, err = r.getObjectFromNode(ctx, address, info)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
r.log.Warn(logs.GetUnableToGetECPartRemote, zap.Error(err), zap.Stringer("part_address", address),
|
||||||
|
zap.String("node", hex.EncodeToString(info.PublicKey())))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.appendECChunkAndObjectWithCheck(objectSDK.ECChunk(ch), obj, dataCount); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *request) appendECChunkAndObjectWithCheck(chunk objectSDK.ECChunk, object *objectSDK.Object, dataCount int) error {
|
||||||
|
if object == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
r.ecGuard.Lock()
|
||||||
|
defer r.ecGuard.Unlock()
|
||||||
|
|
||||||
|
r.infoEC[chunk.Index] = chunk
|
||||||
|
r.partsEC[chunk.Index] = object
|
||||||
|
|
||||||
|
if len(r.infoEC) >= dataCount && len(r.partsEC) >= dataCount {
|
||||||
|
return r.createECInfoError()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *request) createECInfoError() error {
|
||||||
|
ecInfo := objectSDK.NewECInfo()
|
||||||
|
for _, chunk := range r.infoEC {
|
||||||
|
ecInfo.AddChunk(objectSDK.ECChunk(chunk))
|
||||||
|
}
|
||||||
|
return objectSDK.NewECInfoError(ecInfo)
|
||||||
|
}
|
||||||
|
|
||||||
type ecGetSuccessErr struct {
|
type ecGetSuccessErr struct {
|
||||||
Object *objectSDK.Object
|
Object *objectSDK.Object
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,8 @@ func (s *Service) get(ctx context.Context, prm RequestParameters) error {
|
||||||
|
|
||||||
prm: prm,
|
prm: prm,
|
||||||
infoSplit: objectSDK.NewSplitInfo(),
|
infoSplit: objectSDK.NewSplitInfo(),
|
||||||
infoEC: objectSDK.NewECInfo(),
|
infoEC: make(map[uint32]objectSDK.ECChunk),
|
||||||
|
partsEC: make(map[uint32]*objectSDK.Object),
|
||||||
log: s.log,
|
log: s.log,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,13 @@ package getsvc
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"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/util"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
||||||
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"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -45,9 +46,19 @@ func (r *request) executeLocal(ctx context.Context) {
|
||||||
mergeSplitInfo(r.splitInfo(), errSplitInfo.SplitInfo())
|
mergeSplitInfo(r.splitInfo(), errSplitInfo.SplitInfo())
|
||||||
r.err = objectSDK.NewSplitInfoError(r.infoSplit)
|
r.err = objectSDK.NewSplitInfoError(r.infoSplit)
|
||||||
case errors.As(err, &errECInfo):
|
case errors.As(err, &errECInfo):
|
||||||
|
if r.isRaw() {
|
||||||
|
r.appendECChunks(errECInfo)
|
||||||
|
r.status = statusEC
|
||||||
|
r.err = r.createECInfoError()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err := r.getECChunksLocal(ctx, errECInfo); err != nil {
|
||||||
|
r.status = statusUndefined
|
||||||
|
r.err = err
|
||||||
|
break
|
||||||
|
}
|
||||||
r.status = statusEC
|
r.status = statusEC
|
||||||
util.MergeECInfo(errECInfo.ECInfo(), r.infoEC)
|
r.err = r.createECInfoError()
|
||||||
r.err = objectSDK.NewECInfoError(r.infoEC)
|
|
||||||
case errors.As(err, &errOutOfRange):
|
case errors.As(err, &errOutOfRange):
|
||||||
r.status = statusOutOfRange
|
r.status = statusOutOfRange
|
||||||
r.err = errOutOfRange
|
r.err = errOutOfRange
|
||||||
|
@ -63,3 +74,49 @@ func (r *request) get(ctx context.Context) (*objectSDK.Object, error) {
|
||||||
}
|
}
|
||||||
return r.localStorage.Get(ctx, r.address())
|
return r.localStorage.Get(ctx, r.address())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *request) appendECChunks(errECInfo *objectSDK.ECInfoError) {
|
||||||
|
r.ecGuard.Lock()
|
||||||
|
defer r.ecGuard.Unlock()
|
||||||
|
for _, ch := range errECInfo.ECInfo().Chunks {
|
||||||
|
r.infoEC[ch.Index] = objectSDK.ECChunk(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *request) appendECChunkAndObject(chunk objectSDK.ECChunk, object *objectSDK.Object) {
|
||||||
|
if object == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.ecGuard.Lock()
|
||||||
|
defer r.ecGuard.Unlock()
|
||||||
|
|
||||||
|
r.infoEC[chunk.Index] = chunk
|
||||||
|
r.partsEC[chunk.Index] = object
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *request) getECChunksLocal(ctx context.Context, errECInfo *objectSDK.ECInfoError) error {
|
||||||
|
for _, ch := range errECInfo.ECInfo().Chunks {
|
||||||
|
var objID oid.ID
|
||||||
|
err := objID.ReadFromV2(ch.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid object ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var address oid.Address
|
||||||
|
address.SetContainer(r.containerID())
|
||||||
|
address.SetObject(objID)
|
||||||
|
var obj *objectSDK.Object
|
||||||
|
if r.headOnly() {
|
||||||
|
obj, err = r.localStorage.Head(ctx, address, false)
|
||||||
|
} else {
|
||||||
|
obj, err = r.localStorage.Get(ctx, address)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
r.log.Warn(logs.GetUnableToGetECPartLocal, zap.Error(err), zap.Stringer("part_address", address))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r.appendECChunkAndObject(objectSDK.ECChunk(ch), obj)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
||||||
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"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -102,3 +103,49 @@ func (r *request) getRemote(ctx context.Context, rs remoteStorage, info client.N
|
||||||
|
|
||||||
return rs.Get(ctx, r.address(), prm)
|
return rs.Get(ctx, r.address(), prm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *request) getObjectFromNode(ctx context.Context, addr oid.Address, info client.NodeInfo) (*objectSDK.Object, error) {
|
||||||
|
rs, err := r.remoteStorageConstructor.Get(info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := r.key()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
prm := RemoteRequestParams{
|
||||||
|
Epoch: r.curProcEpoch,
|
||||||
|
TTL: 1,
|
||||||
|
PrivateKey: key,
|
||||||
|
SessionToken: r.prm.common.SessionToken(),
|
||||||
|
BearerToken: r.prm.common.BearerToken(),
|
||||||
|
XHeaders: r.prm.common.XHeaders(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs.Get(ctx, addr, prm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *request) headObjectFromNode(ctx context.Context, addr oid.Address, info client.NodeInfo) (*objectSDK.Object, error) {
|
||||||
|
rs, err := r.remoteStorageConstructor.Get(info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := r.key()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
prm := RemoteRequestParams{
|
||||||
|
Epoch: r.curProcEpoch,
|
||||||
|
TTL: 1,
|
||||||
|
PrivateKey: key,
|
||||||
|
SessionToken: r.prm.common.SessionToken(),
|
||||||
|
BearerToken: r.prm.common.BearerToken(),
|
||||||
|
XHeaders: r.prm.common.XHeaders(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs.Head(ctx, addr, prm)
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package getsvc
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
clientcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/client"
|
clientcore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/client"
|
||||||
|
@ -23,7 +24,9 @@ type request struct {
|
||||||
|
|
||||||
infoSplit *objectSDK.SplitInfo
|
infoSplit *objectSDK.SplitInfo
|
||||||
|
|
||||||
infoEC *objectSDK.ECInfo
|
ecGuard sync.Mutex
|
||||||
|
infoEC map[uint32]objectSDK.ECChunk
|
||||||
|
partsEC map[uint32]*objectSDK.Object
|
||||||
|
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
|
|
||||||
|
@ -220,7 +223,7 @@ func (r *request) writeCollectedObject(ctx context.Context) {
|
||||||
|
|
||||||
// isForwardingEnabled returns true if common execution
|
// isForwardingEnabled returns true if common execution
|
||||||
// parameters has request forwarding closure set.
|
// parameters has request forwarding closure set.
|
||||||
func (r request) isForwardingEnabled() bool {
|
func (r *request) isForwardingEnabled() bool {
|
||||||
return r.prm.forwarder != nil
|
return r.prm.forwarder != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue