peer drpc conn pool
This commit is contained in:
parent
a898c6fc9c
commit
553ed3a64b
@ -1,7 +1,12 @@
|
|||||||
package peer
|
package peer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/anyproto/any-sync/app/ocache"
|
||||||
"github.com/anyproto/any-sync/net/transport"
|
"github.com/anyproto/any-sync/net/transport"
|
||||||
|
"storj.io/drpc"
|
||||||
|
"storj.io/drpc/drpcconn"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/anyproto/any-sync/app/logger"
|
"github.com/anyproto/any-sync/app/logger"
|
||||||
@ -12,7 +17,10 @@ var log = logger.NewNamed("common.net.peer")
|
|||||||
|
|
||||||
func NewPeer(mc transport.MultiConn) (p Peer, err error) {
|
func NewPeer(mc transport.MultiConn) (p Peer, err error) {
|
||||||
ctx := mc.Context()
|
ctx := mc.Context()
|
||||||
pr := &peer{}
|
pr := &peer{
|
||||||
|
active: map[drpc.Conn]struct{}{},
|
||||||
|
MultiConn: mc,
|
||||||
|
}
|
||||||
if pr.id, err = CtxPeerId(ctx); err != nil {
|
if pr.id, err = CtxPeerId(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -21,12 +29,25 @@ func NewPeer(mc transport.MultiConn) (p Peer, err error) {
|
|||||||
|
|
||||||
type Peer interface {
|
type Peer interface {
|
||||||
Id() string
|
Id() string
|
||||||
|
|
||||||
|
AcquireDrpcConn(ctx context.Context) (drpc.Conn, error)
|
||||||
|
ReleaseDrpcConn(conn drpc.Conn)
|
||||||
|
|
||||||
|
IsClosed() bool
|
||||||
|
|
||||||
TryClose(objectTTL time.Duration) (res bool, err error)
|
TryClose(objectTTL time.Duration) (res bool, err error)
|
||||||
transport.MultiConn
|
|
||||||
|
ocache.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
type peer struct {
|
type peer struct {
|
||||||
id string
|
id string
|
||||||
|
|
||||||
|
// drpc conn pool
|
||||||
|
inactive []drpc.Conn
|
||||||
|
active map[drpc.Conn]struct{}
|
||||||
|
mu sync.Mutex
|
||||||
|
|
||||||
transport.MultiConn
|
transport.MultiConn
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,10 +55,44 @@ func (p *peer) Id() string {
|
|||||||
return p.id
|
return p.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *peer) AcquireDrpcConn(ctx context.Context) (drpc.Conn, error) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if len(p.inactive) == 0 {
|
||||||
|
conn, err := p.Open(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dconn := drpcconn.New(conn)
|
||||||
|
p.inactive = append(p.inactive, dconn)
|
||||||
|
}
|
||||||
|
idx := len(p.inactive) - 1
|
||||||
|
res := p.inactive[idx]
|
||||||
|
p.inactive = p.inactive[:idx]
|
||||||
|
p.active[res] = struct{}{}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *peer) ReleaseDrpcConn(conn drpc.Conn) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if _, ok := p.active[conn]; ok {
|
||||||
|
delete(p.active, conn)
|
||||||
|
}
|
||||||
|
p.inactive = append(p.inactive, conn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (p *peer) TryClose(objectTTL time.Duration) (res bool, err error) {
|
func (p *peer) TryClose(objectTTL time.Duration) (res bool, err error) {
|
||||||
if time.Now().Sub(p.LastUsage()) < objectTTL {
|
if time.Now().Sub(p.LastUsage()) < objectTTL {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
p.mu.Lock()
|
||||||
|
if len(p.active) > 0 {
|
||||||
|
p.mu.Unlock()
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
p.mu.Unlock()
|
||||||
return true, p.Close()
|
return true, p.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
62
net/peer/peer_test.go
Normal file
62
net/peer/peer_test.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package peer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/anyproto/any-sync/net/transport/mock_transport"
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ctx = context.Background()
|
||||||
|
|
||||||
|
func TestPeer_AcquireDrpcConn(t *testing.T) {
|
||||||
|
fx := newFixture(t, "p1")
|
||||||
|
defer fx.finish()
|
||||||
|
in, out := net.Pipe()
|
||||||
|
defer out.Close()
|
||||||
|
fx.mc.EXPECT().Open(gomock.Any()).Return(in, nil)
|
||||||
|
dc, err := fx.AcquireDrpcConn(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, dc)
|
||||||
|
defer dc.Close()
|
||||||
|
|
||||||
|
assert.Len(t, fx.active, 1)
|
||||||
|
assert.Len(t, fx.inactive, 0)
|
||||||
|
|
||||||
|
fx.ReleaseDrpcConn(dc)
|
||||||
|
|
||||||
|
assert.Len(t, fx.active, 0)
|
||||||
|
assert.Len(t, fx.inactive, 1)
|
||||||
|
|
||||||
|
dc, err = fx.AcquireDrpcConn(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, dc)
|
||||||
|
assert.Len(t, fx.active, 1)
|
||||||
|
assert.Len(t, fx.inactive, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFixture(t *testing.T, peerId string) *fixture {
|
||||||
|
fx := &fixture{
|
||||||
|
ctrl: gomock.NewController(t),
|
||||||
|
}
|
||||||
|
fx.mc = mock_transport.NewMockMultiConn(fx.ctrl)
|
||||||
|
ctx := CtxWithPeerId(context.Background(), peerId)
|
||||||
|
fx.mc.EXPECT().Context().Return(ctx).AnyTimes()
|
||||||
|
p, err := NewPeer(fx.mc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
fx.peer = p.(*peer)
|
||||||
|
return fx
|
||||||
|
}
|
||||||
|
|
||||||
|
type fixture struct {
|
||||||
|
*peer
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
mc *mock_transport.MockMultiConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fx *fixture) finish() {
|
||||||
|
fx.ctrl.Finish()
|
||||||
|
}
|
||||||
@ -83,7 +83,7 @@ func (p *peerService) Accept(mc transport.MultiConn) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = p.pool.AddPeer(pr); err != nil {
|
if err = p.pool.AddPeer(context.Background(), pr); err != nil {
|
||||||
_ = pr.Close()
|
_ = pr.Close()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
net2 "net"
|
net2 "net"
|
||||||
|
"storj.io/drpc"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -216,6 +217,12 @@ type testPeer struct {
|
|||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *testPeer) AcquireDrpcConn(ctx context.Context) (drpc.Conn, error) {
|
||||||
|
return nil, fmt.Errorf("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testPeer) ReleaseDrpcConn(conn drpc.Conn) {}
|
||||||
|
|
||||||
func (t *testPeer) Context() context.Context {
|
func (t *testPeer) Context() context.Context {
|
||||||
//TODO implement me
|
//TODO implement me
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
@ -239,12 +246,6 @@ func (t *testPeer) Id() string {
|
|||||||
return t.id
|
return t.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testPeer) LastUsage() time.Time {
|
|
||||||
return time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testPeer) UpdateLastUsage() {}
|
|
||||||
|
|
||||||
func (t *testPeer) TryClose(objectTTL time.Duration) (res bool, err error) {
|
func (t *testPeer) TryClose(objectTTL time.Duration) (res bool, err error) {
|
||||||
return true, t.Close()
|
return true, t.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
188
net/transport/mock_transport/mock_transport.go
Normal file
188
net/transport/mock_transport/mock_transport.go
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/anyproto/any-sync/net/transport (interfaces: Transport,MultiConn)
|
||||||
|
|
||||||
|
// Package mock_transport is a generated GoMock package.
|
||||||
|
package mock_transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
net "net"
|
||||||
|
reflect "reflect"
|
||||||
|
time "time"
|
||||||
|
|
||||||
|
transport "github.com/anyproto/any-sync/net/transport"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockTransport is a mock of Transport interface.
|
||||||
|
type MockTransport struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockTransportMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockTransportMockRecorder is the mock recorder for MockTransport.
|
||||||
|
type MockTransportMockRecorder struct {
|
||||||
|
mock *MockTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockTransport creates a new mock instance.
|
||||||
|
func NewMockTransport(ctrl *gomock.Controller) *MockTransport {
|
||||||
|
mock := &MockTransport{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockTransportMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockTransport) EXPECT() *MockTransportMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial mocks base method.
|
||||||
|
func (m *MockTransport) Dial(arg0 context.Context, arg1 string) (transport.MultiConn, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Dial", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(transport.MultiConn)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial indicates an expected call of Dial.
|
||||||
|
func (mr *MockTransportMockRecorder) Dial(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Dial", reflect.TypeOf((*MockTransport)(nil).Dial), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAccepter mocks base method.
|
||||||
|
func (m *MockTransport) SetAccepter(arg0 transport.Accepter) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "SetAccepter", arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAccepter indicates an expected call of SetAccepter.
|
||||||
|
func (mr *MockTransportMockRecorder) SetAccepter(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAccepter", reflect.TypeOf((*MockTransport)(nil).SetAccepter), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockMultiConn is a mock of MultiConn interface.
|
||||||
|
type MockMultiConn struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockMultiConnMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockMultiConnMockRecorder is the mock recorder for MockMultiConn.
|
||||||
|
type MockMultiConnMockRecorder struct {
|
||||||
|
mock *MockMultiConn
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockMultiConn creates a new mock instance.
|
||||||
|
func NewMockMultiConn(ctrl *gomock.Controller) *MockMultiConn {
|
||||||
|
mock := &MockMultiConn{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockMultiConnMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockMultiConn) EXPECT() *MockMultiConnMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept mocks base method.
|
||||||
|
func (m *MockMultiConn) Accept() (net.Conn, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Accept")
|
||||||
|
ret0, _ := ret[0].(net.Conn)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept indicates an expected call of Accept.
|
||||||
|
func (mr *MockMultiConnMockRecorder) Accept() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Accept", reflect.TypeOf((*MockMultiConn)(nil).Accept))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr mocks base method.
|
||||||
|
func (m *MockMultiConn) Addr() string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Addr")
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr indicates an expected call of Addr.
|
||||||
|
func (mr *MockMultiConnMockRecorder) Addr() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Addr", reflect.TypeOf((*MockMultiConn)(nil).Addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close mocks base method.
|
||||||
|
func (m *MockMultiConn) Close() error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Close")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close indicates an expected call of Close.
|
||||||
|
func (mr *MockMultiConnMockRecorder) Close() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockMultiConn)(nil).Close))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context mocks base method.
|
||||||
|
func (m *MockMultiConn) Context() context.Context {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Context")
|
||||||
|
ret0, _ := ret[0].(context.Context)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context indicates an expected call of Context.
|
||||||
|
func (mr *MockMultiConnMockRecorder) Context() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockMultiConn)(nil).Context))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClosed mocks base method.
|
||||||
|
func (m *MockMultiConn) IsClosed() bool {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "IsClosed")
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClosed indicates an expected call of IsClosed.
|
||||||
|
func (mr *MockMultiConnMockRecorder) IsClosed() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsClosed", reflect.TypeOf((*MockMultiConn)(nil).IsClosed))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastUsage mocks base method.
|
||||||
|
func (m *MockMultiConn) LastUsage() time.Time {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "LastUsage")
|
||||||
|
ret0, _ := ret[0].(time.Time)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastUsage indicates an expected call of LastUsage.
|
||||||
|
func (mr *MockMultiConnMockRecorder) LastUsage() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastUsage", reflect.TypeOf((*MockMultiConn)(nil).LastUsage))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open mocks base method.
|
||||||
|
func (m *MockMultiConn) Open(arg0 context.Context) (net.Conn, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Open", arg0)
|
||||||
|
ret0, _ := ret[0].(net.Conn)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open indicates an expected call of Open.
|
||||||
|
func (mr *MockMultiConnMockRecorder) Open(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockMultiConn)(nil).Open), arg0)
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
//go:generate mockgen -destination mock_transport/mock_transport.go github.com/anyproto/any-sync/net/transport Transport,MultiConn
|
||||||
package transport
|
package transport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user