frostfs-node/services/public/object/listing.go

287 lines
6.4 KiB
Go
Raw Normal View History

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(),
},
}
}