package oltp
import (
"context"
"database/sql"
"path/filepath"
"testing"
"time"
)
func pragmaInt(t *testing.T, db *sql.DB, q string) int {
t.Helper()
var n int
if err := db.QueryRow(q).Scan(&n); err != nil {
t.Fatalf("%s: %v", q, err)
}
return n
}
func TestAccountPoolsRecycle(t *testing.T) {
oi, ol := connMaxIdleTime, connMaxLifetime
connMaxIdleTime, connMaxLifetime = 20*time.Millisecond, time.Nanosecond
defer func() { connMaxIdleTime, connMaxLifetime = oi, ol }()
d, err := Open(filepath.Join(t.TempDir(), "oltp.db"))
if err != nil {
t.Fatal(err)
}
defer d.Close()
var conns []*sql.Conn
for range 3 {
c, err := d.db.Conn(context.Background())
if err != nil {
t.Fatal(err)
}
conns = append(conns, c)
}
for _, c := range conns {
c.Close()
}
if got := d.db.Stats().Idle; got > 1 {
t.Errorf("FAIL: reader pool keeps %d idle, want at most 1", got)
}
if got := d.w.Stats().MaxOpenConnections; got != 1 {
t.Errorf("FAIL: writer pool max open = %d, want 1", got)
}
for name, db := range map[string]*sql.DB{"reader": d.db, "writer": d.w} {
for range 3 {
// busy_timeout is a per-connection pragma from the DSN; a
// recycled connection must still carry it.
if got := pragmaInt(t, db, `PRAGMA busy_timeout`); got != 5000 {
t.Errorf("FAIL: %s busy_timeout = %d on a recycled connection, want 5000", name, got)
}
time.Sleep(2 * time.Millisecond)
}
if st := db.Stats(); st.MaxLifetimeClosed == 0 {
t.Errorf("FAIL: %s pool recycled nothing: policy not applied", name)
}
}
// Idle closure and reopening.
deadline := time.Now().Add(5 * time.Second)
for d.db.Stats().OpenConnections != 0 && time.Now().Before(deadline) {
time.Sleep(10 * time.Millisecond)
}
if got := d.db.Stats().OpenConnections; got != 0 {
t.Errorf("FAIL: %d reader connections open after idle time", got)
}
if got := pragmaInt(t, d.db, `PRAGMA busy_timeout`); got != 5000 {
t.Errorf("FAIL: busy_timeout after reopen = %d", got)
}
if !t.Failed() {
t.Log("ok")
}
}