forked from TrueCloudLab/frostfs-node
dadfd90dcd
Initial public review release v0.10.0
481 lines
11 KiB
Go
481 lines
11 KiB
Go
package object
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"sync"
|
|
|
|
"github.com/nspcc-dev/neofs-api-go/hash"
|
|
"github.com/nspcc-dev/neofs-api-go/object"
|
|
"github.com/nspcc-dev/neofs-api-go/service"
|
|
"github.com/nspcc-dev/neofs-node/internal"
|
|
"github.com/nspcc-dev/neofs-node/lib/objio"
|
|
"github.com/nspcc-dev/neofs-node/lib/transport"
|
|
"github.com/pkg/errors"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type (
|
|
// Range is a type alias of
|
|
// Range from object package of neofs-api-go.
|
|
Range = object.Range
|
|
|
|
// RangeDescriptor is a type alias of
|
|
// RangeDescriptor from objio package.
|
|
RangeDescriptor = objio.RangeDescriptor
|
|
|
|
// RangeChopper is a type alias of
|
|
// RangeChopper from objio package.
|
|
RangeChopper = objio.RangeChopper
|
|
|
|
// GetRangeRequest is a type alias of
|
|
// GetRangeRequest from object package of neofs-api-go.
|
|
GetRangeRequest = object.GetRangeRequest
|
|
|
|
// GetRangeResponse is a type alias of
|
|
// GetRangeResponse from object package of neofs-api-go.
|
|
GetRangeResponse = object.GetRangeResponse
|
|
|
|
// GetRangeHashRequest is a type alias of
|
|
// GetRangeResponse from object package of neofs-api-go.
|
|
GetRangeHashRequest = object.GetRangeHashRequest
|
|
|
|
// GetRangeHashResponse is a type alias of
|
|
// GetRangeHashResponse from object package of neofs-api-go.
|
|
GetRangeHashResponse = object.GetRangeHashResponse
|
|
|
|
objectRangeReceiver interface {
|
|
getRange(context.Context, rangeTool) (interface{}, error)
|
|
}
|
|
|
|
rangeTool interface {
|
|
transport.RangeHashInfo
|
|
budOff(*RangeDescriptor) rangeTool
|
|
handler() rangeItemAccumulator
|
|
}
|
|
|
|
rawRangeInfo struct {
|
|
*rawAddrInfo
|
|
rng Range
|
|
}
|
|
|
|
rawRangeHashInfo struct {
|
|
*rawAddrInfo
|
|
rngList []Range
|
|
salt []byte
|
|
}
|
|
|
|
coreRangeReceiver struct {
|
|
rngRevealer rangeRevealer
|
|
straightRngRecv objectRangeReceiver
|
|
|
|
// Set of errors that won't be converted into errPayloadRangeNotFound
|
|
mErr map[error]struct{}
|
|
|
|
log *zap.Logger
|
|
}
|
|
|
|
straightRangeReceiver struct {
|
|
executor operationExecutor
|
|
}
|
|
|
|
singleItemHandler struct {
|
|
*sync.Once
|
|
item interface{}
|
|
}
|
|
|
|
rangeItemAccumulator interface {
|
|
responseItemHandler
|
|
collect() (interface{}, error)
|
|
}
|
|
|
|
rangeHashAccum struct {
|
|
concat bool
|
|
h []Hash
|
|
}
|
|
|
|
rangeRevealer interface {
|
|
reveal(context.Context, *RangeDescriptor) ([]RangeDescriptor, error)
|
|
}
|
|
|
|
coreRngRevealer struct {
|
|
relativeRecv objio.RelativeReceiver
|
|
chopTable objio.ChopperTable
|
|
}
|
|
|
|
getRangeServerWriter struct {
|
|
req *GetRangeRequest
|
|
|
|
srv object.Service_GetRangeServer
|
|
|
|
respPreparer responsePreparer
|
|
}
|
|
)
|
|
|
|
const (
|
|
emGetRangeFail = "could get object range #%d part #%d"
|
|
emRangeRevealFail = "could not reveal object range #%d"
|
|
emRangeCollect = "could not collect result of object range #%d"
|
|
|
|
errRangeReveal = internal.Error("could not reveal payload range")
|
|
)
|
|
|
|
var (
|
|
_ transport.RangeInfo = (*rawRangeInfo)(nil)
|
|
_ rangeTool = (*rawRangeHashInfo)(nil)
|
|
_ rangeTool = (*transportRequest)(nil)
|
|
_ rangeItemAccumulator = (*rangeHashAccum)(nil)
|
|
_ rangeItemAccumulator = (*singleItemHandler)(nil)
|
|
_ rangeRevealer = (*coreRngRevealer)(nil)
|
|
_ objectRangeReceiver = (*coreRangeReceiver)(nil)
|
|
_ objectRangeReceiver = (*straightRangeReceiver)(nil)
|
|
_ io.Writer = (*getRangeServerWriter)(nil)
|
|
_ transport.RangeInfo = (*transportRequest)(nil)
|
|
)
|
|
|
|
func (s *objectService) GetRange(req *GetRangeRequest, srv object.Service_GetRangeServer) (err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
s.log.Error(panicLogMsg,
|
|
zap.Stringer("request", object.RequestRange),
|
|
zap.Any("reason", r),
|
|
)
|
|
|
|
err = errServerPanic
|
|
}
|
|
|
|
err = s.statusCalculator.make(requestError{
|
|
t: object.RequestRange,
|
|
e: err,
|
|
})
|
|
}()
|
|
|
|
var r interface{}
|
|
|
|
if r, err = s.requestHandler.handleRequest(srv.Context(), handleRequestParams{
|
|
request: req,
|
|
executor: s,
|
|
}); err == nil {
|
|
_, err = io.CopyBuffer(
|
|
&getRangeServerWriter{
|
|
req: req,
|
|
srv: srv,
|
|
respPreparer: s.rangeChunkPreparer,
|
|
},
|
|
r.(io.Reader),
|
|
make([]byte, maxGetPayloadSize),
|
|
)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (s *objectService) GetRangeHash(ctx context.Context, req *GetRangeHashRequest) (res *GetRangeHashResponse, err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
s.log.Error(panicLogMsg,
|
|
zap.Stringer("request", object.RequestRangeHash),
|
|
zap.Any("reason", r),
|
|
)
|
|
|
|
err = errServerPanic
|
|
}
|
|
|
|
err = s.statusCalculator.make(requestError{
|
|
t: object.RequestRangeHash,
|
|
e: err,
|
|
})
|
|
}()
|
|
|
|
var r interface{}
|
|
|
|
if r, err = s.requestHandler.handleRequest(ctx, handleRequestParams{
|
|
request: req,
|
|
executor: s,
|
|
}); err != nil {
|
|
return
|
|
}
|
|
|
|
res = makeRangeHashResponse(r.([]Hash))
|
|
err = s.respPreparer.prepareResponse(ctx, req, res)
|
|
|
|
return
|
|
}
|
|
|
|
func (s *coreRangeReceiver) getRange(ctx context.Context, rt rangeTool) (res interface{}, err error) {
|
|
defer func() {
|
|
if err != nil {
|
|
if _, ok := s.mErr[errors.Cause(err)]; !ok {
|
|
s.log.Error("get range failure",
|
|
zap.String("error", err.Error()),
|
|
)
|
|
|
|
err = errPayloadRangeNotFound
|
|
}
|
|
}
|
|
}()
|
|
|
|
var (
|
|
subRngSet []RangeDescriptor
|
|
rngSet = rt.GetRanges()
|
|
addr = rt.GetAddress()
|
|
handler = rt.handler()
|
|
)
|
|
|
|
for i := range rngSet {
|
|
rd := RangeDescriptor{
|
|
Size: int64(rngSet[i].Length),
|
|
Offset: int64(rngSet[i].Offset),
|
|
Addr: addr,
|
|
}
|
|
|
|
if rt.GetTTL() < service.NonForwardingTTL {
|
|
subRngSet = []RangeDescriptor{rd}
|
|
} else if subRngSet, err = s.rngRevealer.reveal(ctx, &rd); err != nil {
|
|
return nil, errors.Wrapf(err, emRangeRevealFail, i+1)
|
|
} else if len(subRngSet) == 0 {
|
|
return nil, errRangeReveal
|
|
}
|
|
|
|
subRangeTool := rt.budOff(&rd)
|
|
subHandler := subRangeTool.handler()
|
|
|
|
for j := range subRngSet {
|
|
tool := subRangeTool.budOff(&subRngSet[j])
|
|
|
|
if subRngSet[j].Addr.Equal(&addr) {
|
|
res, err = s.straightRngRecv.getRange(ctx, tool)
|
|
} else {
|
|
res, err = s.getRange(ctx, tool)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, emGetRangeFail, i+1, j+1)
|
|
}
|
|
|
|
subHandler.handleItem(res)
|
|
}
|
|
|
|
rngRes, err := subHandler.collect()
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, emRangeCollect, i+1)
|
|
}
|
|
|
|
handler.handleItem(rngRes)
|
|
}
|
|
|
|
return handler.collect()
|
|
}
|
|
|
|
func (s *straightRangeReceiver) getRange(ctx context.Context, rt rangeTool) (interface{}, error) {
|
|
handler := newSingleItemHandler()
|
|
if err := s.executor.executeOperation(ctx, rt, handler); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return handler.collect()
|
|
}
|
|
|
|
func (s *coreRngRevealer) reveal(ctx context.Context, r *RangeDescriptor) ([]RangeDescriptor, error) {
|
|
chopper, err := s.getChopper(r.Addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return chopper.Chop(ctx, r.Size, r.Offset, true)
|
|
}
|
|
|
|
func (s *coreRngRevealer) getChopper(addr Address) (res RangeChopper, err error) {
|
|
if res, err = s.chopTable.GetChopper(addr, objio.RCCharybdis); err == nil && res.Closed() {
|
|
return
|
|
} else if res, err = s.chopTable.GetChopper(addr, objio.RCScylla); err == nil {
|
|
return
|
|
} else if res, err = objio.NewScylla(&objio.ChopperParams{
|
|
RelativeReceiver: s.relativeRecv,
|
|
Addr: addr,
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_ = s.chopTable.PutChopper(addr, res)
|
|
|
|
return
|
|
}
|
|
|
|
func loopData(data []byte, size, off int64) []byte {
|
|
if len(data) == 0 {
|
|
return make([]byte, 0)
|
|
}
|
|
|
|
res := make([]byte, 0, size)
|
|
|
|
var (
|
|
cut int64
|
|
tail = data[off%int64(len(data)):]
|
|
)
|
|
|
|
for added := int64(0); added < size; added += cut {
|
|
cut = min(int64(len(tail)), size-added)
|
|
res = append(res, tail[:cut]...)
|
|
tail = data
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func min(a, b int64) int64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
|
|
return b
|
|
}
|
|
|
|
func newSingleItemHandler() rangeItemAccumulator { return &singleItemHandler{Once: new(sync.Once)} }
|
|
|
|
func (s *singleItemHandler) handleItem(item interface{}) { s.Do(func() { s.item = item }) }
|
|
|
|
func (s *singleItemHandler) collect() (interface{}, error) { return s.item, nil }
|
|
|
|
func (s *rangeHashAccum) handleItem(h interface{}) {
|
|
if v, ok := h.(Hash); ok {
|
|
s.h = append(s.h, v)
|
|
return
|
|
}
|
|
|
|
s.h = append(s.h, h.([]Hash)...)
|
|
}
|
|
|
|
func (s *rangeHashAccum) collect() (interface{}, error) {
|
|
if s.concat {
|
|
return hash.Concat(s.h)
|
|
}
|
|
|
|
return s.h, nil
|
|
}
|
|
|
|
func (s *rawRangeHashInfo) GetRanges() []Range {
|
|
return s.rngList
|
|
}
|
|
|
|
func (s *rawRangeHashInfo) setRanges(v []Range) {
|
|
s.rngList = v
|
|
}
|
|
|
|
func (s *rawRangeHashInfo) GetSalt() []byte {
|
|
return s.salt
|
|
}
|
|
|
|
func (s *rawRangeHashInfo) setSalt(v []byte) {
|
|
s.salt = v
|
|
}
|
|
|
|
func (s *rawRangeHashInfo) getAddrInfo() *rawAddrInfo {
|
|
return s.rawAddrInfo
|
|
}
|
|
|
|
func (s *rawRangeHashInfo) setAddrInfo(v *rawAddrInfo) {
|
|
s.rawAddrInfo = v
|
|
s.setType(object.RequestRangeHash)
|
|
}
|
|
|
|
func newRawRangeHashInfo() *rawRangeHashInfo {
|
|
res := new(rawRangeHashInfo)
|
|
|
|
res.setAddrInfo(newRawAddressInfo())
|
|
|
|
return res
|
|
}
|
|
|
|
func (s *rawRangeHashInfo) budOff(r *RangeDescriptor) rangeTool {
|
|
res := newRawRangeHashInfo()
|
|
|
|
res.setMetaInfo(s.getMetaInfo())
|
|
res.setAddress(r.Addr)
|
|
res.setRanges([]Range{
|
|
{
|
|
Offset: uint64(r.Offset),
|
|
Length: uint64(r.Size),
|
|
},
|
|
})
|
|
res.setSalt(loopData(s.salt, int64(len(s.salt)), r.Offset))
|
|
res.setSessionToken(s.GetSessionToken())
|
|
res.setBearerToken(s.GetBearerToken())
|
|
res.setExtendedHeaders(s.ExtendedHeaders())
|
|
|
|
return res
|
|
}
|
|
|
|
func (s *rawRangeHashInfo) handler() rangeItemAccumulator { return &rangeHashAccum{concat: true} }
|
|
|
|
func (s *transportRequest) GetRanges() []Range {
|
|
return s.serviceRequest.(*object.GetRangeHashRequest).Ranges
|
|
}
|
|
|
|
func (s *transportRequest) GetSalt() []byte {
|
|
return s.serviceRequest.(*object.GetRangeHashRequest).Salt
|
|
}
|
|
|
|
func (s *transportRequest) budOff(rd *RangeDescriptor) rangeTool {
|
|
res := newRawRangeHashInfo()
|
|
|
|
res.setTTL(s.GetTTL())
|
|
res.setTimeout(s.GetTimeout())
|
|
res.setAddress(rd.Addr)
|
|
res.setRanges([]Range{
|
|
{
|
|
Offset: uint64(rd.Offset),
|
|
Length: uint64(rd.Size),
|
|
},
|
|
})
|
|
res.setSalt(s.serviceRequest.(*object.GetRangeHashRequest).GetSalt())
|
|
res.setSessionToken(s.GetSessionToken())
|
|
res.setBearerToken(s.GetBearerToken())
|
|
res.setExtendedHeaders(s.ExtendedHeaders())
|
|
|
|
return res
|
|
}
|
|
|
|
func (s *transportRequest) handler() rangeItemAccumulator { return new(rangeHashAccum) }
|
|
|
|
func (s *getRangeServerWriter) Write(p []byte) (int, error) {
|
|
resp := makeRangeResponse(p)
|
|
if err := s.respPreparer.prepareResponse(s.srv.Context(), s.req, resp); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if err := s.srv.Send(resp); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return len(p), nil
|
|
}
|
|
|
|
func (s *rawRangeInfo) GetRange() Range {
|
|
return s.rng
|
|
}
|
|
|
|
func (s *rawRangeInfo) setRange(rng Range) {
|
|
s.rng = rng
|
|
}
|
|
|
|
func (s *rawRangeInfo) getAddrInfo() *rawAddrInfo {
|
|
return s.rawAddrInfo
|
|
}
|
|
|
|
func (s *rawRangeInfo) setAddrInfo(v *rawAddrInfo) {
|
|
s.rawAddrInfo = v
|
|
s.setType(object.RequestRange)
|
|
}
|
|
|
|
func newRawRangeInfo() *rawRangeInfo {
|
|
res := new(rawRangeInfo)
|
|
|
|
res.setAddrInfo(newRawAddressInfo())
|
|
|
|
return res
|
|
}
|
|
|
|
func (s *transportRequest) GetRange() Range {
|
|
return s.serviceRequest.(*GetRangeRequest).Range
|
|
}
|