frostfs-node/pkg/network/transport/object/grpc/service.go
Stanislav Bogatyrev b7b5079934 Add Inner Ring code
2020-07-24 17:07:37 +03:00

674 lines
17 KiB
Go

package object
import (
"context"
"crypto/ecdsa"
"math"
"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-api-go/session"
"github.com/nspcc-dev/neofs-api-go/storagegroup"
eaclstorage "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage"
"github.com/nspcc-dev/neofs-node/pkg/core/container/storage"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore"
"github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper"
contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper"
"github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc"
libgrpc "github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc"
_range "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc/range"
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement"
storage2 "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage"
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer"
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport"
storagegroup2 "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport/storagegroup"
"github.com/nspcc-dev/neofs-node/pkg/services/object_manager/verifier"
"github.com/panjf2000/ants/v2"
"github.com/pkg/errors"
"go.uber.org/zap"
)
type (
// CID is a type alias of
// CID from refs package of neofs-api-go.
CID = refs.CID
// Object is a type alias of
// Object from object package of neofs-api-go.
Object = object.Object
// ID is a type alias of
// ObjectID from refs package of neofs-api-go.
ID = refs.ObjectID
// OwnerID is a type alias of
// OwnerID from refs package of neofs-api-go.
OwnerID = refs.OwnerID
// Address is a type alias of
// Address from refs package of neofs-api-go.
Address = refs.Address
// Hash is a type alias of
// Hash from hash package of neofs-api-go.
Hash = hash.Hash
// Meta is a type alias of
// ObjectMeta from localstore package.
Meta = localstore.ObjectMeta
// Filter is a type alias of
// FilterPipeline from localstore package.
Filter = localstore.FilterPipeline
// Header is a type alias of
// Header from object package of neofs-api-go.
Header = object.Header
// UserHeader is a type alias of
// UserHeader from object package of neofs-api-go.
UserHeader = object.UserHeader
// SystemHeader is a type alias of
// SystemHeader from object package of neofs-api-go.
SystemHeader = object.SystemHeader
// CreationPoint is a type alias of
// CreationPoint from object package of neofs-api-go.
CreationPoint = object.CreationPoint
// Service is an interface of the server of Object service.
Service interface {
grpc.Service
CapacityMeter
object.ServiceServer
}
// CapacityMeter is an interface of node storage capacity meter.
CapacityMeter interface {
RelativeAvailableCap() float64
AbsoluteAvailableCap() uint64
}
// EpochReceiver is an interface of the container of epoch number with read access.
EpochReceiver interface {
Epoch() uint64
}
// RemoteService is an interface of Object service client constructor.
RemoteService interface {
Remote(context.Context, multiaddr.Multiaddr) (object.ServiceClient, error)
}
// Placer is an interface of placement component.
Placer interface {
IsContainerNode(ctx context.Context, addr multiaddr.Multiaddr, cid CID, previousNetMap bool) (bool, error)
GetNodes(ctx context.Context, addr Address, usePreviousNetMap bool, excl ...multiaddr.Multiaddr) ([]multiaddr.Multiaddr, error)
}
// WorkerPool is an interface of go-routing pool.
WorkerPool interface {
Submit(func()) error
}
// Salitor is a salting slice function.
Salitor func(data []byte, salt []byte) []byte
serviceRequest interface {
object.Request
service.RequestData
service.SignKeyPairAccumulator
service.SignKeyPairSource
SetToken(*service.Token)
SetBearer(*service.BearerTokenMsg)
SetHeaders([]service.RequestExtendedHeader_KV)
}
NetmapClient = wrapper.Wrapper
// Params groups the parameters of Object service server's constructor.
Params struct {
CheckACL bool
Assembly bool
WindowSize int
MaxProcessingSize uint64
StorageCapacity uint64
PoolSize int
Salitor Salitor
LocalStore localstore.Localstore
Placer Placer
ObjectRestorer transformer.ObjectRestorer
RemoteService RemoteService
AddressStore storage2.AddressStoreComponent
Logger *zap.Logger
TokenStore session.PrivateTokenStore
EpochReceiver EpochReceiver
PlacementWrapper *placement.PlacementWrapper
DialTimeout time.Duration
Key *ecdsa.PrivateKey
PutParams OperationParams
GetParams OperationParams
DeleteParams OperationParams
HeadParams OperationParams
SearchParams OperationParams
RangeParams OperationParams
RangeHashParams OperationParams
headRecv objectReceiver
Verifier verifier.Verifier
Transformer transformer.Transformer
MaxPayloadSize uint64
// ACL pre-processor params
ContainerStorage storage.Storage
NetmapClient *NetmapClient
SGInfoReceiver storagegroup.InfoReceiver
ExtendedACLSource eaclstorage.Storage
requestActionCalculator requestActionCalculator
targetFinder RequestTargeter
aclInfoReceiver aclInfoReceiver
}
// OperationParams groups the parameters of particular object operation.
OperationParams struct {
Timeout time.Duration
LogErrors bool
}
objectService struct {
ls localstore.Localstore
storageCap uint64
executor transport.SelectiveContainerExecutor
pPut OperationParams
pGet OperationParams
pDel OperationParams
pHead OperationParams
pSrch OperationParams
pRng OperationParams
pRngHash OperationParams
log *zap.Logger
requestHandler requestHandler
objSearcher objectSearcher
objRecv objectReceiver
objStorer objectStorer
objRemover objectRemover
rngRecv objectRangeReceiver
payloadRngRecv payloadRangeReceiver
respPreparer responsePreparer
getChunkPreparer responsePreparer
rangeChunkPreparer responsePreparer
statusCalculator *statusCalculator
}
)
const (
defaultDialTimeout = 5 * time.Second
defaultPutTimeout = time.Second
defaultGetTimeout = time.Second
defaultDeleteTimeout = time.Second
defaultHeadTimeout = time.Second
defaultSearchTimeout = time.Second
defaultRangeTimeout = time.Second
defaultRangeHashTimeout = time.Second
defaultPoolSize = 10
readyObjectsCheckpointFilterName = "READY_OBJECTS_PUT_CHECKPOINT"
allObjectsCheckpointFilterName = "ALL_OBJECTS_PUT_CHECKPOINT"
)
var (
errEmptyTokenStore = errors.New("objectService.New failed: key store not provided")
errEmptyPlacer = errors.New("objectService.New failed: placer not provided")
errEmptyTransformer = errors.New("objectService.New failed: transformer pipeline not provided")
errEmptyGRPC = errors.New("objectService.New failed: gRPC connector not provided")
errEmptyAddress = errors.New("objectService.New failed: address store not provided")
errEmptyLogger = errors.New("objectService.New failed: logger not provided")
errEmptyEpochReceiver = errors.New("objectService.New failed: epoch receiver not provided")
errEmptyLocalStore = errors.New("new local client failed: <nil> localstore passed")
errEmptyPrivateKey = errors.New("objectService.New failed: private key not provided")
errEmptyVerifier = errors.New("objectService.New failed: object verifier not provided")
errEmptyACLHelper = errors.New("objectService.New failed: ACL helper not provided")
errEmptyCnrLister = errors.New("objectService.New failed: container lister not provided")
errEmptySGInfoRecv = errors.New("objectService.New failed: SG info receiver not provided")
errInvalidCIDFilter = errors.New("invalid CID filter")
errTokenRetrieval = errors.New("objectService.Put failed on token retrieval")
errHeaderExpected = errors.New("expected header as a first message in stream")
)
var requestSignFunc = service.SignRequestData
var requestVerifyFunc = libgrpc.VerifyRequestWithSignatures
// New is an Object service server's constructor.
func New(p *Params) (Service, error) {
if p.PutParams.Timeout <= 0 {
p.PutParams.Timeout = defaultPutTimeout
}
if p.GetParams.Timeout <= 0 {
p.GetParams.Timeout = defaultGetTimeout
}
if p.DeleteParams.Timeout <= 0 {
p.DeleteParams.Timeout = defaultDeleteTimeout
}
if p.HeadParams.Timeout <= 0 {
p.HeadParams.Timeout = defaultHeadTimeout
}
if p.SearchParams.Timeout <= 0 {
p.SearchParams.Timeout = defaultSearchTimeout
}
if p.RangeParams.Timeout <= 0 {
p.RangeParams.Timeout = defaultRangeTimeout
}
if p.RangeHashParams.Timeout <= 0 {
p.RangeHashParams.Timeout = defaultRangeHashTimeout
}
if p.DialTimeout <= 0 {
p.DialTimeout = defaultDialTimeout
}
if p.PoolSize <= 0 {
p.PoolSize = defaultPoolSize
}
switch {
case p.TokenStore == nil:
return nil, errEmptyTokenStore
case p.Placer == nil:
return nil, errEmptyPlacer
case p.LocalStore == nil:
return nil, errEmptyLocalStore
case (p.ObjectRestorer == nil || p.Transformer == nil) && p.Assembly:
return nil, errEmptyTransformer
case p.RemoteService == nil:
return nil, errEmptyGRPC
case p.AddressStore == nil:
return nil, errEmptyAddress
case p.Logger == nil:
return nil, errEmptyLogger
case p.EpochReceiver == nil:
return nil, errEmptyEpochReceiver
case p.Key == nil:
return nil, errEmptyPrivateKey
case p.Verifier == nil:
return nil, errEmptyVerifier
case p.NetmapClient == nil:
return nil, contract.ErrNilWrapper
case p.PlacementWrapper == nil:
return nil, errEmptyCnrLister
case p.ContainerStorage == nil:
return nil, storage.ErrNilStorage
case p.SGInfoReceiver == nil:
return nil, errEmptySGInfoRecv
case p.ExtendedACLSource == nil:
return nil, eaclstorage.ErrNilStorage
}
pool, err := ants.NewPool(p.PoolSize)
if err != nil {
return nil, errors.Wrap(err, "objectService.New failed: could not create worker pool")
}
if p.MaxProcessingSize <= 0 {
p.MaxProcessingSize = math.MaxUint64
}
if p.StorageCapacity <= 0 {
p.StorageCapacity = math.MaxUint64
}
epochRespPreparer := &epochResponsePreparer{
epochRecv: p.EpochReceiver,
}
p.targetFinder = &targetFinder{
log: p.Logger,
irKeysRecv: p.NetmapClient,
cnrLister: p.PlacementWrapper,
cnrStorage: p.ContainerStorage,
}
p.requestActionCalculator = &reqActionCalc{
storage: p.ExtendedACLSource,
log: p.Logger,
}
p.aclInfoReceiver = aclInfoReceiver{
cnrStorage: p.ContainerStorage,
targetFinder: p.targetFinder,
}
srv := &objectService{
ls: p.LocalStore,
log: p.Logger,
pPut: p.PutParams,
pGet: p.GetParams,
pDel: p.DeleteParams,
pHead: p.HeadParams,
pSrch: p.SearchParams,
pRng: p.RangeParams,
pRngHash: p.RangeHashParams,
storageCap: p.StorageCapacity,
requestHandler: &coreRequestHandler{
preProc: newPreProcessor(p),
postProc: newPostProcessor(),
},
respPreparer: &complexResponsePreparer{
items: []responsePreparer{
epochRespPreparer,
&aclResponsePreparer{
aclInfoReceiver: p.aclInfoReceiver,
reqActCalc: p.requestActionCalculator,
eaclSrc: p.ExtendedACLSource,
},
},
},
getChunkPreparer: epochRespPreparer,
rangeChunkPreparer: epochRespPreparer,
statusCalculator: serviceStatusCalculator(),
}
tr, err := NewMultiTransport(MultiTransportParams{
AddressStore: p.AddressStore,
EpochReceiver: p.EpochReceiver,
RemoteService: p.RemoteService,
Logger: p.Logger,
Key: p.Key,
PutTimeout: p.PutParams.Timeout,
GetTimeout: p.GetParams.Timeout,
HeadTimeout: p.HeadParams.Timeout,
SearchTimeout: p.SearchParams.Timeout,
RangeHashTimeout: p.RangeHashParams.Timeout,
DialTimeout: p.DialTimeout,
PrivateTokenStore: p.TokenStore,
})
if err != nil {
return nil, err
}
exec, err := transport.NewContainerTraverseExecutor(tr)
if err != nil {
return nil, err
}
srv.executor, err = transport.NewObjectContainerHandler(transport.ObjectContainerHandlerParams{
NodeLister: p.PlacementWrapper,
Executor: exec,
Logger: p.Logger,
})
if err != nil {
return nil, err
}
local := &localStoreExecutor{
salitor: p.Salitor,
epochRecv: p.EpochReceiver,
localStore: p.LocalStore,
}
qvc := &queryVersionController{
m: make(map[int]localQueryImposer),
}
qvc.m[1] = &coreQueryImposer{
fCreator: new(coreFilterCreator),
lsLister: p.LocalStore,
log: p.Logger,
}
localExec := &localOperationExecutor{
objRecv: local,
headRecv: local,
objStore: local,
queryImp: qvc,
rngReader: local,
rngHasher: local,
}
opExec := &coreOperationExecutor{
pre: new(coreExecParamsComp),
fin: &coreOperationFinalizer{
curPlacementBuilder: &corePlacementUtil{
prevNetMap: false,
placementBuilder: p.Placer,
log: p.Logger,
},
prevPlacementBuilder: &corePlacementUtil{
prevNetMap: true,
placementBuilder: p.Placer,
log: p.Logger,
},
interceptorPreparer: &coreInterceptorPreparer{
localExec: localExec,
addressStore: p.AddressStore,
},
workerPool: pool,
traverseExec: exec,
resLogger: &coreResultLogger{
mLog: requestLogMap(p),
log: p.Logger,
},
log: p.Logger,
},
loc: localExec,
}
srv.objSearcher = &coreObjectSearcher{
executor: opExec,
}
childLister := &coreChildrenLister{
queryFn: coreChildrenQueryFunc,
objSearcher: srv.objSearcher,
log: p.Logger,
timeout: p.SearchParams.Timeout,
}
childrenRecv := &coreChildrenReceiver{
timeout: p.HeadParams.Timeout,
}
chopperTable := _range.NewChopperTable()
relRecv := &neighborReceiver{
firstChildQueryFn: firstChildQueryFunc,
leftNeighborQueryFn: leftNeighborQueryFunc,
rightNeighborQueryFn: rightNeighborQueryFunc,
rangeDescRecv: &selectiveRangeRecv{executor: srv.executor},
}
straightObjRecv := &straightObjectReceiver{
executor: opExec,
}
rngRecv := &corePayloadRangeReceiver{
chopTable: chopperTable,
relRecv: relRecv,
payloadRecv: &corePayloadPartReceiver{
rDataRecv: &straightRangeDataReceiver{
executor: opExec,
},
windowController: &simpleWindowController{
windowSize: p.WindowSize,
},
},
mErr: map[error]struct{}{
localstore.ErrOutOfRange: {},
},
log: p.Logger,
}
coreObjRecv := &coreObjectReceiver{
straightObjRecv: straightObjRecv,
childLister: childLister,
ancestralRecv: &coreAncestralReceiver{
childrenRecv: childrenRecv,
objRewinder: &coreObjectRewinder{
transformer: p.ObjectRestorer,
},
pRangeRecv: rngRecv,
},
log: p.Logger,
}
childrenRecv.coreObjRecv = coreObjRecv
srv.objRecv = coreObjRecv
srv.payloadRngRecv = rngRecv
if !p.Assembly {
coreObjRecv.ancestralRecv, coreObjRecv.childLister = nil, nil
}
p.headRecv = srv.objRecv
filter, err := newIncomingObjectFilter(p)
if err != nil {
return nil, err
}
straightStorer := &straightObjectStorer{
executor: opExec,
}
bf, err := basicFilter(p)
if err != nil {
return nil, err
}
transformerObjStorer := &transformingObjectStorer{
transformer: p.Transformer,
objStorer: straightStorer,
mErr: map[error]struct{}{
transformer.ErrInvalidSGLinking: {},
storagegroup2.ErrIncompleteSGInfo: {},
},
}
srv.objStorer = &filteringObjectStorer{
filter: bf,
objStorer: &bifurcatingObjectStorer{
straightStorer: &filteringObjectStorer{
filter: filter,
objStorer: &receivingObjectStorer{
straightStorer: straightStorer,
vPayload: storage2.NewPayloadVerifier(),
},
},
tokenStorer: &tokenObjectStorer{
tokenStore: p.TokenStore,
objStorer: transformerObjStorer,
},
},
}
srv.objRemover = &coreObjRemover{
delPrep: &coreDelPreparer{
childLister: childLister,
},
straightRem: &straightObjRemover{
tombCreator: new(coreTombCreator),
objStorer: transformerObjStorer,
},
tokenStore: p.TokenStore,
mErr: map[error]struct{}{},
log: p.Logger,
}
srv.rngRecv = &coreRangeReceiver{
rngRevealer: &coreRngRevealer{
relativeRecv: relRecv,
chopTable: chopperTable,
},
straightRngRecv: &straightRangeReceiver{
executor: opExec,
},
mErr: map[error]struct{}{
localstore.ErrOutOfRange: {},
},
log: p.Logger,
}
return srv, nil
}
func requestLogMap(p *Params) map[object.RequestType]struct{} {
m := make(map[object.RequestType]struct{})
if p.PutParams.LogErrors {
m[object.RequestPut] = struct{}{}
}
if p.GetParams.LogErrors {
m[object.RequestGet] = struct{}{}
}
if p.HeadParams.LogErrors {
m[object.RequestHead] = struct{}{}
}
if p.SearchParams.LogErrors {
m[object.RequestSearch] = struct{}{}
}
if p.RangeParams.LogErrors {
m[object.RequestRange] = struct{}{}
}
if p.RangeHashParams.LogErrors {
m[object.RequestRangeHash] = struct{}{}
}
return m
}
func (s *objectService) Name() string { return "Object Service" }
func (s *objectService) Register(g *grpc.Server) { object.RegisterServiceServer(g, s) }