diff --git a/pkg/services/tree/cache.go b/pkg/services/tree/cache.go new file mode 100644 index 00000000..db954d90 --- /dev/null +++ b/pkg/services/tree/cache.go @@ -0,0 +1,63 @@ +package tree + +import ( + "context" + "sync" + + "github.com/hashicorp/golang-lru/simplelru" + "github.com/nspcc-dev/neofs-node/pkg/network" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" +) + +type clientCache struct { + sync.Mutex + simplelru.LRU +} + +const defaultClientCacheSize = 10 + +func (c *clientCache) init() { + l, _ := simplelru.NewLRU(defaultClientCacheSize, func(key, value interface{}) { + _ = value.(*grpc.ClientConn).Close() + }) + c.LRU = *l +} + +func (c *clientCache) get(ctx context.Context, netmapAddr string) (TreeServiceClient, error) { + c.Lock() + ccInt, ok := c.LRU.Get(netmapAddr) + c.Unlock() + + if ok { + cc := ccInt.(*grpc.ClientConn) + if s := cc.GetState(); s == connectivity.Idle || s == connectivity.Ready { + return NewTreeServiceClient(cc), nil + } + _ = cc.Close() + } + + cc, err := dialTreeService(ctx, netmapAddr) + if err != nil { + return nil, err + } + + c.Lock() + c.LRU.Add(netmapAddr, cc) + c.Unlock() + + return NewTreeServiceClient(cc), nil +} + +func dialTreeService(ctx context.Context, netmapAddr string) (*grpc.ClientConn, error) { + var netAddr network.Address + if err := netAddr.FromString(netmapAddr); err != nil { + return nil, err + } + + cc, err := grpc.DialContext(ctx, netAddr.URIAddr(), grpc.WithInsecure()) + if err != nil { + return nil, err + } + return cc, nil +} diff --git a/pkg/services/tree/redirect.go b/pkg/services/tree/redirect.go index 304ed0d7..52348fb0 100644 --- a/pkg/services/tree/redirect.go +++ b/pkg/services/tree/redirect.go @@ -5,10 +5,8 @@ import ( "context" "errors" - "github.com/nspcc-dev/neofs-node/pkg/network" netmapSDK "github.com/nspcc-dev/neofs-sdk-go/netmap" "go.uber.org/zap" - "google.golang.org/grpc" ) var errNoSuitableNode = errors.New("no node was found to execute the request") @@ -26,7 +24,7 @@ func (s *Service) forEachNode(ctx context.Context, cntNodes []netmapSDK.NodeInfo for _, n := range cntNodes { var stop bool n.IterateNetworkEndpoints(func(endpoint string) bool { - c, err := dialTreeService(ctx, endpoint) + c, err := s.cache.get(ctx, endpoint) if err != nil { return false } @@ -45,16 +43,3 @@ func (s *Service) forEachNode(ctx context.Context, cntNodes []netmapSDK.NodeInfo } return nil } - -func dialTreeService(ctx context.Context, netmapAddr string) (TreeServiceClient, error) { - var netAddr network.Address - if err := netAddr.FromString(netmapAddr); err != nil { - return nil, err - } - - cc, err := grpc.DialContext(ctx, netAddr.URIAddr(), grpc.WithInsecure()) - if err != nil { - return nil, err - } - return NewTreeServiceClient(cc), nil -} diff --git a/pkg/services/tree/replicator.go b/pkg/services/tree/replicator.go index 15ef0b03..313f12db 100644 --- a/pkg/services/tree/replicator.go +++ b/pkg/services/tree/replicator.go @@ -70,7 +70,7 @@ func (s *Service) replicate(ctx context.Context, op movePair) error { n.IterateNetworkEndpoints(func(addr string) bool { lastAddr = addr - c, err := dialTreeService(ctx, addr) + c, err := s.cache.get(ctx, addr) if err != nil { lastErr = err return false diff --git a/pkg/services/tree/service.go b/pkg/services/tree/service.go index b0d86ce1..af09d606 100644 --- a/pkg/services/tree/service.go +++ b/pkg/services/tree/service.go @@ -18,6 +18,7 @@ import ( type Service struct { cfg + cache clientCache replicateCh chan movePair closeCh chan struct{} } @@ -38,6 +39,7 @@ func New(opts ...Option) *Service { s.log = zap.NewNop() } + s.cache.init() s.closeCh = make(chan struct{}) s.replicateCh = make(chan movePair, defaultReplicatorCapacity) @@ -424,7 +426,8 @@ func (s *Service) Apply(_ context.Context, req *ApplyRequest) (*ApplyResponse, e } d := pilorama.CIDDescriptor{CID: cid, Position: pos, Size: size} - return nil, s.forest.TreeApply(d, req.GetBody().GetTreeId(), &pilorama.Move{ + resp := &ApplyResponse{Body: &ApplyResponse_Body{}, Signature: &Signature{}} + return resp, s.forest.TreeApply(d, req.GetBody().GetTreeId(), &pilorama.Move{ Parent: op.GetParentId(), Child: op.GetChildId(), Meta: meta,