forked from TrueCloudLab/frostfs-node
287 lines
6.4 KiB
Go
287 lines
6.4 KiB
Go
|
package object
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"time"
|
||
|
|
||
|
"github.com/multiformats/go-multiaddr"
|
||
|
"github.com/nspcc-dev/neofs-api-go/object"
|
||
|
v1 "github.com/nspcc-dev/neofs-api-go/query"
|
||
|
"github.com/nspcc-dev/neofs-api-go/service"
|
||
|
"github.com/nspcc-dev/neofs-node/internal"
|
||
|
"github.com/nspcc-dev/neofs-node/lib/implementations"
|
||
|
"github.com/nspcc-dev/neofs-node/lib/objio"
|
||
|
"github.com/nspcc-dev/neofs-node/lib/transport"
|
||
|
"go.uber.org/zap"
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
objectChildrenLister interface {
|
||
|
children(context.Context, Address) []ID
|
||
|
}
|
||
|
|
||
|
coreChildrenLister struct {
|
||
|
queryFn relationQueryFunc
|
||
|
objSearcher objectSearcher
|
||
|
log *zap.Logger
|
||
|
timeout time.Duration
|
||
|
}
|
||
|
|
||
|
relationQueryFunc func(Address) ([]byte, error)
|
||
|
|
||
|
rawSearchInfo struct {
|
||
|
*rawMetaInfo
|
||
|
cid CID
|
||
|
query []byte
|
||
|
}
|
||
|
|
||
|
neighborReceiver struct {
|
||
|
firstChildQueryFn relationQueryFunc
|
||
|
leftNeighborQueryFn relationQueryFunc
|
||
|
rightNeighborQueryFn relationQueryFunc
|
||
|
rangeDescRecv selectiveRangeReceiver
|
||
|
}
|
||
|
|
||
|
selectiveRangeReceiver interface {
|
||
|
rangeDescriptor(context.Context, Address, relationQueryFunc) (RangeDescriptor, error)
|
||
|
}
|
||
|
|
||
|
selectiveRangeRecv struct {
|
||
|
executor implementations.SelectiveContainerExecutor
|
||
|
}
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
lmQueryMarshalFail = "marshal search query failure"
|
||
|
lmListFail = "searching inside children listing failure"
|
||
|
|
||
|
errRelationNotFound = internal.Error("relation not found")
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
_ relationQueryFunc = coreChildrenQueryFunc
|
||
|
_ transport.SearchInfo = (*rawSearchInfo)(nil)
|
||
|
_ objectChildrenLister = (*coreChildrenLister)(nil)
|
||
|
_ objio.RelativeReceiver = (*neighborReceiver)(nil)
|
||
|
_ selectiveRangeReceiver = (*selectiveRangeRecv)(nil)
|
||
|
)
|
||
|
|
||
|
func (s *neighborReceiver) Base(ctx context.Context, addr Address) (RangeDescriptor, error) {
|
||
|
if res, err := s.rangeDescRecv.rangeDescriptor(ctx, addr, s.firstChildQueryFn); err == nil {
|
||
|
return res, nil
|
||
|
}
|
||
|
|
||
|
return s.rangeDescRecv.rangeDescriptor(ctx, addr, nil)
|
||
|
}
|
||
|
|
||
|
func (s *neighborReceiver) Neighbor(ctx context.Context, addr Address, left bool) (res RangeDescriptor, err error) {
|
||
|
if left {
|
||
|
res, err = s.rangeDescRecv.rangeDescriptor(ctx, addr, s.leftNeighborQueryFn)
|
||
|
} else {
|
||
|
res, err = s.rangeDescRecv.rangeDescriptor(ctx, addr, s.rightNeighborQueryFn)
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (s *selectiveRangeRecv) rangeDescriptor(ctx context.Context, addr Address, fn relationQueryFunc) (res RangeDescriptor, err error) {
|
||
|
b := false
|
||
|
|
||
|
p := &implementations.HeadParams{
|
||
|
GetParams: implementations.GetParams{
|
||
|
SelectiveParams: implementations.SelectiveParams{
|
||
|
CID: addr.CID,
|
||
|
ServeLocal: true,
|
||
|
TTL: service.SingleForwardingTTL,
|
||
|
Token: tokenFromContext(ctx),
|
||
|
Bearer: bearerFromContext(ctx),
|
||
|
|
||
|
ExtendedHeaders: extendedHeadersFromContext(ctx),
|
||
|
},
|
||
|
Handler: func(_ multiaddr.Multiaddr, obj *Object) {
|
||
|
res.Addr = *obj.Address()
|
||
|
res.Offset = 0
|
||
|
res.Size = int64(obj.SystemHeader.PayloadLength)
|
||
|
|
||
|
sameID := res.Addr.ObjectID.Equal(addr.ObjectID)
|
||
|
bound := boundaryChild(obj)
|
||
|
res.LeftBound = sameID || bound == boundBoth || bound == boundLeft
|
||
|
res.RightBound = sameID || bound == boundBoth || bound == boundRight
|
||
|
|
||
|
b = true
|
||
|
},
|
||
|
},
|
||
|
FullHeaders: true,
|
||
|
}
|
||
|
|
||
|
if fn != nil {
|
||
|
if p.Query, err = fn(addr); err != nil {
|
||
|
return
|
||
|
}
|
||
|
} else {
|
||
|
p.IDList = []ID{addr.ObjectID}
|
||
|
}
|
||
|
|
||
|
if err = s.executor.Head(ctx, p); err != nil {
|
||
|
return
|
||
|
} else if !b {
|
||
|
err = errRelationNotFound
|
||
|
}
|
||
|
|
||
|
return res, err
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
boundBoth = iota
|
||
|
boundLeft
|
||
|
boundRight
|
||
|
boundMid
|
||
|
)
|
||
|
|
||
|
func boundaryChild(obj *Object) (res int) {
|
||
|
splitInd, _ := obj.LastHeader(object.HeaderType(object.TransformHdr))
|
||
|
if splitInd < 0 {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for i := len(obj.Headers) - 1; i > splitInd; i-- {
|
||
|
hVal := obj.Headers[i].GetValue()
|
||
|
if hVal == nil {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
hLink, ok := hVal.(*object.Header_Link)
|
||
|
if !ok || hLink == nil || hLink.Link == nil {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
linkType := hLink.Link.GetType()
|
||
|
if linkType != object.Link_Previous && linkType != object.Link_Next {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
res = boundMid
|
||
|
|
||
|
if hLink.Link.ID.Empty() {
|
||
|
if linkType == object.Link_Next {
|
||
|
res = boundRight
|
||
|
} else if linkType == object.Link_Previous {
|
||
|
res = boundLeft
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
func firstChildQueryFunc(addr Address) ([]byte, error) {
|
||
|
return (&v1.Query{
|
||
|
Filters: append(parentFilters(addr), QueryFilter{
|
||
|
Type: v1.Filter_Exact,
|
||
|
Name: KeyPrev,
|
||
|
Value: ID{}.String(),
|
||
|
}),
|
||
|
}).Marshal()
|
||
|
}
|
||
|
|
||
|
func leftNeighborQueryFunc(addr Address) ([]byte, error) {
|
||
|
return idQueryFunc(KeyNext, addr.ObjectID)
|
||
|
}
|
||
|
|
||
|
func rightNeighborQueryFunc(addr Address) ([]byte, error) {
|
||
|
return idQueryFunc(KeyPrev, addr.ObjectID)
|
||
|
}
|
||
|
|
||
|
func idQueryFunc(key string, id ID) ([]byte, error) {
|
||
|
return (&v1.Query{Filters: []QueryFilter{
|
||
|
{
|
||
|
Type: v1.Filter_Exact,
|
||
|
Name: key,
|
||
|
Value: id.String(),
|
||
|
},
|
||
|
}}).Marshal()
|
||
|
}
|
||
|
|
||
|
func coreChildrenQueryFunc(addr Address) ([]byte, error) {
|
||
|
return (&v1.Query{Filters: parentFilters(addr)}).Marshal()
|
||
|
}
|
||
|
|
||
|
func (s *coreChildrenLister) children(ctx context.Context, parent Address) []ID {
|
||
|
query, err := s.queryFn(parent)
|
||
|
if err != nil {
|
||
|
s.log.Error(lmQueryMarshalFail, zap.Error(err))
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
sInfo := newRawSearchInfo()
|
||
|
sInfo.setTTL(service.NonForwardingTTL)
|
||
|
sInfo.setTimeout(s.timeout)
|
||
|
sInfo.setCID(parent.CID)
|
||
|
sInfo.setQuery(query)
|
||
|
sInfo.setSessionToken(tokenFromContext(ctx))
|
||
|
sInfo.setBearerToken(bearerFromContext(ctx))
|
||
|
sInfo.setExtendedHeaders(extendedHeadersFromContext(ctx))
|
||
|
|
||
|
children, err := s.objSearcher.searchObjects(ctx, sInfo)
|
||
|
if err != nil {
|
||
|
s.log.Error(lmListFail, zap.Error(err))
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
res := make([]ID, 0, len(children))
|
||
|
for i := range children {
|
||
|
res = append(res, children[i].ObjectID)
|
||
|
}
|
||
|
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
func (s *rawSearchInfo) GetCID() CID {
|
||
|
return s.cid
|
||
|
}
|
||
|
|
||
|
func (s *rawSearchInfo) setCID(v CID) {
|
||
|
s.cid = v
|
||
|
}
|
||
|
|
||
|
func (s *rawSearchInfo) GetQuery() []byte {
|
||
|
return s.query
|
||
|
}
|
||
|
|
||
|
func (s *rawSearchInfo) setQuery(v []byte) {
|
||
|
s.query = v
|
||
|
}
|
||
|
|
||
|
func (s *rawSearchInfo) getMetaInfo() *rawMetaInfo {
|
||
|
return s.rawMetaInfo
|
||
|
}
|
||
|
|
||
|
func (s *rawSearchInfo) setMetaInfo(v *rawMetaInfo) {
|
||
|
s.rawMetaInfo = v
|
||
|
s.setType(object.RequestSearch)
|
||
|
}
|
||
|
|
||
|
func newRawSearchInfo() *rawSearchInfo {
|
||
|
res := new(rawSearchInfo)
|
||
|
|
||
|
res.setMetaInfo(newRawMetaInfo())
|
||
|
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
func parentFilters(addr Address) []QueryFilter {
|
||
|
return []QueryFilter{
|
||
|
{
|
||
|
Type: v1.Filter_Exact,
|
||
|
Name: transport.KeyHasParent,
|
||
|
},
|
||
|
{
|
||
|
Type: v1.Filter_Exact,
|
||
|
Name: transport.KeyParent,
|
||
|
Value: addr.ObjectID.String(),
|
||
|
},
|
||
|
}
|
||
|
}
|