Skip to content

Failed to use connection pool by SetTransport. #993

Open
@winterant

Description

@winterant

Usually, we'd like using connection pool to optimize system performance. For resty v3, SetTransport allows us to set max connection. However, it doesn't work. For example:

package main

import (
	"fmt"
	"log"
	"net/http"
	"resty.dev/v3"
	"time"
)

func main() {
	client := resty.New().SetTransport(&http.Transport{
		MaxIdleConnsPerHost: 1,
		MaxConnsPerHost:     1,
		IdleConnTimeout:     60 * time.Second,
	})
	// client.SetDebug(true)

	for i := 0; i < 2; i++ {
		fmt.Printf("\n====== test %d ======\n", i+1)

		resp, err := client.R().
			SetHeader("Connection", "keep-alive").
			EnableTrace().
			Get("http://httpbin.org/get")

		ti := resp.Request.TraceInfo()
		fmt.Println("Request Trace Info:")
		fmt.Println("  DNSLookup     :", ti.DNSLookup)
		fmt.Println("  ConnTime      :", ti.ConnTime)
		fmt.Println("  TCPConnTime   :", ti.TCPConnTime)
		fmt.Println("  TLSHandshake  :", ti.TLSHandshake)
		fmt.Println("  ServerTime    :", ti.ServerTime)
		fmt.Println("  ResponseTime  :", ti.ResponseTime)
		fmt.Println("  TotalTime     :", ti.TotalTime)
		fmt.Println("  IsConnReused  :", ti.IsConnReused)
		fmt.Println("  IsConnWasIdle :", ti.IsConnWasIdle)
		fmt.Println("  ConnIdleTime  :", ti.ConnIdleTime)

		if err != nil {
			log.Printf("error: %v\n", err)
		} else {
			fmt.Printf("response status: %s\n", resp.Status())
		}
	}
}

Stdout:

====== test 1 ======
Request Trace Info:
  DNSLookup     : 7.926584ms
  ConnTime      : 383.178667ms
  TCPConnTime   : 374.85325ms
  TLSHandshake  : 0s
  ServerTime    : 264.704667ms
  ResponseTime  : 108.208µs
  TotalTime     : 647.653292ms
  IsConnReused  : false
  IsConnWasIdle : false
  ConnIdleTime  : 0s
response status: 200 OK

====== test 2 ======
            <------ Hang at here. I can only force the process to be killed

Expectedly, the second time, TCPConn should be reused. But it doesn't, it will hang before the second time to call client.R().Get().

What's interesting is that if enable client.SetDebug(true) at the line 17 everything will meet expectations:

====== test 1 ======
2025/03/24 19:23:27.758266 DEBUG RESTY 
==============================================================================
~~~ REQUEST ~~~
GET  /get  HTTP/1.1
HOST   : httpbin.org
HEADERS:
        Accept-Encoding: gzip, deflate
        Connection: keep-alive
        User-Agent: go-resty/3.0.0-beta.1 (https://resty.dev)
BODY   :
***** NO CONTENT *****
------------------------------------------------------------------------------
~~~ RESPONSE ~~~
STATUS       : 200 OK
PROTO        : HTTP/1.1
RECEIVED AT  : 2025-03-24T19:23:27.757659+08:00
DURATION     : 725.12975ms
HEADERS      :
        Access-Control-Allow-Credentials: true
        Access-Control-Allow-Origin: *
        Connection: keep-alive
        Content-Length: 305
        Content-Type: application/json
        Date: Mon, 24 Mar 2025 11:23:27 GMT
        Server: gunicorn/19.9.0
BODY         :
{
   "args": {},
   "headers": {
      "Accept-Encoding": "gzip, deflate",
      "Host": "httpbin.org",
      "User-Agent": "go-resty/3.0.0-beta.1 (https://resty.dev)",
      "X-Amzn-Trace-Id": "Root=1-67e140af-4344002e59387a2e6dad486a"
   },
   "origin": "111.108.111.135",
   "url": "http://httpbin.org/get"
}

------------------------------------------------------------------------------
TRACE INFO:
  DNSLookupTime : 48.719917ms
  ConnTime      : 427.163833ms
  TCPConnTime   : 378.175875ms
  TLSHandshake  : 0s
  ServerTime    : 298.028709ms
  ResponseTime  : 154.958µs
  TotalTime     : 725.12975ms
  IsConnReused  : false
  IsConnWasIdle : false
  ConnIdleTime  : 0s
  RequestAttempt: 1
  RemoteAddr    : 100.28.166.39:80
==============================================================================
Request Trace Info:
  DNSLookup     : 48.719917ms
  ConnTime      : 427.163833ms
  TCPConnTime   : 378.175875ms
  TLSHandshake  : 0s
  ServerTime    : 298.028709ms
  ResponseTime  : 154.958µs
  TotalTime     : 725.12975ms
  IsConnReused  : false
  IsConnWasIdle : false
  ConnIdleTime  : 0s
response status: 200 OK

====== test 2 ======
2025/03/24 19:23:28.051593 DEBUG RESTY 
==============================================================================
~~~ REQUEST ~~~
GET  /get  HTTP/1.1
HOST   : httpbin.org
HEADERS:
        Accept-Encoding: gzip, deflate
        Connection: keep-alive
        User-Agent: go-resty/3.0.0-beta.1 (https://resty.dev)
BODY   :
***** NO CONTENT *****
------------------------------------------------------------------------------
~~~ RESPONSE ~~~
STATUS       : 200 OK
PROTO        : HTTP/1.1
RECEIVED AT  : 2025-03-24T19:23:28.051324+08:00
DURATION     : 292.801583ms
HEADERS      :
        Access-Control-Allow-Credentials: true
        Access-Control-Allow-Origin: *
        Connection: keep-alive
        Content-Length: 305
        Content-Type: application/json
        Date: Mon, 24 Mar 2025 11:23:28 GMT
        Server: gunicorn/19.9.0
BODY         :
{
   "args": {},
   "headers": {
      "Accept-Encoding": "gzip, deflate",
      "Host": "httpbin.org",
      "User-Agent": "go-resty/3.0.0-beta.1 (https://resty.dev)",
      "X-Amzn-Trace-Id": "Root=1-67e140b0-1b19e79171754077680b1b64"
   },
   "origin": "111.108.111.135",
   "url": "http://httpbin.org/get"
}

------------------------------------------------------------------------------
TRACE INFO:
  DNSLookupTime : 0s
  ConnTime      : 10.667µs
  TCPConnTime   : 0s
  TLSHandshake  : 0s
  ServerTime    : 292.700791ms
  ResponseTime  : 90.125µs
  TotalTime     : 292.801583ms
  IsConnReused  : true
  IsConnWasIdle : true
  ConnIdleTime  : 786.459µs
  RequestAttempt: 1
  RemoteAddr    : 100.28.166.39:80
==============================================================================
Request Trace Info:
  DNSLookup     : 0s
  ConnTime      : 10.667µs
  TCPConnTime   : 0s
  TLSHandshake  : 0s
  ServerTime    : 292.700791ms
  ResponseTime  : 90.125µs
  TotalTime     : 292.801583ms
  IsConnReused  : true
  IsConnWasIdle : true
  ConnIdleTime  : 786.459µs
response status: 200 OK

As expected, the second time that IsConnReused is true.

Anybody explains that? All my need is reuse connection without enable debug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions