github.com/cloudwego/kitex@v0.9.0/pkg/remote/trans/netpollmux/mux_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 netpollmux
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"math/rand"
    24  	"net"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/golang/mock/gomock"
    29  
    30  	mocksnetpoll "github.com/cloudwego/kitex/internal/mocks/netpoll"
    31  	mocksremote "github.com/cloudwego/kitex/internal/mocks/remote"
    32  
    33  	"github.com/cloudwego/kitex/internal/test"
    34  	dialer "github.com/cloudwego/kitex/pkg/remote"
    35  	"github.com/cloudwego/kitex/pkg/utils"
    36  )
    37  
    38  var (
    39  	mockAddr0 = "127.0.0.1:8000"
    40  	mockAddr1 = "127.0.0.1:8001"
    41  )
    42  
    43  func TestMuxConnPoolGetTimeout(t *testing.T) {
    44  	ctrl := gomock.NewController(t)
    45  	defer ctrl.Finish()
    46  
    47  	p := NewMuxConnPool(1)
    48  
    49  	d := mocksremote.NewMockDialer(ctrl)
    50  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
    51  		connectCost := time.Millisecond * 10
    52  		if timeout < connectCost {
    53  			return nil, errors.New("connect timeout")
    54  		}
    55  		na := utils.NewNetAddr(network, address)
    56  		conn := mocksnetpoll.NewMockConnection(ctrl)
    57  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
    58  		conn.EXPECT().SetOnRequest(gomock.Any())
    59  		conn.EXPECT().AddCloseCallback(gomock.Any())
    60  		conn.EXPECT().IsActive().Return(false).AnyTimes()
    61  		return conn, nil
    62  	}).AnyTimes()
    63  
    64  	var err error
    65  
    66  	_, err = p.Get(context.TODO(), "tcp", mockAddr0, dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second})
    67  	test.Assert(t, err == nil)
    68  
    69  	_, err = p.Get(context.TODO(), "tcp", mockAddr0, dialer.ConnOption{Dialer: d, ConnectTimeout: time.Millisecond})
    70  	test.Assert(t, err != nil)
    71  }
    72  
    73  func TestMuxConnPoolReuse(t *testing.T) {
    74  	ctrl := gomock.NewController(t)
    75  	defer ctrl.Finish()
    76  
    77  	p := NewMuxConnPool(1)
    78  
    79  	d := mocksremote.NewMockDialer(ctrl)
    80  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
    81  		na := utils.NewNetAddr(network, address)
    82  		conn := mocksnetpoll.NewMockConnection(ctrl)
    83  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
    84  		conn.EXPECT().SetOnRequest(gomock.Any())
    85  		conn.EXPECT().AddCloseCallback(gomock.Any())
    86  		conn.EXPECT().IsActive().Return(true).AnyTimes()
    87  		return conn, nil
    88  	}).AnyTimes()
    89  
    90  	addr1, addr2 := mockAddr1, "127.0.0.1:8002"
    91  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
    92  
    93  	count := make(map[net.Conn]int)
    94  	for i := 0; i < 10; i++ {
    95  		c, err := p.Get(context.TODO(), "tcp", addr1, opt)
    96  		test.Assert(t, err == nil)
    97  		count[c]++
    98  	}
    99  	test.Assert(t, len(count) == 1)
   100  
   101  	count = make(map[net.Conn]int)
   102  	for i := 0; i < 10; i++ {
   103  		c, err := p.Get(context.TODO(), "tcp", addr1, opt)
   104  		test.Assert(t, err == nil)
   105  		err = p.Put(c)
   106  		test.Assert(t, err == nil)
   107  		count[c]++
   108  	}
   109  	test.Assert(t, len(count) == 1)
   110  
   111  	count = make(map[net.Conn]int)
   112  	for i := 0; i < 10; i++ {
   113  		c, err := p.Get(context.TODO(), "tcp", addr1, opt)
   114  		test.Assert(t, err == nil)
   115  		err = p.Put(c)
   116  		test.Assert(t, err == nil)
   117  		count[c]++
   118  
   119  		c, err = p.Get(context.TODO(), "tcp", addr2, opt)
   120  		test.Assert(t, err == nil)
   121  		err = p.Put(c)
   122  		test.Assert(t, err == nil)
   123  		count[c]++
   124  	}
   125  	test.Assert(t, len(count) == 2)
   126  }
   127  
   128  func TestMuxConnPoolDiscardClean(t *testing.T) {
   129  	ctrl := gomock.NewController(t)
   130  	defer ctrl.Finish()
   131  
   132  	size := 4
   133  	conn := mocksnetpoll.NewMockConnection(ctrl)
   134  	d := mocksremote.NewMockDialer(ctrl)
   135  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   136  		conn.EXPECT().Close().Return(nil).Times(0)
   137  		conn.EXPECT().IsActive().Return(true).AnyTimes()
   138  		conn.EXPECT().SetOnRequest(gomock.Any())
   139  		conn.EXPECT().AddCloseCallback(gomock.Any())
   140  		return conn, nil
   141  	}).AnyTimes()
   142  
   143  	p := NewMuxConnPool(size)
   144  
   145  	network, address := "tcp", mockAddr0
   146  	var conns []net.Conn
   147  	for i := 0; i < 1024; i++ {
   148  		conn, err := p.Get(context.TODO(), network, address, dialer.ConnOption{Dialer: d})
   149  		test.Assert(t, err == nil)
   150  		conns = append(conns, conn)
   151  	}
   152  	for i := 0; i < 128; i++ {
   153  		p.Discard(conns[i])
   154  	}
   155  	conn.EXPECT().Close().Return(nil).Times(size)
   156  	p.Clean(network, address)
   157  }
   158  
   159  func TestMuxConnPoolClose(t *testing.T) {
   160  	ctrl := gomock.NewController(t)
   161  	defer ctrl.Finish()
   162  
   163  	conn := mocksnetpoll.NewMockConnection(ctrl)
   164  	conn.EXPECT().Close().Return(nil).Times(2)
   165  	conn.EXPECT().IsActive().Return(true).AnyTimes()
   166  	conn.EXPECT().SetOnRequest(gomock.Any()).AnyTimes()
   167  	conn.EXPECT().AddCloseCallback(gomock.Any()).AnyTimes()
   168  	d := mocksremote.NewMockDialer(ctrl)
   169  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   170  		return conn, nil
   171  	}).AnyTimes()
   172  
   173  	p := NewMuxConnPool(1)
   174  
   175  	network, address := "tcp", mockAddr0
   176  	_, err := p.Get(context.TODO(), network, address, dialer.ConnOption{Dialer: d})
   177  	test.Assert(t, err == nil)
   178  
   179  	network, address = "tcp", mockAddr1
   180  	_, err = p.Get(context.TODO(), network, address, dialer.ConnOption{Dialer: d})
   181  	test.Assert(t, err == nil)
   182  
   183  	connCount := 0
   184  	p.connMap.Range(func(key, value interface{}) bool {
   185  		connCount++
   186  		return true
   187  	})
   188  	test.Assert(t, connCount == 2)
   189  
   190  	p.Close()
   191  
   192  	connCount = 0
   193  	p.connMap.Range(func(key, value interface{}) bool {
   194  		connCount++
   195  		return true
   196  	})
   197  	test.Assert(t, connCount == 0)
   198  }
   199  
   200  func BenchmarkMuxPoolGetOne(b *testing.B) {
   201  	ctrl := gomock.NewController(b)
   202  	defer ctrl.Finish()
   203  
   204  	d := mocksremote.NewMockDialer(ctrl)
   205  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   206  		na := utils.NewNetAddr(network, address)
   207  		conn := mocksnetpoll.NewMockConnection(ctrl)
   208  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   209  		conn.EXPECT().SetOnRequest(gomock.Any()).AnyTimes()
   210  		conn.EXPECT().AddCloseCallback(gomock.Any()).AnyTimes()
   211  		conn.EXPECT().IsActive().Return(false).AnyTimes()
   212  		conn.EXPECT().Close().Return(nil).AnyTimes()
   213  		return conn, nil
   214  	}).AnyTimes()
   215  	p := NewMuxConnPool(1)
   216  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   217  
   218  	b.ResetTimer()
   219  	b.ReportAllocs()
   220  	b.RunParallel(func(pb *testing.PB) {
   221  		for pb.Next() {
   222  			p.Get(context.TODO(), "tcp", mockAddr1, opt)
   223  		}
   224  	})
   225  }
   226  
   227  func BenchmarkMuxPoolGetRand2000(b *testing.B) {
   228  	ctrl := gomock.NewController(b)
   229  	defer ctrl.Finish()
   230  
   231  	d := mocksremote.NewMockDialer(ctrl)
   232  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   233  		na := utils.NewNetAddr(network, address)
   234  		conn := mocksnetpoll.NewMockConnection(ctrl)
   235  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   236  		conn.EXPECT().SetOnRequest(gomock.Any()).AnyTimes()
   237  		conn.EXPECT().AddCloseCallback(gomock.Any()).AnyTimes()
   238  		conn.EXPECT().IsActive().Return(false).AnyTimes()
   239  		conn.EXPECT().Close().Return(nil).AnyTimes()
   240  		return conn, nil
   241  	}).AnyTimes()
   242  	p := NewMuxConnPool(1)
   243  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   244  
   245  	var addrs []string
   246  	for i := 0; i < 2000; i++ {
   247  		addrs = append(addrs, fmt.Sprintf("127.0.0.1:%d", 8000+rand.Intn(10000)))
   248  	}
   249  	b.ResetTimer()
   250  	b.ReportAllocs()
   251  	b.RunParallel(func(pb *testing.PB) {
   252  		for pb.Next() {
   253  			p.Get(context.TODO(), "tcp", addrs[rand.Intn(2000)], opt)
   254  		}
   255  	})
   256  }
   257  
   258  func BenchmarkMuxPoolGetRand2000Mesh(b *testing.B) {
   259  	ctrl := gomock.NewController(b)
   260  	defer ctrl.Finish()
   261  
   262  	d := mocksremote.NewMockDialer(ctrl)
   263  	d.EXPECT().DialTimeout(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(network, address string, timeout time.Duration) (net.Conn, error) {
   264  		na := utils.NewNetAddr(network, address)
   265  		conn := mocksnetpoll.NewMockConnection(ctrl)
   266  		conn.EXPECT().RemoteAddr().Return(na).AnyTimes()
   267  		conn.EXPECT().SetOnRequest(gomock.Any()).AnyTimes()
   268  		conn.EXPECT().AddCloseCallback(gomock.Any()).AnyTimes()
   269  		conn.EXPECT().IsActive().Return(false).AnyTimes()
   270  		conn.EXPECT().Close().Return(nil).AnyTimes()
   271  		return conn, nil
   272  	}).AnyTimes()
   273  	p := NewMuxConnPool(1)
   274  	opt := dialer.ConnOption{Dialer: d, ConnectTimeout: time.Second}
   275  
   276  	var addrs []string
   277  	for i := 0; i < 2000; i++ {
   278  		addrs = append(addrs, fmt.Sprintf("127.0.0.1:%d", 8000+rand.Intn(10000)))
   279  	}
   280  	b.ResetTimer()
   281  	b.ReportAllocs()
   282  	b.RunParallel(func(pb *testing.PB) {
   283  		for pb.Next() {
   284  			p.Get(context.TODO(), "tcp", addrs[rand.Intn(2000)], opt)
   285  		}
   286  	})
   287  }