forked from TrueCloudLab/frostfs-node
Initial commit
Initial public review release v0.10.0
This commit is contained in:
commit
dadfd90dcd
276 changed files with 46331 additions and 0 deletions
657
lib/implementations/transport.go
Normal file
657
lib/implementations/transport.go
Normal file
|
@ -0,0 +1,657 @@
|
|||
package implementations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
"github.com/nspcc-dev/neofs-api-go/hash"
|
||||
"github.com/nspcc-dev/neofs-api-go/object"
|
||||
"github.com/nspcc-dev/neofs-api-go/refs"
|
||||
"github.com/nspcc-dev/neofs-api-go/service"
|
||||
"github.com/nspcc-dev/neofs-node/internal"
|
||||
"github.com/nspcc-dev/neofs-node/lib/transport"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
/*
|
||||
File source code includes implementation of unified objects container handler.
|
||||
Implementation provides the opportunity to perform any logic over object container distributed in network.
|
||||
Implementation holds placement and object transport implementations in a black box.
|
||||
Any special logic could be tuned through passing handle parameters.
|
||||
NOTE: Although the implementation of the other interfaces via OCH is the same, they are still separated in order to avoid mess.
|
||||
*/
|
||||
|
||||
type (
|
||||
// SelectiveContainerExecutor is an interface the tool that performs
|
||||
// object operations in container with preconditions.
|
||||
SelectiveContainerExecutor interface {
|
||||
Put(context.Context, *PutParams) error
|
||||
Get(context.Context, *GetParams) error
|
||||
Head(context.Context, *HeadParams) error
|
||||
Search(context.Context, *SearchParams) error
|
||||
RangeHash(context.Context, *RangeHashParams) error
|
||||
}
|
||||
|
||||
// PutParams groups the parameters
|
||||
// of selective object Put.
|
||||
PutParams struct {
|
||||
SelectiveParams
|
||||
Object *object.Object
|
||||
Handler func(multiaddr.Multiaddr, bool)
|
||||
|
||||
CopiesNumber uint32
|
||||
}
|
||||
|
||||
// GetParams groups the parameters
|
||||
// of selective object Get.
|
||||
GetParams struct {
|
||||
SelectiveParams
|
||||
Handler func(multiaddr.Multiaddr, *object.Object)
|
||||
}
|
||||
|
||||
// HeadParams groups the parameters
|
||||
// of selective object Head.
|
||||
HeadParams struct {
|
||||
GetParams
|
||||
FullHeaders bool
|
||||
}
|
||||
|
||||
// SearchParams groups the parameters
|
||||
// of selective object Search.
|
||||
SearchParams struct {
|
||||
SelectiveParams
|
||||
SearchCID refs.CID
|
||||
SearchQuery []byte
|
||||
Handler func(multiaddr.Multiaddr, []refs.Address)
|
||||
}
|
||||
|
||||
// RangeHashParams groups the parameters
|
||||
// of selective object GetRangeHash.
|
||||
RangeHashParams struct {
|
||||
SelectiveParams
|
||||
Ranges []object.Range
|
||||
Salt []byte
|
||||
Handler func(multiaddr.Multiaddr, []hash.Hash)
|
||||
}
|
||||
|
||||
// SelectiveParams groups the parameters of
|
||||
// the execution of selective container operation.
|
||||
SelectiveParams struct {
|
||||
/* Should be set to true only if service under object transport implementations is served on localhost. */
|
||||
ServeLocal bool
|
||||
|
||||
/* Raw option of the request */
|
||||
Raw bool
|
||||
|
||||
/* TTL for object transport. All transport operations inherit same value. */
|
||||
TTL uint32
|
||||
|
||||
/* Required ID of processing container. If empty or not set, an error is returned. */
|
||||
CID
|
||||
|
||||
/* List of nodes selected for processing. If not specified => nodes will be selected during. */
|
||||
Nodes []multiaddr.Multiaddr
|
||||
|
||||
/*
|
||||
Next two parameters provide the opportunity to process selective objects in container.
|
||||
At least on of non-empty IDList or Query is required, an error is returned otherwise.
|
||||
*/
|
||||
|
||||
/* List of objects to process (overlaps query). */
|
||||
IDList []refs.ObjectID
|
||||
/* If no objects is indicated, query is used for selection. */
|
||||
Query []byte
|
||||
|
||||
/*
|
||||
If function provided, it is called after every successful operation.
|
||||
True result breaks operation performing.
|
||||
*/
|
||||
Breaker func(refs.Address) ProgressControlFlag
|
||||
|
||||
/* Public session token */
|
||||
Token service.SessionToken
|
||||
|
||||
/* Bearer token */
|
||||
Bearer service.BearerToken
|
||||
|
||||
/* Extended headers */
|
||||
ExtendedHeaders []service.ExtendedHeader
|
||||
}
|
||||
|
||||
// ProgressControlFlag is an enumeration of progress control flags.
|
||||
ProgressControlFlag int
|
||||
|
||||
// ObjectContainerHandlerParams grops the parameters of SelectiveContainerExecutor constructor.
|
||||
ObjectContainerHandlerParams struct {
|
||||
NodeLister ContainerNodesLister
|
||||
Executor ContainerTraverseExecutor
|
||||
*zap.Logger
|
||||
}
|
||||
|
||||
simpleTraverser struct {
|
||||
*sync.Once
|
||||
list []multiaddr.Multiaddr
|
||||
}
|
||||
|
||||
selectiveCnrExec struct {
|
||||
cnl ContainerNodesLister
|
||||
Executor ContainerTraverseExecutor
|
||||
log *zap.Logger
|
||||
}
|
||||
|
||||
metaInfo struct {
|
||||
ttl uint32
|
||||
raw bool
|
||||
rt object.RequestType
|
||||
|
||||
token service.SessionToken
|
||||
|
||||
bearer service.BearerToken
|
||||
|
||||
extHdrs []service.ExtendedHeader
|
||||
}
|
||||
|
||||
putInfo struct {
|
||||
metaInfo
|
||||
obj *object.Object
|
||||
cn uint32
|
||||
}
|
||||
|
||||
getInfo struct {
|
||||
metaInfo
|
||||
addr Address
|
||||
raw bool
|
||||
}
|
||||
|
||||
headInfo struct {
|
||||
getInfo
|
||||
fullHdr bool
|
||||
}
|
||||
|
||||
searchInfo struct {
|
||||
metaInfo
|
||||
cid CID
|
||||
query []byte
|
||||
}
|
||||
|
||||
rangeHashInfo struct {
|
||||
metaInfo
|
||||
addr Address
|
||||
ranges []object.Range
|
||||
salt []byte
|
||||
}
|
||||
|
||||
execItems struct {
|
||||
params SelectiveParams
|
||||
metaConstructor func(addr Address) transport.MetaInfo
|
||||
handler transport.ResultHandler
|
||||
}
|
||||
|
||||
searchTarget struct {
|
||||
list []refs.Address
|
||||
}
|
||||
|
||||
// ContainerTraverseExecutor is an interface of
|
||||
// object operation executor with container traversing.
|
||||
ContainerTraverseExecutor interface {
|
||||
Execute(context.Context, TraverseParams)
|
||||
}
|
||||
|
||||
// TraverseParams groups the parameters of container traversing.
|
||||
TraverseParams struct {
|
||||
TransportInfo transport.MetaInfo
|
||||
Handler transport.ResultHandler
|
||||
Traverser Traverser
|
||||
WorkerPool WorkerPool
|
||||
ExecutionInterceptor func(context.Context, multiaddr.Multiaddr) bool
|
||||
}
|
||||
|
||||
// WorkerPool is an interface of go-routine pool
|
||||
WorkerPool interface {
|
||||
Submit(func()) error
|
||||
}
|
||||
|
||||
// Traverser is an interface of container traverser.
|
||||
Traverser interface {
|
||||
Next(context.Context) []multiaddr.Multiaddr
|
||||
}
|
||||
|
||||
cnrTraverseExec struct {
|
||||
transport transport.ObjectTransport
|
||||
}
|
||||
|
||||
singleRoutinePool struct{}
|
||||
|
||||
emptyReader struct{}
|
||||
)
|
||||
|
||||
const (
|
||||
_ ProgressControlFlag = iota
|
||||
|
||||
// NextAddress is a ProgressControlFlag of to go to the next address of the object.
|
||||
NextAddress
|
||||
|
||||
// NextNode is a ProgressControlFlag of to go to the next node.
|
||||
NextNode
|
||||
|
||||
// BreakProgress is a ProgressControlFlag to interrupt the execution.
|
||||
BreakProgress
|
||||
)
|
||||
|
||||
const (
|
||||
instanceFailMsg = "could not create container objects collector"
|
||||
errEmptyLogger = internal.Error("empty logger")
|
||||
errEmptyNodeLister = internal.Error("empty container node lister")
|
||||
errEmptyTraverseExecutor = internal.Error("empty container traverse executor")
|
||||
|
||||
errSelectiveParams = internal.Error("neither ID list nor query provided")
|
||||
)
|
||||
|
||||
var errNilObjectTransport = errors.New("object transport is nil")
|
||||
|
||||
func (s *selectiveCnrExec) Put(ctx context.Context, p *PutParams) error {
|
||||
meta := &putInfo{
|
||||
metaInfo: metaInfo{
|
||||
ttl: p.TTL,
|
||||
rt: object.RequestPut,
|
||||
raw: p.Raw,
|
||||
|
||||
token: p.Token,
|
||||
|
||||
bearer: p.Bearer,
|
||||
|
||||
extHdrs: p.ExtendedHeaders,
|
||||
},
|
||||
obj: p.Object,
|
||||
cn: p.CopiesNumber,
|
||||
}
|
||||
|
||||
return s.exec(ctx, &execItems{
|
||||
params: p.SelectiveParams,
|
||||
metaConstructor: func(Address) transport.MetaInfo { return meta },
|
||||
handler: p,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *selectiveCnrExec) Get(ctx context.Context, p *GetParams) error {
|
||||
return s.exec(ctx, &execItems{
|
||||
params: p.SelectiveParams,
|
||||
metaConstructor: func(addr Address) transport.MetaInfo {
|
||||
return &getInfo{
|
||||
metaInfo: metaInfo{
|
||||
ttl: p.TTL,
|
||||
rt: object.RequestGet,
|
||||
raw: p.Raw,
|
||||
|
||||
token: p.Token,
|
||||
|
||||
bearer: p.Bearer,
|
||||
|
||||
extHdrs: p.ExtendedHeaders,
|
||||
},
|
||||
addr: addr,
|
||||
raw: p.Raw,
|
||||
}
|
||||
},
|
||||
handler: p,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *selectiveCnrExec) Head(ctx context.Context, p *HeadParams) error {
|
||||
return s.exec(ctx, &execItems{
|
||||
params: p.SelectiveParams,
|
||||
metaConstructor: func(addr Address) transport.MetaInfo {
|
||||
return &headInfo{
|
||||
getInfo: getInfo{
|
||||
metaInfo: metaInfo{
|
||||
ttl: p.TTL,
|
||||
rt: object.RequestHead,
|
||||
raw: p.Raw,
|
||||
|
||||
token: p.Token,
|
||||
|
||||
bearer: p.Bearer,
|
||||
|
||||
extHdrs: p.ExtendedHeaders,
|
||||
},
|
||||
addr: addr,
|
||||
raw: p.Raw,
|
||||
},
|
||||
fullHdr: p.FullHeaders,
|
||||
}
|
||||
},
|
||||
handler: p,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *selectiveCnrExec) Search(ctx context.Context, p *SearchParams) error {
|
||||
return s.exec(ctx, &execItems{
|
||||
params: p.SelectiveParams,
|
||||
metaConstructor: func(Address) transport.MetaInfo {
|
||||
return &searchInfo{
|
||||
metaInfo: metaInfo{
|
||||
ttl: p.TTL,
|
||||
rt: object.RequestSearch,
|
||||
raw: p.Raw,
|
||||
|
||||
token: p.Token,
|
||||
|
||||
bearer: p.Bearer,
|
||||
|
||||
extHdrs: p.ExtendedHeaders,
|
||||
},
|
||||
cid: p.SearchCID,
|
||||
query: p.SearchQuery,
|
||||
}
|
||||
},
|
||||
handler: p,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *selectiveCnrExec) RangeHash(ctx context.Context, p *RangeHashParams) error {
|
||||
return s.exec(ctx, &execItems{
|
||||
params: p.SelectiveParams,
|
||||
metaConstructor: func(addr Address) transport.MetaInfo {
|
||||
return &rangeHashInfo{
|
||||
metaInfo: metaInfo{
|
||||
ttl: p.TTL,
|
||||
rt: object.RequestRangeHash,
|
||||
raw: p.Raw,
|
||||
|
||||
token: p.Token,
|
||||
|
||||
bearer: p.Bearer,
|
||||
|
||||
extHdrs: p.ExtendedHeaders,
|
||||
},
|
||||
addr: addr,
|
||||
ranges: p.Ranges,
|
||||
salt: p.Salt,
|
||||
}
|
||||
},
|
||||
handler: p,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *selectiveCnrExec) exec(ctx context.Context, p *execItems) error {
|
||||
if err := p.params.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nodes, err := s.prepareNodes(ctx, &p.params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loop:
|
||||
for i := range nodes {
|
||||
addrList := s.prepareAddrList(ctx, &p.params, nodes[i])
|
||||
if len(addrList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for j := range addrList {
|
||||
if p.params.Breaker != nil {
|
||||
switch cFlag := p.params.Breaker(addrList[j]); cFlag {
|
||||
case NextAddress:
|
||||
continue
|
||||
case NextNode:
|
||||
continue loop
|
||||
case BreakProgress:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
s.Executor.Execute(ctx, TraverseParams{
|
||||
TransportInfo: p.metaConstructor(addrList[j]),
|
||||
Handler: p.handler,
|
||||
Traverser: newSimpleTraverser(nodes[i]),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SelectiveParams) validate() error {
|
||||
switch {
|
||||
case len(s.IDList) == 0 && len(s.Query) == 0:
|
||||
return errSelectiveParams
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *selectiveCnrExec) prepareNodes(ctx context.Context, p *SelectiveParams) ([]multiaddr.Multiaddr, error) {
|
||||
if len(p.Nodes) > 0 {
|
||||
return p.Nodes, nil
|
||||
}
|
||||
|
||||
// If node serves Object transport service on localhost => pass single empty node
|
||||
if p.ServeLocal {
|
||||
// all transport implementations will use localhost by default
|
||||
return []multiaddr.Multiaddr{nil}, nil
|
||||
}
|
||||
|
||||
// Otherwise use container nodes
|
||||
return s.cnl.ContainerNodes(ctx, p.CID)
|
||||
}
|
||||
|
||||
func (s *selectiveCnrExec) prepareAddrList(ctx context.Context, p *SelectiveParams, node multiaddr.Multiaddr) []refs.Address {
|
||||
var (
|
||||
addrList []Address
|
||||
l = len(p.IDList)
|
||||
)
|
||||
|
||||
if l > 0 {
|
||||
addrList = make([]Address, 0, l)
|
||||
for i := range p.IDList {
|
||||
addrList = append(addrList, Address{CID: p.CID, ObjectID: p.IDList[i]})
|
||||
}
|
||||
|
||||
return addrList
|
||||
}
|
||||
|
||||
handler := new(searchTarget)
|
||||
|
||||
s.Executor.Execute(ctx, TraverseParams{
|
||||
TransportInfo: &searchInfo{
|
||||
metaInfo: metaInfo{
|
||||
ttl: p.TTL,
|
||||
rt: object.RequestSearch,
|
||||
raw: p.Raw,
|
||||
|
||||
token: p.Token,
|
||||
|
||||
bearer: p.Bearer,
|
||||
|
||||
extHdrs: p.ExtendedHeaders,
|
||||
},
|
||||
cid: p.CID,
|
||||
query: p.Query,
|
||||
},
|
||||
Handler: handler,
|
||||
Traverser: newSimpleTraverser(node),
|
||||
})
|
||||
|
||||
return handler.list
|
||||
}
|
||||
|
||||
func newSimpleTraverser(list ...multiaddr.Multiaddr) Traverser {
|
||||
return &simpleTraverser{
|
||||
Once: new(sync.Once),
|
||||
list: list,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simpleTraverser) Next(context.Context) (res []multiaddr.Multiaddr) {
|
||||
s.Do(func() {
|
||||
res = s.list
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s metaInfo) GetTTL() uint32 { return s.ttl }
|
||||
|
||||
func (s metaInfo) GetTimeout() time.Duration { return 0 }
|
||||
|
||||
func (s metaInfo) GetRaw() bool { return s.raw }
|
||||
|
||||
func (s metaInfo) Type() object.RequestType { return s.rt }
|
||||
|
||||
func (s metaInfo) GetSessionToken() service.SessionToken { return s.token }
|
||||
|
||||
func (s metaInfo) GetBearerToken() service.BearerToken { return s.bearer }
|
||||
|
||||
func (s metaInfo) ExtendedHeaders() []service.ExtendedHeader { return s.extHdrs }
|
||||
|
||||
func (s *putInfo) GetHead() *object.Object { return s.obj }
|
||||
|
||||
func (s *putInfo) Payload() io.Reader { return new(emptyReader) }
|
||||
|
||||
func (*emptyReader) Read(p []byte) (int, error) { return 0, io.EOF }
|
||||
|
||||
func (s *putInfo) CopiesNumber() uint32 {
|
||||
return s.cn
|
||||
}
|
||||
|
||||
func (s *getInfo) GetAddress() refs.Address { return s.addr }
|
||||
|
||||
func (s *getInfo) Raw() bool { return s.raw }
|
||||
|
||||
func (s *headInfo) GetFullHeaders() bool { return s.fullHdr }
|
||||
|
||||
func (s *searchInfo) GetCID() refs.CID { return s.cid }
|
||||
|
||||
func (s *searchInfo) GetQuery() []byte { return s.query }
|
||||
|
||||
func (s *rangeHashInfo) GetAddress() refs.Address { return s.addr }
|
||||
|
||||
func (s *rangeHashInfo) GetRanges() []object.Range { return s.ranges }
|
||||
|
||||
func (s *rangeHashInfo) GetSalt() []byte { return s.salt }
|
||||
|
||||
func (s *searchTarget) HandleResult(_ context.Context, _ multiaddr.Multiaddr, r interface{}, e error) {
|
||||
if e == nil {
|
||||
s.list = append(s.list, r.([]refs.Address)...)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleResult calls Handler with:
|
||||
// - Multiaddr with argument value;
|
||||
// - error equality to nil.
|
||||
func (s *PutParams) HandleResult(_ context.Context, node multiaddr.Multiaddr, _ interface{}, e error) {
|
||||
s.Handler(node, e == nil)
|
||||
}
|
||||
|
||||
// HandleResult calls Handler if error argument is nil with:
|
||||
// - Multiaddr with argument value;
|
||||
// - result casted to an Object pointer.
|
||||
func (s *GetParams) HandleResult(_ context.Context, node multiaddr.Multiaddr, r interface{}, e error) {
|
||||
if e == nil {
|
||||
s.Handler(node, r.(*object.Object))
|
||||
}
|
||||
}
|
||||
|
||||
// HandleResult calls Handler if error argument is nil with:
|
||||
// - Multiaddr with argument value;
|
||||
// - result casted to Address slice.
|
||||
func (s *SearchParams) HandleResult(_ context.Context, node multiaddr.Multiaddr, r interface{}, e error) {
|
||||
if e == nil {
|
||||
s.Handler(node, r.([]refs.Address))
|
||||
}
|
||||
}
|
||||
|
||||
// HandleResult calls Handler if error argument is nil with:
|
||||
// - Multiaddr with argument value;
|
||||
// - result casted to Hash slice.
|
||||
func (s *RangeHashParams) HandleResult(_ context.Context, node multiaddr.Multiaddr, r interface{}, e error) {
|
||||
if e == nil {
|
||||
s.Handler(node, r.([]hash.Hash))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *cnrTraverseExec) Execute(ctx context.Context, p TraverseParams) {
|
||||
if p.WorkerPool == nil {
|
||||
p.WorkerPool = new(singleRoutinePool)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
nodes := p.Traverser.Next(ctx)
|
||||
if len(nodes) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for i := range nodes {
|
||||
node := nodes[i]
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
if err := p.WorkerPool.Submit(func() {
|
||||
defer wg.Done()
|
||||
|
||||
if p.ExecutionInterceptor != nil && p.ExecutionInterceptor(ctx, node) {
|
||||
return
|
||||
}
|
||||
|
||||
s.transport.Transport(ctx, transport.ObjectTransportParams{
|
||||
TransportInfo: p.TransportInfo,
|
||||
TargetNode: node,
|
||||
ResultHandler: p.Handler,
|
||||
})
|
||||
}); err != nil {
|
||||
wg.Done()
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func (*singleRoutinePool) Submit(fn func()) error {
|
||||
fn()
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewObjectContainerHandler is a SelectiveContainerExecutor constructor.
|
||||
func NewObjectContainerHandler(p ObjectContainerHandlerParams) (SelectiveContainerExecutor, error) {
|
||||
switch {
|
||||
case p.Executor == nil:
|
||||
return nil, errors.Wrap(errEmptyTraverseExecutor, instanceFailMsg)
|
||||
case p.Logger == nil:
|
||||
return nil, errors.Wrap(errEmptyLogger, instanceFailMsg)
|
||||
case p.NodeLister == nil:
|
||||
return nil, errors.Wrap(errEmptyNodeLister, instanceFailMsg)
|
||||
}
|
||||
|
||||
return &selectiveCnrExec{
|
||||
cnl: p.NodeLister,
|
||||
Executor: p.Executor,
|
||||
log: p.Logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewContainerTraverseExecutor is a ContainerTraverseExecutor executor.
|
||||
func NewContainerTraverseExecutor(t transport.ObjectTransport) (ContainerTraverseExecutor, error) {
|
||||
if t == nil {
|
||||
return nil, errNilObjectTransport
|
||||
}
|
||||
|
||||
return &cnrTraverseExec{transport: t}, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue