github.com/cloudwego/kitex@v0.9.0/pkg/remote/connpool/long_pool_test.go (about)

     1  /*
     2   * Copyright 2021 CloudWeGo Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package connpool
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"math/rand"
    24  	"net"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/cloudwego/kitex/pkg/connpool"
    30  
    31  	"github.com/golang/mock/gomock"
    32  
    33  	mocksnetpoll "github.com/cloudwego/kitex/internal/mocks/netpoll"
    34  	mocksremote "github.com/cloudwego/kitex/internal/mocks/remote"
    35  	"github.com/cloudwego/kitex/internal/test"
    36  	dialer "github.com/cloudwego/kitex/pkg/remote"
    37  	"github.com/cloudwego/kitex/pkg/utils"
    38  )
    39  
    40  var (
    41  	mockDestService = "destService"
    42  	mockAddr0       = "127.0.0.1:8000"
    43  	mockAddr1       = "127.0.0.1:8001"
    44  )
    45  
    46  func TestPoolReuse(t *testing.T) {
    47  	ctrl := gomock.NewController(t)
    48  	defer ctrl.Finish()
    49  
    50  	var (
    51  		minIdle        = 0
    52  		maxIdle        = 1
    53  		maxIdleTimeout = time.Millisecond
    54  	)
    55  
    56  	p := newPool(minIdle, maxIdle, maxIdleTimeout)
    57  	count := make(map[*longConn]bool)
    58  
    59  	conn := newLongConnForTest(ctrl, mockAddr0)
    60  	recycled := p.Put(conn)
    61  	test.Assert(t, recycled == true)
    62  	test.Assert(t, p.Len() == 1)
    63  
    64  	c, reused, decN := p.Get()
    65  	test.Assert(t, c != nil)
    66  	test.Assert(t, reused == true)
    67  	test.Assert(t, decN == 1)
    68  	count[c] = true
    69  	test.Assert(t, len(count) == 1)
    70  }
    71  
    72  // TestPoolGetInactiveConn tests the pool with only inactive connection. Get() should return nil.
    73  func TestPoolGetInactiveConn(t *testing.T) {
    74  	ctrl := gomock.NewController(t)
    75  	defer ctrl.Finish()
    76  
    77  	var (
    78  		minIdle        = 0
    79  		maxIdle        = 1
    80  		maxIdleTimeout = time.Millisecond
    81  	)
    82  
    83  	p := newPool(minIdle, maxIdle, maxIdleTimeout)
    84  
    85  	// inactive conn
    86  	var closed bool
    87  	c := mocksnetpoll.NewMockConnection(ctrl)
    88  	c.EXPECT().IsActive().Return(false).AnyTimes()
    89  	c.EXPECT().Close().DoAndReturn(func() error {
    90  		closed = true
    91  		return nil
    92  	}).AnyTimes()
    93  	conn := &longConn{
    94  		Conn:    c,
    95  		address: mockAddr0,
    96  	}
    97  
    98  	recycled := p.Put(conn)
    99  	test.Assert(t, recycled == true)
   100  	// inactive
   101  	conn, reused, decNum := p.Get()
   102  	test.Assert(t, decNum == 1)
   103  	test.Assert(t, conn == nil)
   104  	test.Assert(t, reused == false)
   105  	test.Assert(t, closed == true)
   106  }
   107  
   108  // TestPoolGetWithInactiveConn tests the pool with inactive connection. Get() should return the first active one.
   109  func TestPoolGetWithInactiveConn(t *testing.T) {
   110  	ctrl := gomock.NewController(t)
   111  	defer ctrl.Finish()
   112  
   113  	var (
   114  		minIdle        = 0
   115  		maxIdle        = 10
   116  		maxIdleTimeout = time.Millisecond
   117  		inactiveNum    = 5
   118  	)
   119  
   120  	p := newPool(minIdle, maxIdle, maxIdleTimeout)
   121  	// put active conn
   122  	activeConn := newLongConnForTest(ctrl, mockAddr0)
   123  	recycled := p.Put(activeConn)
   124  	test.Assert(t, recycled == true)
   125  
   126  	// put inactive conn
   127  	closed := make([]bool, inactiveNum)
   128  	for i := 0; i < inactiveNum; i++ {
   129  		idx := i
   130  		c := mocksnetpoll.NewMockConnection(ctrl)
   131  		c.EXPECT().IsActive().Return(false).AnyTimes()
   132  		c.EXPECT().Close().DoAndReturn(func() error {
   133  			closed[idx] = true
   134  			return nil
   135  		}).AnyTimes()
   136  		conn := &longConn{
   137  			Conn:    c,
   138  			address: mockAddr0,
   139  		}
   140  		recycled := p.Put(conn)
   141  		test.Assert(t, recycled == true)
   142  	}
   143  	test.Assert(t, p.Len() == inactiveNum+1)
   144  
   145  	// decNum should be inactiveNum + 1
   146  	conn, reused, decNum := p.Get()
   147  	test.Assert(t, conn != nil)
   148  	test.Assert(t, reused == true)
   149  	test.Assert(t, decNum == inactiveNum+1)
   150  	// check if all inactive conns have been closed
   151  	for i := 0; i < inactiveNum; i++ {
   152  		test.Assert(t, closed[i] == true)
   153  	}
   154  }
   155  
   156  func TestPoolMaxIdle(t *testing.T) {
   157  	ctrl := gomock.NewController(t)
   158  	defer ctrl.Finish()
   159  
   160  	var (
   161  		minIdle        = 0
   162  		maxIdle        = 2
   163  		maxIdleTimeout = time.Millisecond
   164  	)
   165  
   166  	p := newPool(minIdle, maxIdle, maxIdleTimeout)
   167  	for i := 0; i < maxIdle+1; i++ {
   168  		recycled := p.Put(newLongConnForTest(ctrl, mockAddr0))
   169  		if i < maxIdle {
   170  			test.Assert(t, recycled == true)
   171  		} else {
   172  			test.Assert(t, recycled == false)
   173  		}
   174  	}
   175  	test.Assert(t, p.Len() == maxIdle)
   176  }
   177  
   178  func TestPoolMinIdle(t *testing.T) {
   179  	ctrl := gomock.NewController(t)
   180  	defer ctrl.Finish()
   181  
   182  	var (
   183  		minIdle        = 1
   184  		maxIdle        = 10
   185  		maxIdleTimeout = time.Millisecond
   186  	)
   187  
   188  	p := newPool(minIdle, maxIdle, maxIdleTimeout)
   189  	for i := 0; i < maxIdle+1; i++ {
   190  		p.Put(newLongConnForTest(ctrl, mockAddr0))
   191  	}
   192  	test.Assert(t, p.Len() == maxIdle)
   193  
   194  	time.Sleep(maxIdleTimeout)
   195  	p.Evict()
   196  	test.Assert(t, p.Len() == minIdle)
   197  }
   198  
   199  func TestPoolClose(t *testing.T) {
   200  	ctrl := gomock.NewController(t)
   201  	defer ctrl.Finish()
   202  
   203  	var (
   204  		minIdle        = 0
   205  		maxIdle        = 2
   206  		maxIdleTimeout = time.Millisecond
   207  	)
   208  
   209  	p := newPool(minIdle, maxIdle, maxIdleTimeout)
   210  	for i := 0; i < maxIdle+1; i++ {
   211  		p.Put(newLongConnForTest(ctrl, mockAddr0))
   212  	}
   213  
   214  	n := p.Close()
   215  	test.Assert(t, n == maxIdle)
   216  	test.Assert(t, p.idleList == nil)
   217  	test.Assert(t, p.Len() == 0)
   218  }
   219  
   220  func TestPoolDump(t *testing.T) {
   221  	ctrl := gomock.NewController(t)
   222  	defer ctrl.Finish()
   223  
   224  	var (
   225  		minIdle        = 0
   226  		maxIdle        = 2
   227  		maxIdleTimeout = time.Millisecond
   228  	)
   229  
   230  	p := newPool(minIdle, maxIdle, maxIdleTimeout)
   231  	for i := 0; i < maxIdle+1; i++ {
   232  		p.Put(newLongConnForTest(ctrl, mockAddr0))
   233  	}
   234  
   235  	dump := p.Dump()
   236  	test.Assert(t, dump.IdleNum == 2)
   237  	test.Assert(t, len(dump.ConnsDeadline) == 2)
   238  }
   239  
   240  func TestLongConnPoolGetTimeout(t *testing.T) {
   241  	ctrl := gomock.NewController(t)
   242  	defer ctrl.Finish()
   243  
   244  	p := newLongPoolForTest(0, 2, 3, time.Second)
   245  	defer p.Close()
   246  
   247  	d := mocksremote.NewMockDialer(ctrl)
   248  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   249  		connectCost := time.Millisecond * 10
   250  		if timeout < connectCost {
   251  			return nil, errors.New("connect timeout")
   252  		}
   253  		na := utils.NewNetAddr(network, address)
   254  		conn := mocksnetpoll.NewMockConnection(ctrl)
   255  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   256  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   257  		conn.EXPECT().Close().AnyTimes()
   258  		return conn, nil
   259  	}).AnyTimes()
   260  	var err error
   261  
   262  	_, err = p.Get(context.TODO(), "tcp", mockAddr0, dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second})
   263  	test.Assert(t, err == nil)
   264  
   265  	_, err = p.Get(context.TODO(), "tcp", mockAddr0, dialer.ConnOption{Dialer: d, ConnectTimeout: time.Millisecond})
   266  	test.Assert(t, err != nil)
   267  }
   268  
   269  func TestLongConnPoolReuse(t *testing.T) {
   270  	ctrl := gomock.NewController(t)
   271  	defer ctrl.Finish()
   272  
   273  	mockReporter := newMockConnReporter()
   274  	SetReporter(mockReporter)
   275  	defer SetReporter(&DummyReporter{}) // reset the reporter to default
   276  
   277  	idleTime := time.Millisecond * 100
   278  	p := newLongPoolForTest(0, 2, 3, idleTime)
   279  	defer p.Close()
   280  	p.EnableReporter()
   281  
   282  	d := mocksremote.NewMockDialer(ctrl)
   283  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   284  		na := utils.NewNetAddr(network, address)
   285  		conn := mocksnetpoll.NewMockConnection(ctrl)
   286  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   287  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   288  		conn.EXPECT().Close().AnyTimes()
   289  		return conn, nil
   290  	}).AnyTimes()
   291  
   292  	addr1, addr2 := mockAddr1, "127.0.0.1:8002"
   293  	netAddr1 := utils.NewNetAddr("tcp", addr1)
   294  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   295  
   296  	count := make(map[net.Conn]int)
   297  	getCnt := 10
   298  	for i := 0; i < getCnt; i++ {
   299  		c, err := p.Get(context.TODO(), "tcp", addr1, opt)
   300  		test.Assert(t, err == nil)
   301  		count[c]++
   302  	}
   303  	test.Assert(t, mockReporter.getConnSucceed(mockDestService, netAddr1) == getCnt)
   304  	test.Assert(t, mockReporter.getReuseSucceed(mockDestService, netAddr1) == 0)
   305  	mockReporter.reset()
   306  	test.Assert(t, len(count) == getCnt)
   307  
   308  	count = make(map[net.Conn]int)
   309  	for i := 0; i < getCnt; i++ {
   310  		c, err := p.Get(context.TODO(), "tcp", addr1, opt)
   311  		test.Assert(t, err == nil)
   312  		err = p.Put(c)
   313  		test.Assert(t, err == nil)
   314  		count[c]++
   315  	}
   316  	// the first Get dial one connection, the following Gets reuse it.
   317  	test.Assert(t, mockReporter.getConnSucceed(mockDestService, netAddr1) == 1)
   318  	test.Assert(t, mockReporter.getReuseSucceed(mockDestService, netAddr1) == getCnt-1)
   319  	test.Assert(t, len(count) == 1)
   320  
   321  	count = make(map[net.Conn]int)
   322  	for i := 0; i < getCnt; i++ {
   323  		c, err := p.Get(context.TODO(), "tcp", addr1, opt)
   324  		test.Assert(t, err == nil)
   325  		err = p.Put(c)
   326  		test.Assert(t, err == nil)
   327  		count[c]++
   328  
   329  		c, err = p.Get(context.TODO(), "tcp", addr2, opt)
   330  		test.Assert(t, err == nil)
   331  		err = p.Put(c)
   332  		test.Assert(t, err == nil)
   333  		count[c]++
   334  	}
   335  	test.Assert(t, len(count) == 2)
   336  
   337  	// test exceed idleTime
   338  	mockReporter.reset()
   339  	count = make(map[net.Conn]int)
   340  	getCnt = 3
   341  	for i := 0; i < getCnt; i++ {
   342  		time.Sleep(idleTime * 3)
   343  		c, err := p.Get(context.TODO(), "tcp", addr1, opt)
   344  		test.Assert(t, err == nil)
   345  		err = p.Put(c)
   346  		test.Assert(t, err == nil)
   347  		count[c]++
   348  	}
   349  	// Every Get needs dial one connection because old connections cannot be used
   350  	test.Assert(t, mockReporter.getConnSucceed(mockDestService, netAddr1) == getCnt)
   351  	test.Assert(t, len(count) == 3)
   352  }
   353  
   354  func TestLongConnPoolMaxIdle(t *testing.T) {
   355  	ctrl := gomock.NewController(t)
   356  	defer ctrl.Finish()
   357  
   358  	p := newLongPoolForTest(0, 2, 5, time.Second)
   359  	defer p.Close()
   360  
   361  	d := mocksremote.NewMockDialer(ctrl)
   362  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   363  		na := utils.NewNetAddr(network, address)
   364  		conn := mocksnetpoll.NewMockConnection(ctrl)
   365  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   366  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   367  		conn.EXPECT().Close().AnyTimes()
   368  		return conn, nil
   369  	}).AnyTimes()
   370  
   371  	addr := mockAddr1
   372  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   373  
   374  	var conns []net.Conn
   375  	count := make(map[net.Conn]int)
   376  	for i := 0; i < 10; i++ {
   377  		c, err := p.Get(context.TODO(), "tcp", addr, opt)
   378  		test.Assert(t, err == nil)
   379  		count[c]++
   380  		conns = append(conns, c)
   381  	}
   382  	test.Assert(t, len(count) == 10)
   383  
   384  	for _, c := range conns {
   385  		err := p.Put(c)
   386  		test.Assert(t, err == nil)
   387  	}
   388  
   389  	for i := 0; i < 10; i++ {
   390  		c, err := p.Get(context.TODO(), "tcp", addr, opt)
   391  		test.Assert(t, err == nil)
   392  		count[c]++
   393  	}
   394  	test.Assert(t, len(count) == 18)
   395  }
   396  
   397  func TestLongConnPoolGlobalMaxIdle(t *testing.T) {
   398  	ctrl := gomock.NewController(t)
   399  	defer ctrl.Finish()
   400  
   401  	p := newLongPoolForTest(0, 2, 3, time.Second)
   402  	defer p.Close()
   403  
   404  	d := mocksremote.NewMockDialer(ctrl)
   405  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   406  		na := utils.NewNetAddr(network, address)
   407  		conn := mocksnetpoll.NewMockConnection(ctrl)
   408  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   409  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   410  		conn.EXPECT().Close().AnyTimes()
   411  		return conn, nil
   412  	}).AnyTimes()
   413  
   414  	addr1, addr2 := mockAddr1, "127.0.0.1:8002"
   415  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   416  
   417  	var conns []net.Conn
   418  	count := make(map[net.Conn]int)
   419  	for i := 0; i < 10; i++ {
   420  		c, err := p.Get(context.TODO(), "tcp", addr1, opt)
   421  		test.Assert(t, err == nil)
   422  		count[c]++
   423  		conns = append(conns, c)
   424  
   425  		c, err = p.Get(context.TODO(), "tcp", addr2, opt)
   426  		test.Assert(t, err == nil)
   427  		count[c]++
   428  		conns = append(conns, c)
   429  	}
   430  	test.Assert(t, len(count) == 20)
   431  
   432  	for _, c := range conns {
   433  		err := p.Put(c)
   434  		test.Assert(t, err == nil)
   435  	}
   436  
   437  	for i := 0; i < 10; i++ {
   438  		c, err := p.Get(context.TODO(), "tcp", addr1, opt)
   439  		test.Assert(t, err == nil)
   440  		count[c]++
   441  
   442  		c, err = p.Get(context.TODO(), "tcp", addr2, opt)
   443  		test.Assert(t, err == nil)
   444  		count[c]++
   445  	}
   446  	test.Assert(t, len(count) == 37)
   447  }
   448  
   449  func TestLongConnPoolCloseOnDiscard(t *testing.T) {
   450  	ctrl := gomock.NewController(t)
   451  	defer ctrl.Finish()
   452  
   453  	p := newLongPoolForTest(0, 2, 5, time.Second)
   454  	defer p.Close()
   455  
   456  	var closed bool
   457  	d := mocksremote.NewMockDialer(ctrl)
   458  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   459  		na := utils.NewNetAddr(network, address)
   460  		conn := mocksnetpoll.NewMockConnection(ctrl)
   461  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   462  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   463  		conn.EXPECT().Close().DoAndReturn(func() error {
   464  			if closed {
   465  				return errors.New("connection already closed")
   466  			}
   467  			closed = true
   468  			return nil
   469  		}).AnyTimes()
   470  		return conn, nil
   471  	}).AnyTimes()
   472  
   473  	addr := mockAddr1
   474  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   475  
   476  	c, err := p.Get(context.TODO(), "tcp", addr, opt)
   477  	test.Assert(t, err == nil)
   478  	test.Assert(t, !closed)
   479  
   480  	err = p.Put(c)
   481  	test.Assert(t, err == nil)
   482  	test.Assert(t, !closed)
   483  
   484  	c2, err := p.Get(context.TODO(), "tcp", addr, opt)
   485  	test.Assert(t, err == nil)
   486  	test.Assert(t, c == c2)
   487  	test.Assert(t, !closed)
   488  
   489  	err = p.Discard(c2)
   490  	test.Assert(t, err == nil)
   491  	test.Assert(t, closed)
   492  
   493  	c3, err := p.Get(context.TODO(), "tcp", addr, opt)
   494  	test.Assert(t, err == nil)
   495  	test.Assert(t, c3 != c2)
   496  }
   497  
   498  func TestLongConnPoolCloseOnError(t *testing.T) {
   499  	ctrl := gomock.NewController(t)
   500  	defer ctrl.Finish()
   501  	p := newLongPoolForTest(0, 2, 5, time.Second)
   502  	defer p.Close()
   503  
   504  	var closed, read bool
   505  
   506  	d := mocksremote.NewMockDialer(ctrl)
   507  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   508  		na := utils.NewNetAddr(network, address)
   509  		conn := mocksnetpoll.NewMockConnection(ctrl)
   510  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   511  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   512  		conn.EXPECT().Close().DoAndReturn(func() error {
   513  			if closed {
   514  				return errors.New("connection already closed")
   515  			}
   516  			closed = true
   517  			return nil
   518  		}).AnyTimes()
   519  		conn.EXPECT().Read(gomock.Any()).DoAndReturn(func(b []byte) (int, error) {
   520  			// Error on second time reading
   521  			if read {
   522  				return 0, errors.New("read timeout")
   523  			}
   524  			read = true
   525  			return 0, nil
   526  		}).AnyTimes()
   527  		return conn, nil
   528  	}).AnyTimes()
   529  
   530  	addr := mockAddr1
   531  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   532  
   533  	c, err := p.Get(context.TODO(), "tcp", addr, opt)
   534  	test.Assert(t, err == nil)
   535  	test.Assert(t, !closed)
   536  
   537  	var buf []byte
   538  	_, err = c.Read(buf)
   539  	test.Assert(t, err == nil)
   540  	test.Assert(t, !closed)
   541  
   542  	err = p.Put(c)
   543  	test.Assert(t, err == nil)
   544  	test.Assert(t, !closed)
   545  
   546  	c2, err := p.Get(context.TODO(), "tcp", addr, opt)
   547  	test.Assert(t, err == nil)
   548  	test.Assert(t, c == c2)
   549  	test.Assert(t, !closed)
   550  
   551  	_, err = c.Read(buf)
   552  	test.Assert(t, err != nil)
   553  	test.Assert(t, !closed)
   554  
   555  	c3, err := p.Get(context.TODO(), "tcp", addr, opt)
   556  	test.Assert(t, err == nil)
   557  	test.Assert(t, c2 != c3)
   558  }
   559  
   560  func TestLongConnPoolCloseOnIdleTimeout(t *testing.T) {
   561  	ctrl := gomock.NewController(t)
   562  	defer ctrl.Finish()
   563  
   564  	idleTime := time.Second
   565  	p := newLongPoolForTest(0, 2, 5, idleTime)
   566  	defer p.Close()
   567  
   568  	var closed bool
   569  	d := mocksremote.NewMockDialer(ctrl)
   570  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   571  		na := utils.NewNetAddr(network, address)
   572  		conn := mocksnetpoll.NewMockConnection(ctrl)
   573  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   574  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   575  		conn.EXPECT().Close().DoAndReturn(func() error {
   576  			if closed {
   577  				return errors.New("connection already closed")
   578  			}
   579  			closed = true
   580  			return nil
   581  		}).AnyTimes()
   582  		return conn, nil
   583  	}).AnyTimes()
   584  
   585  	addr := mockAddr1
   586  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   587  
   588  	c, err := p.Get(context.TODO(), "tcp", addr, opt)
   589  	test.Assert(t, err == nil)
   590  	test.Assert(t, !closed)
   591  
   592  	err = p.Put(c)
   593  	test.Assert(t, err == nil)
   594  	test.Assert(t, !closed)
   595  
   596  	time.Sleep(idleTime * 3)
   597  
   598  	c2, err := p.Get(context.TODO(), "tcp", addr, opt)
   599  	test.Assert(t, err == nil)
   600  	test.Assert(t, c != c2)
   601  	test.Assert(t, closed) // the first connection should be closed
   602  }
   603  
   604  func TestLongConnPoolCloseOnClean(t *testing.T) {
   605  	ctrl := gomock.NewController(t)
   606  	defer ctrl.Finish()
   607  
   608  	p := newLongPoolForTest(0, 2, 5, time.Second)
   609  	defer p.Close()
   610  
   611  	var closed bool
   612  	var mu sync.RWMutex // fix data race in test
   613  	d := mocksremote.NewMockDialer(ctrl)
   614  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   615  		na := utils.NewNetAddr(network, address)
   616  		conn := mocksnetpoll.NewMockConnection(ctrl)
   617  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   618  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   619  		conn.EXPECT().Close().DoAndReturn(func() error {
   620  			mu.RLock()
   621  			if closed {
   622  				mu.RUnlock()
   623  				return errors.New("connection already closed")
   624  			}
   625  			mu.RUnlock()
   626  			mu.Lock()
   627  			closed = true
   628  			mu.Unlock()
   629  			return nil
   630  		}).AnyTimes()
   631  		return conn, nil
   632  	}).AnyTimes()
   633  
   634  	addr := mockAddr1
   635  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   636  
   637  	c, err := p.Get(context.TODO(), "tcp", addr, opt)
   638  	test.Assert(t, err == nil)
   639  	mu.RLock()
   640  	test.Assert(t, !closed)
   641  	mu.RUnlock()
   642  
   643  	err = p.Put(c)
   644  	test.Assert(t, err == nil)
   645  	mu.RLock()
   646  	test.Assert(t, !closed)
   647  	mu.RUnlock()
   648  
   649  	p.Clean("tcp", addr)
   650  	time.Sleep(100 * time.Millisecond) // Wait for the clean goroutine to finish
   651  	mu.RLock()
   652  	test.Assert(t, closed)
   653  	mu.RUnlock()
   654  }
   655  
   656  func TestLongConnPoolDiscardUnknownConnection(t *testing.T) {
   657  	ctrl := gomock.NewController(t)
   658  	defer ctrl.Finish()
   659  
   660  	p := newLongPoolForTest(0, 2, 5, time.Second)
   661  	defer p.Close()
   662  
   663  	var closed bool
   664  	conn := mocksnetpoll.NewMockConnection(ctrl)
   665  	conn.EXPECT().IsActive().Return(true).AnyTimes()
   666  	d := mocksremote.NewMockDialer(ctrl)
   667  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   668  		na := utils.NewNetAddr(network, address)
   669  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   670  		conn.EXPECT().Close().DoAndReturn(func() error {
   671  			if closed {
   672  				return errors.New("connection already closed")
   673  			}
   674  			closed = true
   675  			return nil
   676  		}).MaxTimes(1)
   677  		return conn, nil
   678  	}).AnyTimes()
   679  
   680  	network, address := "tcp", mockAddr1
   681  
   682  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   683  	c, err := p.Get(context.TODO(), network, address, opt)
   684  	test.Assert(t, err == nil)
   685  	test.Assert(t, !closed)
   686  
   687  	lc, ok := c.(*longConn)
   688  	test.Assert(t, ok)
   689  	test.Assert(t, lc.Conn == conn)
   690  
   691  	err = p.Discard(lc.Conn)
   692  	test.Assert(t, err == nil)
   693  	test.Assert(t, closed)
   694  }
   695  
   696  func TestConnPoolClose(t *testing.T) {
   697  	ctrl := gomock.NewController(t)
   698  	defer ctrl.Finish()
   699  
   700  	p := newLongPoolForTest(0, 2, 3, time.Second)
   701  
   702  	var closed int
   703  	d := mocksremote.NewMockDialer(ctrl)
   704  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   705  		na := utils.NewNetAddr(network, address)
   706  		conn := mocksnetpoll.NewMockConnection(ctrl)
   707  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   708  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   709  		conn.EXPECT().Close().DoAndReturn(func() error {
   710  			closed++
   711  			return nil
   712  		}).AnyTimes()
   713  		return conn, nil
   714  	}).AnyTimes()
   715  
   716  	network, address := "tcp", mockAddr0
   717  	conn0, err := p.Get(context.TODO(), network, address, dialer.ConnOption{Dialer: d})
   718  	test.Assert(t, err == nil)
   719  	p.Put(conn0)
   720  
   721  	network, address = "tcp", mockAddr1
   722  	conn1, err := p.Get(context.TODO(), network, address, dialer.ConnOption{Dialer: d})
   723  	test.Assert(t, err == nil)
   724  	p.Put(conn1)
   725  
   726  	connCount := 0
   727  	p.peerMap.Range(func(key, value interface{}) bool {
   728  		connCount++
   729  		return true
   730  	})
   731  	test.Assert(t, connCount == 2)
   732  
   733  	p.Close()
   734  	test.Assert(t, closed == 2, closed)
   735  	test.Assert(t, p.globalIdle.Now() == 0)
   736  
   737  	connCount = 0
   738  	p.peerMap.Range(func(key, value interface{}) bool {
   739  		connCount++
   740  		return true
   741  	})
   742  	test.Assert(t, connCount == 0)
   743  }
   744  
   745  func TestClosePoolAndSharedTicker(t *testing.T) {
   746  	ctrl := gomock.NewController(t)
   747  	defer ctrl.Finish()
   748  
   749  	var (
   750  		poolNum         = 10
   751  		idleTimeoutUnit = 111 * time.Millisecond
   752  		pools           = make([]*LongPool, poolNum)
   753  	)
   754  	// add new pool with different idleTimeout, increasing the number of shared ticker
   755  	for i := 0; i < poolNum; i++ {
   756  		pools[i] = newLongPoolForTest(0, 2, 3, time.Duration(i+1)*idleTimeoutUnit)
   757  	}
   758  	// close
   759  	for i := 0; i < poolNum; i++ {
   760  		pools[i].Close()
   761  		// should be removed from shardTickers
   762  		_, ok := sharedTickers.Load(pools[i])
   763  		test.Assert(t, !ok)
   764  	}
   765  }
   766  
   767  func TestLongConnPoolPutUnknownConnection(t *testing.T) {
   768  	ctrl := gomock.NewController(t)
   769  	defer ctrl.Finish()
   770  
   771  	p := newLongPoolForTest(0, 2, 5, time.Second)
   772  	defer p.Close()
   773  
   774  	var closed bool
   775  	conn := mocksnetpoll.NewMockConnection(ctrl)
   776  	conn.EXPECT().IsActive().Return(true).AnyTimes()
   777  	d := mocksremote.NewMockDialer(ctrl)
   778  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   779  		na := utils.NewNetAddr(network, address)
   780  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   781  		conn.EXPECT().Close().DoAndReturn(func() error {
   782  			if closed {
   783  				return errors.New("connection already closed")
   784  			}
   785  			closed = true
   786  			return nil
   787  		}).AnyTimes()
   788  		return conn, nil
   789  	}).AnyTimes()
   790  
   791  	network, address := "tcp", mockAddr1
   792  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   793  	c, err := p.Get(context.TODO(), network, address, opt)
   794  	test.Assert(t, err == nil)
   795  	test.Assert(t, !closed)
   796  
   797  	lc, ok := c.(*longConn)
   798  	test.Assert(t, ok)
   799  	test.Assert(t, lc.Conn == conn)
   800  
   801  	err = p.Put(lc.Conn)
   802  	test.Assert(t, err == nil)
   803  	test.Assert(t, closed)
   804  }
   805  
   806  func TestLongConnPoolEvict(t *testing.T) {
   807  	ctrl := gomock.NewController(t)
   808  	defer ctrl.Finish()
   809  
   810  	var (
   811  		idleTime = time.Millisecond * 100
   812  		minIdle  = 1
   813  		maxIdle  = 5
   814  	)
   815  	p := newLongPoolForTest(minIdle, maxIdle, maxIdle, idleTime)
   816  	defer p.Close()
   817  
   818  	d := mocksremote.NewMockDialer(ctrl)
   819  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   820  		na := utils.NewNetAddr(network, address)
   821  		conn := mocksnetpoll.NewMockConnection(ctrl)
   822  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   823  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   824  		conn.EXPECT().Close().DoAndReturn(func() error {
   825  			return nil
   826  		}).MaxTimes(1)
   827  		return conn, nil
   828  	}).AnyTimes()
   829  
   830  	network, address := "tcp", mockAddr1
   831  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   832  	// get a new conn
   833  	var conns []net.Conn
   834  	for i := 0; i < maxIdle; i++ {
   835  		c, err := p.Get(context.TODO(), network, address, opt)
   836  		test.Assert(t, err == nil)
   837  		conns = append(conns, c)
   838  	}
   839  	for i := 0; i < maxIdle; i++ {
   840  		// put conn back to the pool
   841  		err := p.Put(conns[i])
   842  		test.Assert(t, err == nil)
   843  	}
   844  
   845  	// only `minIdle` of connections should be kept in the pool
   846  	// 3 times of idleTime to make sure the eviction goroutine can be done
   847  	time.Sleep(3 * idleTime)
   848  	p.peerMap.Range(func(key, value interface{}) bool {
   849  		v := value.(*peer)
   850  		test.Assert(t, v.Len() == minIdle)
   851  		return true
   852  	})
   853  	// globalIdle should also be decreased when evicting
   854  	test.Assert(t, int(p.globalIdle.Now()) == minIdle, p.globalIdle.Now())
   855  	// get after eviction
   856  	conns = []net.Conn{}
   857  	for i := 0; i < maxIdle; i++ {
   858  		c, err := p.Get(context.TODO(), network, address, opt)
   859  		test.Assert(t, err == nil)
   860  		conns = append(conns, c)
   861  	}
   862  	for i := 0; i < maxIdle; i++ {
   863  		// put conn back to the pool
   864  		err := p.Put(conns[i])
   865  		test.Assert(t, err == nil)
   866  	}
   867  }
   868  
   869  func TestLongConnPoolDump(t *testing.T) {
   870  	ctrl := gomock.NewController(t)
   871  	defer ctrl.Finish()
   872  
   873  	idleTime := time.Second
   874  	p := newLongPoolForTest(0, 2, 3, idleTime)
   875  	defer p.Close()
   876  
   877  	d := mocksremote.NewMockDialer(ctrl)
   878  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   879  		na := utils.NewNetAddr(network, address)
   880  		conn := mocksnetpoll.NewMockConnection(ctrl)
   881  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   882  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   883  		conn.EXPECT().Close().AnyTimes()
   884  		return conn, nil
   885  	}).AnyTimes()
   886  
   887  	// get a new conn
   888  	conn, err := p.Get(context.TODO(), "tcp", mockAddr0, dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second})
   889  	test.Assert(t, err == nil, err)
   890  
   891  	// put conn back to the pool
   892  	err = p.Put(conn)
   893  	test.Assert(t, err == nil, err)
   894  
   895  	// test Dump() to get conn pool info
   896  	data := p.Dump().(map[string]interface{})
   897  	val := data[mockAddr0]
   898  	test.Assert(t, val != nil)
   899  
   900  	length := len(val.(PoolDump).ConnsDeadline)
   901  	test.Assert(t, length == 1)
   902  }
   903  
   904  func BenchmarkLongPoolGetOne(b *testing.B) {
   905  	ctrl := gomock.NewController(b)
   906  	defer ctrl.Finish()
   907  
   908  	d := mocksremote.NewMockDialer(ctrl)
   909  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   910  		na := utils.NewNetAddr(network, address)
   911  		conn := mocksnetpoll.NewMockConnection(ctrl)
   912  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   913  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   914  		conn.EXPECT().Close().AnyTimes()
   915  		return conn, nil
   916  	}).AnyTimes()
   917  
   918  	p := newLongPoolForTest(0, 100000, 1<<20, time.Second)
   919  	defer p.Close()
   920  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   921  
   922  	b.ResetTimer()
   923  	b.ReportAllocs()
   924  	b.RunParallel(func(pb *testing.PB) {
   925  		for pb.Next() {
   926  			p.Get(context.TODO(), "tcp", mockAddr1, opt)
   927  		}
   928  	})
   929  }
   930  
   931  func BenchmarkLongPoolGetRand2000(b *testing.B) {
   932  	ctrl := gomock.NewController(b)
   933  	defer ctrl.Finish()
   934  
   935  	d := mocksremote.NewMockDialer(ctrl)
   936  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   937  		na := utils.NewNetAddr(network, address)
   938  		conn := mocksnetpoll.NewMockConnection(ctrl)
   939  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   940  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   941  		conn.EXPECT().Close().AnyTimes()
   942  		return conn, nil
   943  	}).AnyTimes()
   944  	p := newLongPoolForTest(0, 50, 50, time.Second)
   945  	defer p.Close()
   946  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   947  
   948  	var addrs []string
   949  	for i := 0; i < 2000; i++ {
   950  		addrs = append(addrs, fmt.Sprintf("127.0.0.1:%d", 8000+rand.Intn(10000)))
   951  	}
   952  	b.ResetTimer()
   953  	b.ReportAllocs()
   954  	b.RunParallel(func(pb *testing.PB) {
   955  		for pb.Next() {
   956  			p.Get(context.TODO(), "tcp", addrs[rand.Intn(2000)], opt)
   957  		}
   958  	})
   959  }
   960  
   961  func BenchmarkLongPoolGetRand2000Mesh(b *testing.B) {
   962  	ctrl := gomock.NewController(b)
   963  	defer ctrl.Finish()
   964  
   965  	d := mocksremote.NewMockDialer(ctrl)
   966  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   967  		na := utils.NewNetAddr(network, address)
   968  		conn := mocksnetpoll.NewMockConnection(ctrl)
   969  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   970  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   971  		conn.EXPECT().Close().AnyTimes()
   972  		return conn, nil
   973  	}).AnyTimes()
   974  	p := newLongPoolForTest(0, 100000, 1<<20, time.Second)
   975  	defer p.Close()
   976  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   977  
   978  	var addrs []string
   979  	for i := 0; i < 2000; i++ {
   980  		addrs = append(addrs, fmt.Sprintf("127.0.0.1:%d", 8000+rand.Intn(10000)))
   981  	}
   982  	b.ResetTimer()
   983  	b.ReportAllocs()
   984  	b.RunParallel(func(pb *testing.PB) {
   985  		for pb.Next() {
   986  			p.Get(context.TODO(), "tcp", addrs[rand.Intn(2000)], opt)
   987  		}
   988  	})
   989  }
   990  
   991  // fakeNewLongPool creates a LongPool object and modifies it to fit tests.
   992  func newLongPoolForTest(minPeerAddr, maxPeerAddr, global int, timeout time.Duration) *LongPool {
   993  	cfg := connpool.IdleConfig{
   994  		MinIdlePerAddress: minPeerAddr,
   995  		MaxIdlePerAddress: maxPeerAddr,
   996  		MaxIdleGlobal:     global,
   997  		MaxIdleTimeout:    timeout,
   998  	}
   999  	return NewLongPool(mockDestService, cfg)
  1000  }
  1001  
  1002  func newLongConnForTest(ctrl *gomock.Controller, addr string) *longConn {
  1003  	conn := mocksnetpoll.NewMockConnection(ctrl)
  1004  	conn.EXPECT().IsActive().Return(true).AnyTimes()
  1005  	conn.EXPECT().Close().AnyTimes()
  1006  
  1007  	return &longConn{
  1008  		Conn:    conn,
  1009  		address: addr,
  1010  	}
  1011  }
  1012  
  1013  type reportStat struct {
  1014  	reuseSucceed int
  1015  	connSucceed  int
  1016  	connFailed   int
  1017  }
  1018  
  1019  func newMockConnReporter() *mockConnReporter {
  1020  	return &mockConnReporter{stats: make(map[string]*reportStat)}
  1021  }
  1022  
  1023  type mockConnReporter struct {
  1024  	stats map[string]*reportStat // serviceName:addr -> reportStat
  1025  	sync.RWMutex
  1026  }
  1027  
  1028  var _ Reporter = &mockConnReporter{}
  1029  
  1030  func (m *mockConnReporter) ConnSucceed(poolType ConnectionPoolType, serviceName string, addr net.Addr) {
  1031  	m.Lock()
  1032  	defer m.Unlock()
  1033  	key := getKey(serviceName, addr)
  1034  	s, ok := m.stats[key]
  1035  	if !ok {
  1036  		s = &reportStat{}
  1037  	}
  1038  	s.connSucceed++
  1039  	m.stats[key] = s
  1040  }
  1041  
  1042  func (m *mockConnReporter) ConnFailed(poolType ConnectionPoolType, serviceName string, addr net.Addr) {
  1043  	m.Lock()
  1044  	defer m.Unlock()
  1045  	key := getKey(serviceName, addr)
  1046  	s, ok := m.stats[key]
  1047  	if !ok {
  1048  		s = &reportStat{}
  1049  	}
  1050  	s.connFailed++
  1051  	m.stats[key] = s
  1052  }
  1053  
  1054  func (m *mockConnReporter) ReuseSucceed(poolType ConnectionPoolType, serviceName string, addr net.Addr) {
  1055  	m.Lock()
  1056  	defer m.Unlock()
  1057  	key := getKey(serviceName, addr)
  1058  	s, ok := m.stats[key]
  1059  	if !ok {
  1060  		s = &reportStat{}
  1061  	}
  1062  	s.reuseSucceed++
  1063  	m.stats[key] = s
  1064  }
  1065  
  1066  func (m *mockConnReporter) getConnSucceed(serviceName string, addr net.Addr) int {
  1067  	m.RLock()
  1068  	defer m.RUnlock()
  1069  	key := getKey(serviceName, addr)
  1070  	s, ok := m.stats[key]
  1071  	if !ok {
  1072  		return 0
  1073  	}
  1074  	return s.connSucceed
  1075  }
  1076  
  1077  func (m *mockConnReporter) getReuseSucceed(serviceName string, addr net.Addr) int {
  1078  	m.RLock()
  1079  	defer m.RUnlock()
  1080  	key := getKey(serviceName, addr)
  1081  	s, ok := m.stats[key]
  1082  	if !ok {
  1083  		return 0
  1084  	}
  1085  	return s.reuseSucceed
  1086  }
  1087  
  1088  func (m *mockConnReporter) reset() {
  1089  	m.Lock()
  1090  	defer m.Unlock()
  1091  	m.stats = make(map[string]*reportStat)
  1092  }
  1093  
  1094  func getKey(serviceName string, addr net.Addr) string {
  1095  	if addr != nil {
  1096  		return fmt.Sprintf("%s:%s", serviceName, addr.String())
  1097  	}
  1098  	return serviceName
  1099  }