@@ -60,7 +60,7 @@ var _ = Describe("ConnPool", func() {
6060 Expect (connPool .Close ()).NotTo (HaveOccurred ())
6161 close (closedChan )
6262
63- // We wait for 1 second and believe that checkMinIdleConns has been executed.
63+ // We wait for 1 second and believe that checkIdleConns has been executed.
6464 time .Sleep (time .Second )
6565
6666 Expect (connPool .Stats ()).To (Equal (& pool.Stats {
@@ -519,7 +519,7 @@ func TestDialerRetryConfiguration(t *testing.T) {
519519 })
520520}
521521
522- var _ = Describe ("asyncNewConn " , func () {
522+ var _ = Describe ("queuedNewConn " , func () {
523523 ctx := context .Background ()
524524
525525 It ("should successfully create connection when pool is exhausted" , func () {
@@ -607,7 +607,7 @@ var _ = Describe("asyncNewConn", func() {
607607
608608 It ("should handle context cancellation while waiting for connection result" , func () {
609609 // This test focuses on proper error handling when context is cancelled
610- // during asyncNewConn execution (not testing connection reuse)
610+ // during queuedNewConn execution (not testing connection reuse)
611611
612612 slowDialer := func (ctx context.Context ) (net.Conn , error ) {
613613 // Simulate slow dialing
@@ -633,7 +633,7 @@ var _ = Describe("asyncNewConn", func() {
633633 defer cancel ()
634634
635635 // This request should timeout while waiting for connection creation result
636- // Testing the error handling path in asyncNewConn select statement
636+ // Testing the error handling path in queuedNewConn select statement
637637 done := make (chan struct {})
638638 var err2 error
639639 go func () {
@@ -781,7 +781,7 @@ var _ = Describe("asyncNewConn", func() {
781781 go func () {
782782 defer GinkgoRecover ()
783783 defer close (done1 )
784- // This will trigger asyncNewConn since pool is full
784+ // This will trigger queuedNewConn since pool is full
785785 conn , err := testPool .Get (ctx )
786786 if err == nil {
787787 // Put connection back to pool after creation
@@ -822,6 +822,93 @@ var _ = Describe("asyncNewConn", func() {
822822
823823 testPool .Put (ctx , conn3 )
824824 })
825+
826+ It ("recover queuedNewConn panic" , func () {
827+ opt := & pool.Options {
828+ Dialer : func (ctx context.Context ) (net.Conn , error ) {
829+ panic ("test panic in queuedNewConn" )
830+ },
831+ PoolSize : int32 (10 ),
832+ MaxConcurrentDials : 10 ,
833+ DialTimeout : 1 * time .Second ,
834+ PoolTimeout : 1 * time .Second ,
835+ }
836+ testPool := pool .NewConnPool (opt )
837+ defer testPool .Close ()
838+
839+ // Trigger queuedNewConn - calling Get() on empty pool will trigger it
840+ // Since dialer will panic, it should be handled by recover
841+ ctx , cancel := context .WithTimeout (context .Background (), 2 * time .Second )
842+ defer cancel ()
843+
844+ // Try to get connections multiple times, each will trigger panic but should be properly recovered
845+ for i := 0 ; i < 3 ; i ++ {
846+ conn , err := testPool .Get (ctx )
847+ // Connection should be nil, error should exist (panic converted to error)
848+ Expect (conn ).To (BeNil ())
849+ Expect (err ).To (HaveOccurred ())
850+ }
851+
852+ // Verify state after panic recovery:
853+ // - turn should be properly released (QueueLen() == 0)
854+ // - connection counts should be correct (TotalConns == 0, IdleConns == 0)
855+ Eventually (func () bool {
856+ stats := testPool .Stats ()
857+ queueLen := testPool .QueueLen ()
858+ return stats .TotalConns == 0 && stats .IdleConns == 0 && queueLen == 0
859+ }, "3s" , "50ms" ).Should (BeTrue ())
860+ })
861+
862+ It ("should handle connection creation success but delivery failure (putIdleConn path)" , func () {
863+ // This test covers the most important untested branch in queuedNewConn:
864+ // cnErr == nil && !delivered -> putIdleConn()
865+
866+ // Use slow dialer to ensure request times out before connection is ready
867+ slowDialer := func (ctx context.Context ) (net.Conn , error ) {
868+ // Delay long enough for client request to timeout first
869+ time .Sleep (300 * time .Millisecond )
870+ return newDummyConn (), nil
871+ }
872+
873+ testPool := pool .NewConnPool (& pool.Options {
874+ Dialer : slowDialer ,
875+ PoolSize : 1 ,
876+ MaxConcurrentDials : 2 ,
877+ DialTimeout : 500 * time .Millisecond , // Long enough for dialer to complete
878+ PoolTimeout : 100 * time .Millisecond , // Client requests will timeout quickly
879+ })
880+ defer testPool .Close ()
881+
882+ // Record initial idle connection count
883+ initialIdleConns := testPool .Stats ().IdleConns
884+
885+ // Make a request that will timeout
886+ // This request will start queuedNewConn, create connection, but fail to deliver due to timeout
887+ shortCtx , cancel := context .WithTimeout (context .Background (), 150 * time .Millisecond )
888+ defer cancel ()
889+
890+ conn , err := testPool .Get (shortCtx )
891+
892+ // Request should fail due to timeout
893+ Expect (err ).To (HaveOccurred ())
894+ Expect (conn ).To (BeNil ())
895+
896+ // However, background queuedNewConn should continue and complete connection creation
897+ // Since it cannot deliver (request timed out), it should call putIdleConn to add connection to idle pool
898+ Eventually (func () bool {
899+ stats := testPool .Stats ()
900+ return stats .IdleConns > initialIdleConns
901+ }, "1s" , "50ms" ).Should (BeTrue ())
902+
903+ // Verify the connection can indeed be used by subsequent requests
904+ conn2 , err2 := testPool .Get (context .Background ())
905+ Expect (err2 ).NotTo (HaveOccurred ())
906+ Expect (conn2 ).NotTo (BeNil ())
907+ Expect (conn2 .IsUsable ()).To (BeTrue ())
908+
909+ // Cleanup
910+ testPool .Put (context .Background (), conn2 )
911+ })
825912})
826913
827914func init () {
0 commit comments