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 }