trpc.group/trpc-go/trpc-go@v1.0.3/pool/connpool/connection_pool_test.go (about)

     1  //
     2  //
     3  // Tencent is pleased to support the open source community by making tRPC available.
     4  //
     5  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     6  // All rights reserved.
     7  //
     8  // If you have downloaded a copy of the tRPC source code from Tencent,
     9  // please note that tRPC source code is licensed under the  Apache 2.0 License,
    10  // A copy of the Apache 2.0 License is included in this file.
    11  //
    12  //
    13  
    14  package connpool
    15  
    16  import (
    17  	"context"
    18  	"errors"
    19  	"io"
    20  	"net"
    21  	"runtime"
    22  	"sync"
    23  	"sync/atomic"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  	"trpc.group/trpc-go/trpc-go/codec"
    30  )
    31  
    32  var (
    33  	ErrFrameSet       = errors.New("framer not set")
    34  	ErrReamFrame      = errors.New("ReadFrame failed")
    35  	ErrRead           = errors.New("Read failed")
    36  	ErrWrite          = errors.New("Write failed")
    37  	ErrSyscallConn    = errors.New("SyscallConn Failed")
    38  	ErrUnexpectedRead = errors.New("unexpected read from socket")
    39  
    40  	mockChecker = func(*PoolConn, bool) bool { return true }
    41  )
    42  
    43  func TestInitialMinIdle(t *testing.T) {
    44  	var established int32
    45  	p := NewConnectionPool(
    46  		WithMinIdle(10),
    47  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
    48  			atomic.AddInt32(&established, 1)
    49  			return &noopConn{closeFunc: func() {}}, nil
    50  		}),
    51  		WithHealthChecker(mockChecker))
    52  	defer closePool(t, p)
    53  
    54  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
    55  	require.Nil(t, err)
    56  	require.Nil(t, pc.Close())
    57  
    58  	start := time.Now()
    59  	for time.Since(start) < time.Second {
    60  		if established := atomic.LoadInt32(&established); established == 10 || established == 11 {
    61  			return
    62  		}
    63  		runtime.Gosched()
    64  	}
    65  	require.FailNow(t, "expected 10/11 established connections for fresh pool")
    66  }
    67  
    68  func TestKeepMinIdle(t *testing.T) {
    69  	var established int32
    70  	minIdle := 10
    71  	p := NewConnectionPool(
    72  		WithMinIdle(minIdle),
    73  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
    74  			atomic.AddInt32(&established, 1)
    75  
    76  			return &noopConn{closeFunc: func() {}}, nil
    77  		}),
    78  		WithHealthChecker(mockChecker))
    79  	defer closePool(t, p)
    80  
    81  	// clear idle conns
    82  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
    83  	require.Nil(t, err)
    84  	require.Nil(t, pc.Close())
    85  	start := time.Now()
    86  	for time.Since(start) < time.Second {
    87  		if established := atomic.LoadInt32(&established); established == 10 || established == 11 {
    88  			break
    89  		}
    90  		runtime.Gosched()
    91  	}
    92  	cnt := (int)(atomic.LoadInt32(&established))
    93  	for i := 0; i < cnt; i++ {
    94  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
    95  		assert.Nil(t, err)
    96  		defer pc.Close()
    97  	}
    98  
    99  	// wait for create idle conns background
   100  	start = time.Now()
   101  	target := (int32)(cnt + minIdle)
   102  	for time.Since(start) < defaultCheckInterval*2 {
   103  		if established := atomic.LoadInt32(&established); established == target {
   104  			return
   105  		}
   106  		runtime.Gosched()
   107  	}
   108  	require.FailNow(t, "expected 20 established connections for fresh pool")
   109  }
   110  
   111  func TestGetTokenWithoutMaxActive(t *testing.T) {
   112  	p := NewConnectionPool(
   113  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   114  			return &noopConn{closeFunc: func() {}}, nil
   115  		}),
   116  		WithHealthChecker(mockChecker))
   117  	defer closePool(t, p)
   118  
   119  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   120  	require.Nil(t, err)
   121  	require.Nil(t, pc.Close())
   122  }
   123  
   124  func TestGetTokenWait(t *testing.T) {
   125  	maxActive := 10
   126  	p := NewConnectionPool(
   127  		WithMaxActive(maxActive),
   128  		WithWait(true),
   129  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   130  			return &noopConn{closeFunc: func() {}}, nil
   131  		}),
   132  		WithHealthChecker(mockChecker))
   133  	defer closePool(t, p)
   134  
   135  	pcs := make([]net.Conn, 0, maxActive)
   136  	for i := 0; i < maxActive; i++ {
   137  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   138  		assert.Nil(t, err)
   139  		pcs = append(pcs, pc)
   140  	}
   141  
   142  	_, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   143  	require.Equal(t, err, context.DeadlineExceeded)
   144  
   145  	for _, pc := range pcs {
   146  		require.Nil(t, pc.Close())
   147  	}
   148  }
   149  
   150  func TestGetTokenNoWait(t *testing.T) {
   151  	maxActive := 10
   152  	p := NewConnectionPool(
   153  		WithMaxActive(maxActive),
   154  		WithWait(false),
   155  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   156  			return &noopConn{closeFunc: func() {}}, nil
   157  		}),
   158  		WithHealthChecker(mockChecker))
   159  	defer closePool(t, p)
   160  
   161  	pcs := make([]net.Conn, 0, maxActive)
   162  	for i := 0; i < maxActive; i++ {
   163  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   164  		assert.Nil(t, err)
   165  		pcs = append(pcs, pc)
   166  	}
   167  
   168  	_, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   169  	require.Equal(t, err, ErrPoolLimit)
   170  
   171  	for _, pc := range pcs {
   172  		require.Nil(t, pc.Close())
   173  	}
   174  
   175  	pc, err2 := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   176  	require.Nil(t, err2)
   177  	require.Nil(t, pc.Close())
   178  }
   179  
   180  func TestIdleTimeout(t *testing.T) {
   181  	var established int32
   182  	idleTimeout := time.Millisecond * 100
   183  	p := NewConnectionPool(
   184  		WithIdleTimeout(idleTimeout),
   185  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   186  			atomic.AddInt32(&established, 1)
   187  			return &noopConn{closeFunc: func() {
   188  				atomic.AddInt32(&established, -1)
   189  			}}, nil
   190  		}))
   191  	defer closePool(t, p)
   192  
   193  	cnt := 3
   194  	pcs := make([]net.Conn, 0, cnt)
   195  	for i := 0; i < cnt; i++ {
   196  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   197  		require.Nil(t, err)
   198  		pcs = append(pcs, pc)
   199  	}
   200  	require.Equal(t, atomic.LoadInt32(&established), int32(cnt))
   201  	for _, pc := range pcs {
   202  		require.Nil(t, pc.Close())
   203  	}
   204  
   205  	start := time.Now()
   206  	for time.Since(start) < defaultCheckInterval*2 {
   207  		if established := atomic.LoadInt32(&established); established == 0 {
   208  			break
   209  		}
   210  		runtime.Gosched()
   211  	}
   212  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   213  	require.Nil(t, err)
   214  	require.Equal(t, atomic.LoadInt32(&established), int32(1))
   215  	require.Nil(t, pc.Close())
   216  }
   217  
   218  func TestMaxConnLifetime(t *testing.T) {
   219  	var established int32
   220  	maxConnLifetime := time.Millisecond * 100
   221  	p := NewConnectionPool(
   222  		WithMaxConnLifetime(maxConnLifetime),
   223  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   224  			atomic.AddInt32(&established, 1)
   225  			return &noopConn{closeFunc: func() {
   226  				atomic.AddInt32(&established, -1)
   227  			}}, nil
   228  		}))
   229  	defer closePool(t, p)
   230  
   231  	cnt := 3
   232  	pcs := make([]net.Conn, 0, cnt)
   233  	for i := 0; i < cnt; i++ {
   234  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   235  		require.Nil(t, err)
   236  		pcs = append(pcs, pc)
   237  	}
   238  	require.Equal(t, atomic.LoadInt32(&established), int32(cnt))
   239  	for _, pc := range pcs {
   240  		require.Nil(t, pc.Close())
   241  	}
   242  
   243  	start := time.Now()
   244  	for time.Since(start) < defaultCheckInterval*2 {
   245  		if established := atomic.LoadInt32(&established); established == 0 {
   246  			break
   247  		}
   248  		runtime.Gosched()
   249  	}
   250  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   251  	require.Nil(t, err)
   252  	require.Equal(t, atomic.LoadInt32(&established), int32(1))
   253  	require.Nil(t, pc.Close())
   254  }
   255  
   256  func TestConcurrencyGet(t *testing.T) {
   257  	p := NewConnectionPool(
   258  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   259  			return &noopConn{closeFunc: func() {}}, nil
   260  		}),
   261  		WithHealthChecker(mockChecker))
   262  	defer closePool(t, p)
   263  
   264  	cnt := 5
   265  	pcs := make([]net.Conn, cnt)
   266  	var wg sync.WaitGroup
   267  	for i := 0; i < cnt; i++ {
   268  		wg.Add(1)
   269  		idx := i
   270  		go func() {
   271  			pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   272  			assert.Nil(t, err)
   273  			pcs[idx] = pc
   274  			wg.Done()
   275  		}()
   276  	}
   277  	wg.Wait()
   278  
   279  	for _, pc := range pcs {
   280  		require.Nil(t, pc.Close())
   281  	}
   282  }
   283  
   284  func TestPutForceClose(t *testing.T) {
   285  	var established int32
   286  	p := NewConnectionPool(
   287  		WithForceClose(true),
   288  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   289  			atomic.AddInt32(&established, 1)
   290  			return &noopConn{closeFunc: func() {
   291  				atomic.AddInt32(&established, -1)
   292  			}}, nil
   293  		}),
   294  		WithHealthChecker(mockChecker))
   295  	defer closePool(t, p)
   296  
   297  	cnt := 5
   298  	pcs := make([]net.Conn, 0, cnt)
   299  	for i := 0; i < cnt; i++ {
   300  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   301  		require.Nil(t, err)
   302  		pcs = append(pcs, pc)
   303  	}
   304  	for _, pc := range pcs {
   305  		require.Nil(t, pc.Close())
   306  	}
   307  	require.Equal(t, atomic.LoadInt32(&established), int32(0))
   308  }
   309  
   310  func TestIdleFifo(t *testing.T) {
   311  	p := NewConnectionPool(
   312  		WithPushIdleConnToTail(true),
   313  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   314  			return &noopConn{closeFunc: func() {}}, nil
   315  		}),
   316  		WithHealthChecker(mockChecker))
   317  	defer closePool(t, p)
   318  
   319  	cnt := 5
   320  	pcs := make([]net.Conn, 0, cnt)
   321  	for i := 0; i < cnt; i++ {
   322  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   323  		require.Nil(t, err)
   324  		pcs = append(pcs, pc)
   325  	}
   326  	for _, pc := range pcs {
   327  		time.Sleep(10 * time.Millisecond)
   328  		require.Nil(t, pc.Close())
   329  	}
   330  
   331  	pcs = make([]net.Conn, 0, cnt)
   332  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   333  	require.Nil(t, err)
   334  	pcs = append(pcs, pc)
   335  	created := pc.(*PoolConn).t
   336  	for i := 1; i < cnt; i++ {
   337  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   338  		require.Nil(t, err)
   339  		pcs = append(pcs, pc)
   340  		require.True(t, created.Before(pc.(*PoolConn).t))
   341  		created = pc.(*PoolConn).t
   342  	}
   343  
   344  	for _, pc := range pcs {
   345  		require.Nil(t, pc.Close())
   346  	}
   347  }
   348  
   349  func TestIdleLifo(t *testing.T) {
   350  	p := NewConnectionPool(
   351  		WithPushIdleConnToTail(false),
   352  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   353  			return &noopConn{closeFunc: func() {}}, nil
   354  		}),
   355  		WithHealthChecker(mockChecker))
   356  	defer closePool(t, p)
   357  
   358  	cnt := 5
   359  	pcs := make([]net.Conn, 0, cnt)
   360  	for i := 0; i < cnt; i++ {
   361  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   362  		require.Nil(t, err)
   363  		pcs = append(pcs, pc)
   364  	}
   365  	for _, pc := range pcs {
   366  		time.Sleep(10 * time.Millisecond)
   367  		require.Nil(t, pc.Close())
   368  	}
   369  
   370  	pcs = make([]net.Conn, 0, cnt)
   371  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   372  	require.Nil(t, err)
   373  	pcs = append(pcs, pc)
   374  	created := pc.(*PoolConn).t
   375  	for i := 1; i < cnt; i++ {
   376  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   377  		require.Nil(t, err)
   378  		pcs = append(pcs, pc)
   379  		require.True(t, created.After(pc.(*PoolConn).t))
   380  		created = pc.(*PoolConn).t
   381  	}
   382  
   383  	for _, pc := range pcs {
   384  		require.Nil(t, pc.Close())
   385  	}
   386  }
   387  
   388  func TestOverMaxIdle(t *testing.T) {
   389  	var established int32
   390  	maxIdle := 5
   391  	p := NewConnectionPool(
   392  		WithMaxIdle(maxIdle),
   393  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   394  			atomic.AddInt32(&established, 1)
   395  			return &noopConn{closeFunc: func() {
   396  				atomic.AddInt32(&established, -1)
   397  			}}, nil
   398  		}),
   399  		WithHealthChecker(mockChecker))
   400  	defer closePool(t, p)
   401  
   402  	cnt := 10
   403  	pcs := make([]net.Conn, 0, cnt)
   404  	for i := 0; i < cnt; i++ {
   405  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   406  		assert.Nil(t, err)
   407  		pcs = append(pcs, pc)
   408  	}
   409  	for _, pc := range pcs {
   410  		require.Nil(t, pc.Close())
   411  	}
   412  
   413  	require.Equal(t, atomic.LoadInt32(&established), int32(maxIdle))
   414  }
   415  
   416  func TestPoolClose(t *testing.T) {
   417  	var established int32
   418  	p := NewConnectionPool(
   419  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   420  			atomic.AddInt32(&established, 1)
   421  			return &noopConn{closeFunc: func() {
   422  				atomic.AddInt32(&established, -1)
   423  
   424  			}}, nil
   425  		}),
   426  		WithHealthChecker(mockChecker))
   427  
   428  	cnt := 10
   429  	pcs := make([]net.Conn, 0, cnt)
   430  	for i := 0; i < cnt; i++ {
   431  		pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   432  		assert.Nil(t, err)
   433  		pcs = append(pcs, pc)
   434  	}
   435  	for _, pc := range pcs {
   436  		require.Nil(t, pc.Close())
   437  	}
   438  
   439  	closePool(t, p)
   440  	require.Equal(t, atomic.LoadInt32(&established), int32(0))
   441  }
   442  
   443  func TestGetAfterPoolClose(t *testing.T) {
   444  	p := NewConnectionPool(
   445  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   446  			return &noopConn{closeFunc: func() {}}, nil
   447  		}),
   448  		WithHealthChecker(mockChecker))
   449  
   450  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   451  	require.Nil(t, err)
   452  	require.Nil(t, pc.Close())
   453  
   454  	closePool(t, p)
   455  
   456  	_, err2 := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   457  	require.Equal(t, err2, ErrPoolClosed)
   458  }
   459  
   460  func TestCloseConnAfterPoolClose(t *testing.T) {
   461  	var established int32
   462  	p := NewConnectionPool(
   463  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   464  			atomic.AddInt32(&established, 1)
   465  			return &noopConn{closeFunc: func() {
   466  				atomic.AddInt32(&established, -1)
   467  			}}, nil
   468  		}),
   469  		WithHealthChecker(mockChecker))
   470  	defer closePool(t, p)
   471  
   472  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   473  	require.Nil(t, err)
   474  
   475  	closePool(t, p)
   476  	require.Nil(t, pc.Close())
   477  	require.Equal(t, atomic.LoadInt32(&established), int32(0))
   478  }
   479  
   480  func TestCloseConnAfterConnCloseWithForceClose(t *testing.T) {
   481  	var established int32
   482  	p := NewConnectionPool(
   483  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   484  			atomic.AddInt32(&established, 1)
   485  			return &noopConn{closeFunc: func() {
   486  				atomic.AddInt32(&established, -1)
   487  			}}, nil
   488  		}),
   489  		WithHealthChecker(mockChecker),
   490  		WithForceClose(true))
   491  	defer closePool(t, p)
   492  
   493  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   494  	require.Nil(t, err)
   495  	require.Nil(t, pc.Close())
   496  	require.Equal(t, atomic.LoadInt32(&established), int32(0))
   497  	require.Equal(t, pc.Close(), ErrConnClosed)
   498  }
   499  
   500  func TestCloseConnAfterConnCloseWithoutForceClose(t *testing.T) {
   501  	var established int32
   502  	p := NewConnectionPool(
   503  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   504  			atomic.AddInt32(&established, 1)
   505  			return &noopConn{closeFunc: func() {
   506  				atomic.AddInt32(&established, -1)
   507  			}}, nil
   508  		}),
   509  		WithHealthChecker(mockChecker))
   510  	defer closePool(t, p)
   511  
   512  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   513  	require.Nil(t, err)
   514  	require.Nil(t, pc.Close())
   515  	require.Equal(t, pc.Close(), ErrConnInPool)
   516  }
   517  
   518  func TestReadFrameAfterClosed(t *testing.T) {
   519  	p := NewConnectionPool(
   520  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   521  			return &noopConn{closeFunc: func() {}}, nil
   522  		}),
   523  		WithHealthChecker(mockChecker),
   524  		WithForceClose(true))
   525  	defer closePool(t, p)
   526  
   527  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   528  	require.Nil(t, err)
   529  	require.Nil(t, pc.Close())
   530  
   531  	_, err2 := pc.(codec.Framer).ReadFrame()
   532  	require.Equal(t, err2, ErrConnClosed)
   533  }
   534  
   535  func TestReadFrameWithoutFramer(t *testing.T) {
   536  	p := NewConnectionPool(
   537  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   538  			return &noopConn{closeFunc: func() {}}, nil
   539  		}),
   540  		WithHealthChecker(mockChecker),
   541  		WithForceClose(true))
   542  	defer closePool(t, p)
   543  
   544  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   545  	require.Nil(t, err)
   546  	_, err2 := pc.(codec.Framer).ReadFrame()
   547  	require.Equal(t, err2, ErrFrameSet)
   548  	require.Equal(t, pc.Close(), ErrConnClosed)
   549  }
   550  
   551  func TestReadFrameFailed(t *testing.T) {
   552  	p := NewConnectionPool(
   553  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   554  			return &noopConn{closeFunc: func() {}}, nil
   555  		}),
   556  		WithHealthChecker(mockChecker),
   557  		WithForceClose(true))
   558  	defer closePool(t, p)
   559  
   560  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader,
   561  		DialTimeout:   time.Second,
   562  		FramerBuilder: &noopFramerBuilder{false},
   563  	})
   564  	require.Nil(t, err)
   565  	_, err2 := pc.(codec.Framer).ReadFrame()
   566  	require.Equal(t, err2, ErrReamFrame)
   567  	require.Equal(t, pc.Close(), ErrConnClosed)
   568  }
   569  
   570  func TestReadFrameWithCopyFrame(t *testing.T) {
   571  	p := NewConnectionPool(
   572  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   573  			return &noopConn{closeFunc: func() {}}, nil
   574  		}),
   575  		WithHealthChecker(mockChecker),
   576  		WithForceClose(true))
   577  
   578  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader,
   579  		DialTimeout:   time.Second,
   580  		FramerBuilder: &noopFramerBuilder{true},
   581  	})
   582  	require.Nil(t, err)
   583  	_, err2 := pc.(codec.Framer).ReadFrame()
   584  	require.Nil(t, err2)
   585  	require.Nil(t, pc.Close())
   586  }
   587  
   588  func TestWriteAfterClosed(t *testing.T) {
   589  	p := NewConnectionPool(
   590  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   591  			return &noopConn{closeFunc: func() {}}, nil
   592  		}),
   593  		WithHealthChecker(mockChecker),
   594  		WithForceClose(true))
   595  	defer closePool(t, p)
   596  
   597  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   598  	require.Nil(t, err)
   599  	require.Nil(t, pc.Close())
   600  
   601  	buf := make([]byte, 1)
   602  	_, err2 := pc.Write(buf)
   603  	require.Equal(t, err2, ErrConnClosed)
   604  	require.Equal(t, pc.Close(), ErrConnClosed)
   605  }
   606  
   607  func TestWriteFailed(t *testing.T) {
   608  	p := NewConnectionPool(
   609  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   610  			return &noopConn{suc: false, closeFunc: func() {}}, nil
   611  		}),
   612  		WithHealthChecker(mockChecker),
   613  		WithForceClose(true))
   614  	defer closePool(t, p)
   615  
   616  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   617  	require.Nil(t, err)
   618  
   619  	buf := make([]byte, 1)
   620  	_, err2 := pc.Write(buf)
   621  	require.Equal(t, err2, ErrWrite)
   622  	require.Equal(t, pc.Close(), ErrConnClosed)
   623  }
   624  
   625  func TestReadAfterClosed(t *testing.T) {
   626  	p := NewConnectionPool(
   627  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   628  			return &noopConn{closeFunc: func() {}}, nil
   629  		}),
   630  		WithHealthChecker(mockChecker),
   631  		WithForceClose(true))
   632  	defer closePool(t, p)
   633  
   634  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   635  	require.Nil(t, err)
   636  	require.Nil(t, pc.Close())
   637  
   638  	buf := make([]byte, 1)
   639  	_, err2 := pc.Read(buf)
   640  	require.Equal(t, err2, ErrConnClosed)
   641  	require.Equal(t, pc.Close(), ErrConnClosed)
   642  
   643  }
   644  
   645  func TestReadFailed(t *testing.T) {
   646  	p := NewConnectionPool(
   647  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   648  			return &noopConn{suc: false, closeFunc: func() {}}, nil
   649  		}),
   650  		WithHealthChecker(mockChecker),
   651  		WithForceClose(true))
   652  	defer closePool(t, p)
   653  
   654  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   655  	require.Nil(t, err)
   656  
   657  	buf := make([]byte, 1)
   658  	_, err2 := pc.Read(buf)
   659  	require.Equal(t, err2, ErrRead)
   660  	require.Equal(t, pc.Close(), ErrConnClosed)
   661  }
   662  
   663  func TestReadFailedFreeToken(t *testing.T) {
   664  	p := NewConnectionPool(
   665  		WithMaxActive(5),
   666  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   667  			return &noopConn{suc: false, closeFunc: func() {}}, nil
   668  		}),
   669  		WithHealthChecker(mockChecker),
   670  		WithForceClose(true))
   671  	defer closePool(t, p)
   672  
   673  	pc, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   674  	require.Nil(t, err)
   675  	require.Equal(t, 1, len(pc.(*PoolConn).pool.token))
   676  
   677  	buf := make([]byte, 1)
   678  	_, err2 := pc.Read(buf)
   679  	require.Equal(t, err2, ErrRead)
   680  	require.Equal(t, pc.Close(), ErrConnClosed)
   681  	require.Equal(t, 0, len(pc.(*PoolConn).pool.token))
   682  }
   683  
   684  func TestConnPoolIdleTimeout(t *testing.T) {
   685  	idleTimeout := time.Millisecond * 100
   686  	poolIdleTimeout := time.Millisecond * 100
   687  	getSize := func(p Pool) int {
   688  		pool, ok := p.(*pool)
   689  		assert.Equal(t, true, ok)
   690  		var count int
   691  		pool.connectionPools.Range(func(key, value interface{}) bool {
   692  			count++
   693  			return true
   694  		})
   695  		return count
   696  	}
   697  	p := NewConnectionPool(
   698  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   699  			return &noopConn{suc: false, closeFunc: func() {}}, nil
   700  		}),
   701  		WithIdleTimeout(idleTimeout),
   702  		WithMinIdle(5),
   703  		WithPoolIdleTimeout(poolIdleTimeout))
   704  
   705  	assert.Equal(t, 0, getSize(p))
   706  
   707  	c, err := p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   708  	assert.Nil(t, err)
   709  	assert.NotNil(t, c)
   710  	assert.Nil(t, c.Close())
   711  
   712  	assert.Equal(t, 1, getSize(p))
   713  	time.Sleep(poolIdleTimeout + defaultCheckInterval)
   714  	assert.Equal(t, 0, getSize(p))
   715  
   716  	// get again
   717  	c, err = p.Get(t.Name(), t.Name(), GetOptions{CustomReader: codec.NewReader, DialTimeout: time.Second})
   718  	assert.Nil(t, err)
   719  	assert.NotNil(t, c)
   720  	assert.Nil(t, c.Close())
   721  }
   722  
   723  func TestConnPoolTokenFreeOnReadFrameError(t *testing.T) {
   724  	const maxActive = 1
   725  	p := NewConnectionPool(
   726  		WithDialFunc(func(*DialOptions) (net.Conn, error) {
   727  			return &noopConn{suc: false, closeFunc: func() {}}, nil
   728  		}),
   729  		WithMaxActive(maxActive),
   730  	)
   731  	c, err := p.Get(t.Name(), t.Name(), GetOptions{DialTimeout: time.Second})
   732  	require.Nil(t, err)
   733  	pc, ok := c.(*PoolConn)
   734  	require.True(t, ok)
   735  	require.Equal(t, 1, len(pc.pool.token))
   736  	_, err = pc.ReadFrame() // Error of ReadFrame will put back (*PoolConn) to pool with forceClose=true.
   737  	require.NotNil(t, err)
   738  	require.Equal(t, 0, len(pc.pool.token))
   739  	ec := make(chan error)
   740  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   741  	defer cancel()
   742  	go func() {
   743  		ec <- pc.Close() // Close will try to put back (*PoolConn) to pool again.
   744  	}()
   745  	errTimeout := errors.New("pc.Close reaches its timeout, probably somewhere hangs")
   746  	select {
   747  	case err = <-ec:
   748  	case <-ctx.Done():
   749  		err = errTimeout
   750  	}
   751  	require.False(t, errors.Is(err, errTimeout))
   752  	go func() {
   753  		ec <- pc.pool.put(pc, true) // Put a closed pc to pool should not hang neither.
   754  	}()
   755  	select {
   756  	case err = <-ec:
   757  	case <-ctx.Done():
   758  		err = errTimeout
   759  	}
   760  	require.False(t, errors.Is(err, errTimeout))
   761  }
   762  
   763  func closePool(t *testing.T, p Pool) {
   764  	v, ok := p.(*pool)
   765  	if !ok {
   766  		return
   767  	}
   768  	key := getNodeKey(t.Name(), t.Name(), "")
   769  	if pool, ok := v.connectionPools.Load(key); ok {
   770  		pool.(*ConnectionPool).Close()
   771  	}
   772  }
   773  
   774  type noopConn struct {
   775  	closeFunc func()
   776  	suc       bool
   777  }
   778  
   779  func (c *noopConn) Read(bs []byte) (int, error) {
   780  	if !c.suc {
   781  		return len(bs), ErrRead
   782  	}
   783  	return len(bs), nil
   784  }
   785  func (c *noopConn) Write(bs []byte) (int, error) {
   786  	if !c.suc {
   787  		return len(bs), ErrWrite
   788  	}
   789  	return len(bs), nil
   790  }
   791  
   792  func (c *noopConn) Close() error {
   793  	c.closeFunc()
   794  	return nil
   795  }
   796  
   797  func (c *noopConn) LocalAddr() net.Addr              { return nil }
   798  func (c *noopConn) RemoteAddr() net.Addr             { return nil }
   799  func (c *noopConn) SetDeadline(time.Time) error      { return nil }
   800  func (c *noopConn) SetReadDeadline(time.Time) error  { return nil }
   801  func (c *noopConn) SetWriteDeadline(time.Time) error { return nil }
   802  
   803  type noopFramerBuilder struct {
   804  	suc bool
   805  }
   806  
   807  func (fb *noopFramerBuilder) New(io.Reader) codec.Framer {
   808  	return &noopFramer{fb.suc}
   809  }
   810  
   811  type noopFramer struct {
   812  	suc bool
   813  }
   814  
   815  func (fr *noopFramer) ReadFrame() ([]byte, error) {
   816  	if !fr.suc {
   817  		return make([]byte, 1), ErrReamFrame
   818  	}
   819  	return make([]byte, 1), nil
   820  }
   821  
   822  func (fr *noopFramer) IsSafe() bool {
   823  	return false
   824  }