Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 61 additions & 39 deletions pkg/services/object/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ type getECTransport struct {
}

// CopyLocalECPartParentHeaderAndPayload implements [getsvc.GetECRequestTransport].
func (x *getECTransport) CopyLocalECPartParentHeaderAndPayload(ctx context.Context, storage *engine.StorageEngine, partInfo iec.PartInfo) (bool, uint64, uint64, uint64, error) {
func (x *getECTransport) CopyLocalECPartParentHeaderAndPayload(ctx context.Context, storage *engine.StorageEngine, partInfo iec.PartInfo, interceptLens func(gotPartPldLen uint64, gotParentPldLen uint64)) (bool, uint64, error) {
logError := func(msg string, err error) {
x.server.log.Warn(msg, zap.Stringer("container", x.requestContainer), zap.Stringer("parent", x.requestObject),
zap.Int("ruleIdx", partInfo.RuleIndex), zap.Int("partIdx", partInfo.Index), zap.Error(err))
Expand All @@ -353,50 +353,52 @@ func (x *getECTransport) CopyLocalECPartParentHeaderAndPayload(ctx context.Conte
if err != nil {
var splitErr *object.SplitInfoError
if errors.Is(err, apistatus.ErrObjectAlreadyRemoved) || errors.As(err, &splitErr) {
return false, 0, 0, 0, err
return false, 0, err
}
if !errors.Is(err, apistatus.ErrObjectNotFound) {
logError("local storage failure (read EC part)", err)
}
return false, 0, 0, 0, nil
return false, 0, nil
}

defer stream.Close()

_, _, partHdrf, err := iobject.GetNonPayloadFieldBounds(buf[:prefixLen])
if err != nil {
return false, 0, 0, 0, fmt.Errorf("parse first %d bytes of object protobuf: %w", prefixLen, err)
return false, 0, fmt.Errorf("parse first %d bytes of object protobuf: %w", prefixLen, err)
}

partHdrBuf := buf[partHdrf.ValueFrom:partHdrf.To]

typ, err := iobject.GetTypeHeader(partHdrBuf)
if err != nil {
logError("invalid local object header (get type)", err)
return false, 0, 0, 0, nil
return false, 0, nil
}
if typ == object.TypeLink {
return false, 0, 0, 0, getsvc.ErrLinker
return false, 0, getsvc.ErrLinker
}

partPldLen, err := iobject.GetPayloadLengthHeader(partHdrBuf)
if err != nil {
logError("invalid local object header (get payload length)", err)
return false, 0, 0, 0, nil
return false, 0, nil
}

parentIDf, parentSigf, parentHdrf, err := iobject.GetParentNonPayloadFieldBoundsHeader(partHdrBuf)
if err != nil {
logError("invalid local object header (get parent fields)", err)
return false, 0, 0, 0, nil
return false, 0, nil
}

parentPldLen, err := iobject.GetPayloadLengthHeader(partHdrBuf[parentHdrf.ValueFrom:parentHdrf.To])
if err != nil {
logError("invalid local object header (get payload length from parent header)", err)
return false, 0, 0, 0, nil
return false, 0, nil
}

interceptLens(partPldLen, parentPldLen)

var n int

if !parentIDf.IsMissing() {
Expand All @@ -418,13 +420,13 @@ func (x *getECTransport) CopyLocalECPartParentHeaderAndPayload(ctx context.Conte
if err != nil {
var e copyReadError
if !errors.As(err, &e) {
return false, 0, 0, 0, err
return false, 0, err
}
logError("local storage stream failure (read EC part)", err)
return true, parentPldLen, partPldLen, uint64(e.written), nil
return true, uint64(e.written), nil
}

return true, parentPldLen, partPldLen, partPldLen, nil
return true, partPldLen, nil
}

// CopyLocalECPartRange implements [getsvc.GetECRequestTransport].
Expand Down Expand Up @@ -495,9 +497,8 @@ func (x *getECTransport) initGetPartRequest(partInfo iec.PartInfo) error {
}

// CopyRemoteECPartParentHeaderAndPayload implements [getsvc.GetECRequestTransport].
func (x *getECTransport) CopyRemoteECPartParentHeaderAndPayload(ctx context.Context, conn clientcore.MultiAddressClient, partInfo iec.PartInfo) (bool, uint64, uint64, uint64, error) {
func (x *getECTransport) CopyRemoteECPartParentHeaderAndPayload(ctx context.Context, conn clientcore.MultiAddressClient, partInfo iec.PartInfo, interceptLens func(gotPartPldLen uint64, gotParentPldLen uint64)) (bool, uint64, error) {
var copiedHdr bool
var parentPldLen uint64
var partPldLen uint64
var copiedPartPld uint64

Expand All @@ -508,7 +509,10 @@ func (x *getECTransport) CopyRemoteECPartParentHeaderAndPayload(ctx context.Cont
}

var err error
copiedHdr, parentPldLen, partPldLen, copiedPartPld, err = x.copyRemotePart(ctx, conn)
copiedHdr, copiedPartPld, err = x.copyRemotePart(ctx, conn, func(gotPartPldLen uint64, gotParentPldLen uint64) {
partPldLen = gotPartPldLen
interceptLens(gotPartPldLen, gotParentPldLen)
})
if err != nil {
return err
}
Expand Down Expand Up @@ -537,25 +541,25 @@ func (x *getECTransport) CopyRemoteECPartParentHeaderAndPayload(ctx context.Cont
return clientcore.ErrSkipConnection
})
if err != nil && !errors.Is(err, clientcore.ErrAllConnectionsSkipped) {
return false, 0, 0, 0, err
return false, 0, err
}

return copiedHdr, parentPldLen, partPldLen, copiedPartPld, nil
return copiedHdr, copiedPartPld, nil
}

func (x *getECTransport) copyRemotePart(ctx context.Context, conn *grpc.ClientConn) (bool, uint64, uint64, uint64, error) {
func (x *getECTransport) copyRemotePart(ctx context.Context, conn *grpc.ClientConn, interceptLens func(gotPartPldLen uint64, gotParentPldLen uint64)) (bool, uint64, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()

stream, err := callGet(ctx, conn, x.getPartRequest)
if err != nil {
err = igrpc.ConvertContextStatus(err)
if errors.Is(err, ctx.Err()) {
return false, 0, 0, 0, err
return false, 0, err
}
// TODO: if error is due to incorrect request, error should be returned. How to catch this?
x.server.log.Warn("GET object API failure (call)", zap.String("node", conn.Target()), zap.Error(err))
return false, 0, 0, 0, nil
return false, 0, nil
}

var copiedHdr bool
Expand All @@ -569,7 +573,7 @@ func (x *getECTransport) copyRemotePart(ctx context.Context, conn *grpc.ClientCo
if err = stream.RecvMsg(&respBuf); err != nil {
err = igrpc.ConvertContextStatus(err)
if errors.Is(err, ctx.Err()) {
return false, 0, 0, 0, err
return false, 0, err
}
if !errors.Is(err, io.EOF) {
x.server.log.Warn("GET object API failure (receive message)", zap.String("node", conn.Target()), zap.Error(err))
Expand All @@ -580,71 +584,75 @@ func (x *getECTransport) copyRemotePart(ctx context.Context, conn *grpc.ClientCo
code, body, err := handleResponseCodeAndBody(respBuf)
if err != nil {
respBuf.Free()
return false, 0, 0, 0, err
return false, 0, err
}

if code == protostatus.ObjectNotFound {
respBuf.Free()
if headWas {
return false, 0, 0, 0, errors.New("received object not found status after header")
return false, 0, errors.New("received object not found status after header")
}
return false, 0, 0, 0, nil
return false, 0, nil
}

if code != protostatus.OK {
if err = x.responseStream.SendMsg(respBuf); err != nil {
return false, 0, 0, 0, fmt.Errorf("%w: %w", getsvc.ErrResponseStreamFailure, err)
return false, 0, fmt.Errorf("%w: %w", getsvc.ErrResponseStreamFailure, err)
}
return false, 0, 0, 0, getsvc.ErrResponded
return false, 0, getsvc.ErrResponded
}

num, fld, err := handleGetResponseBodyOneof(&headWas, body)
if err != nil {
respBuf.Free()
return false, 0, 0, 0, err
return false, 0, err
}

switch num {
default:
respBuf.Free()
return false, 0, 0, 0, errors.New("none of the supported oneof fields are specified")
return false, 0, errors.New("none of the supported oneof fields are specified")
case protoobject.FieldGetResponseBodyInit:
var parentID, parentSig, parentHdr iprotobuf.BuffersSlice
parentID, parentSig, parentHdr, parentPldLen, partPldLen, err = handleGetECPartResponseInit(fld)
parentID, parentSig, parentHdr, err = handleGetECPartResponseInit(fld, func(gotPartPldLen uint64, gotParentPldLen uint64) {
partPldLen = gotPartPldLen
parentPldLen = gotParentPldLen
interceptLens(gotPartPldLen, gotParentPldLen)
})
if err != nil {
respBuf.Free()
return false, 0, 0, 0, err
return false, 0, err
}

err = x.server.writeInitGetResponseBuffers(x.responseStream, parentID, parentSig, parentHdr, x.signResponses)
respBuf.Free()
if err != nil {
return false, 0, 0, 0, err
return false, 0, err
}

copiedHdr = true
case protoobject.FieldGetResponseBodyChunk:
copiedPartPldLen += uint64(fld.Len())
if copiedPartPldLen > partPldLen {
respBuf.Free()
return false, 0, 0, 0, fmt.Errorf("part payload overflow: full %d bytes, copied %d", partPldLen, copiedPartPldLen)
return false, 0, fmt.Errorf("part payload overflow: full %d bytes, copied %d", partPldLen, copiedPartPldLen)
}
if copiedPartPldLen > parentPldLen {
respBuf.Free()
return false, 0, 0, 0, fmt.Errorf("parent payload overflow: full %d bytes, copied %d", parentPldLen, copiedPartPldLen)
return false, 0, fmt.Errorf("parent payload overflow: full %d bytes, copied %d", parentPldLen, copiedPartPldLen)
}

if err = x.responseStream.SendMsg(respBuf); err != nil {
return false, 0, 0, 0, fmt.Errorf("%w: %w", getsvc.ErrResponseStreamFailure, err)
return false, 0, fmt.Errorf("%w: %w", getsvc.ErrResponseStreamFailure, err)
}
case protoobject.FieldGetResponseBodySplitInfo:
err := handleSplitInfo(fld, true)
respBuf.Free()
return false, 0, 0, 0, err
return false, 0, err
}
}

return copiedHdr, parentPldLen, partPldLen, copiedPartPldLen, nil
return copiedHdr, copiedPartPldLen, nil
}

func (x *getECTransport) copyRemotePartRange(ctx context.Context, conn *grpc.ClientConn, partInfo iec.PartInfo, off, ln uint64, controlCh <-chan bool) (uint64, error) {
Expand Down Expand Up @@ -759,10 +767,12 @@ func (x *getECTransport) copyRemotePartRange(ctx context.Context, conn *grpc.Cli
return copied, nil
}

func handleGetECPartResponseInit(buffers iprotobuf.BuffersSlice) (iprotobuf.BuffersSlice, iprotobuf.BuffersSlice, iprotobuf.BuffersSlice, uint64, uint64, error) {
func handleGetECPartResponseInit(buffers iprotobuf.BuffersSlice, interceptLens func(gotPartPldLen uint64, gotParentPldLen uint64)) (iprotobuf.BuffersSlice, iprotobuf.BuffersSlice, iprotobuf.BuffersSlice, error) {
var parentID, parentSig, parentHdr iprotobuf.BuffersSlice
var parentPldLen uint64
var parentPldLenDone bool
var partPldLen uint64
var partPldLenDone bool

var opts protoscan.ScanMessageOptions
opts.InterceptNested = func(num protowire.Number, buffers iprotobuf.BuffersSlice) error {
Expand All @@ -774,6 +784,10 @@ func handleGetECPartResponseInit(buffers iprotobuf.BuffersSlice) (iprotobuf.Buff
opts.InterceptUint64 = func(num protowire.Number, u uint64) error {
if num == protoobject.FieldHeaderPayloadLength {
partPldLen = u
partPldLenDone = true
if parentPldLenDone {
interceptLens(partPldLen, parentPldLen)
}
}
return nil
}
Expand All @@ -798,6 +812,10 @@ func handleGetECPartResponseInit(buffers iprotobuf.BuffersSlice) (iprotobuf.Buff
opts.InterceptUint64 = func(num protowire.Number, u uint64) error {
if num == protoobject.FieldHeaderPayloadLength {
parentPldLen = u
parentPldLenDone = true
if partPldLenDone {
interceptLens(partPldLen, parentPldLen)
}
}
return nil
}
Expand All @@ -822,10 +840,14 @@ func handleGetECPartResponseInit(buffers iprotobuf.BuffersSlice) (iprotobuf.Buff

err := protoscan.ScanMessage(buffers, protoscan.ObjectGetResponseInitScheme, opts)
if err != nil {
return iprotobuf.BuffersSlice{}, iprotobuf.BuffersSlice{}, iprotobuf.BuffersSlice{}, 0, 0, err
return iprotobuf.BuffersSlice{}, iprotobuf.BuffersSlice{}, iprotobuf.BuffersSlice{}, err
}

if !partPldLenDone || !parentPldLenDone {
interceptLens(partPldLen, parentPldLen)
}

return parentID, parentSig, parentHdr, parentPldLen, partPldLen, nil
return parentID, parentSig, parentHdr, nil
}

func (x *getECTransport) CopyRemoteECPartRange(ctx context.Context, conn clientcore.MultiAddressClient, partInfo iec.PartInfo, off uint64, ln uint64, controlCh <-chan bool) (uint64, error) {
Expand Down
Loading
Loading