Repository: go
Issue: database/sql: SetConnMaxIdleTime without SetConnMaxLifetime has no effect
Labels: NeedsInvestigation

<!--
Please answer these questions before submitting your issue. Thanks!
For questions please use one of our forums: https://github.com/golang/go/wiki/Questions
-->

### What version of Go are you using (`go version`)?

<pre>
$ go version
go version devel +b5c8d88bba Tue Aug 25 12:10:40 2020 -0400 darwin/amd64
</pre>

Custom version of `go branch [dev.link]` (1.16 as of about a week ago).

### Does this issue reproduce with the latest release?

Yes.

### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN="/Users/jfaller/go/bin"
GOCACHE="/Users/jfaller/Library/Caches/go-build"
GOENV="/Users/jfaller/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/jfaller/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/jfaller/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/Users/jfaller/src/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/Users/jfaller/src/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/jfaller/go/db_test/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/kt/9h7gfgy955lgbm_cys108l2c002pwh/T/go-build532706798=/tmp/go-build -gno-record-gcc-switches -fno-common"

</pre></details>

### What did you do?

<!--
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.
-->

Run the following:

```
package main

import (
	"database/sql"
	"fmt"
	"os"
	"strconv"
	"time"
)

func getVar(name string) int {
	val := os.Getenv(name)
	if len(val) == 0 {
		panic(fmt.Sprintf("error getting: %v", name))
	}
	v, err := strconv.Atoi(val)
	if err != nil {
		panic(fmt.Sprintf("error parsing %v %v", name, err))
	}
	return v
}

func main() {
	db, err := // connect to the db of your choice.
	if err != nil {
		panic(err)
	}
	defer db.Close()
	db.SetConnMaxIdleTime(time.Second * time.Duration(getVar("MAXIDLE")))
	db.SetConnMaxLifetime(time.Second * time.Duration(getVar("MAXLIFE")))
	db.SetMaxIdleConns(1)
	db.SetMaxOpenConns(1)
	sleep := time.Second*time.Duration(getVar("SLEEP"))
	for i := 0; i < 10; i++ {
		db.Ping()
		time.Sleep(sleep)
		print("\r", i)
	}
	fmt.Printf("\n%+v\n", db.Stats())
}
```
If you run the following with:

`MAXIDLE=1 MAXLIFE=0 SLEEP=5 go run .`

You expect to see some idle connections closed. If you set MAXLIFE as well, the program works correctly, eg:

`MAXIDLE=1 MAXLIFE=2 SLEEP=5 go run .`

### What did you expect to see?

`SetConnMaxIdleTime` should have an effect without setting `SetConnMaxLifetime`

### What did you see instead?

`SetConnMaxIdleTime` should either:

- Have an effect without calling `SetConnMaxLifetime`
- or be documented to have no effect without calling `SetConnMaxLifetime`

The error might be fixed by tweaking the callsite around `shortestIdleTimeLocked`