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 }