frostfs-node/services/public/object/ranges.go
alexvanin dadfd90dcd Initial commit
Initial public review release v0.10.0
2020-07-10 17:45:00 +03:00

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
}