mirror of https://github.com/jackc/pgx.git
ConnPool.Begin retry logic checks connection IsAlive
ErrDeadConn is returned when calling an already dead connection. But the initial failure returns the real error. So we check for IsAlive instead of ErrDeadConn. Added test for ConnPool.Begin retry logic.pull/95/head
parent
6e5fa60c4c
commit
93aa2b2e80
16
conn_pool.go
16
conn_pool.go
|
@ -228,13 +228,19 @@ func (p *ConnPool) BeginIso(iso string) (*Tx, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, err := c.BeginIso(iso)
|
tx, err := c.BeginIso(iso)
|
||||||
if err == ErrDeadConn {
|
|
||||||
p.Release(c)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
alive := c.IsAlive()
|
||||||
p.Release(c)
|
p.Release(c)
|
||||||
return nil, err
|
|
||||||
|
// If connection is still alive then the error is not something trying
|
||||||
|
// again on a new connection would fix, so just return the error. But
|
||||||
|
// if the connection is dead try to acquire a new connection and try
|
||||||
|
// again.
|
||||||
|
if alive {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.pool = p
|
tx.pool = p
|
||||||
|
|
|
@ -363,41 +363,46 @@ func TestConnPoolTransactionIso(t *testing.T) {
|
||||||
func TestConnPoolBeginRetry(t *testing.T) {
|
func TestConnPoolBeginRetry(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pool := createConnPool(t, 2)
|
// Run timing sensitive test many times
|
||||||
defer pool.Close()
|
for i := 0; i < 50; i++ {
|
||||||
|
func() {
|
||||||
|
pool := createConnPool(t, 2)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
killerConn, err := pool.Acquire()
|
killerConn, err := pool.Acquire()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer pool.Release(killerConn)
|
defer pool.Release(killerConn)
|
||||||
|
|
||||||
victimConn, err := pool.Acquire()
|
victimConn, err := pool.Acquire()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
pool.Release(victimConn)
|
pool.Release(victimConn)
|
||||||
|
|
||||||
// Terminate connection that was released to pool
|
// Terminate connection that was released to pool
|
||||||
if _, err = killerConn.Exec("select pg_terminate_backend($1)", victimConn.Pid); err != nil {
|
if _, err = killerConn.Exec("select pg_terminate_backend($1)", victimConn.Pid); err != nil {
|
||||||
t.Fatalf("Unable to kill backend PostgreSQL process: %v", err)
|
t.Fatalf("Unable to kill backend PostgreSQL process: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since victimConn is the only available connection in the pool, pool.Begin should
|
// Since victimConn is the only available connection in the pool, pool.Begin should
|
||||||
// try to use it, fail, and allocate another connection
|
// try to use it, fail, and allocate another connection
|
||||||
tx, err := pool.Begin()
|
tx, err := pool.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("pool.Begin failed: %v", err)
|
t.Fatalf("pool.Begin failed: %v", err)
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
var txPid int32
|
var txPid int32
|
||||||
err = tx.QueryRow("select pg_backend_pid()").Scan(&txPid)
|
err = tx.QueryRow("select pg_backend_pid()").Scan(&txPid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tx.QueryRow Scan failed: %v", err)
|
t.Fatalf("tx.QueryRow Scan failed: %v", err)
|
||||||
}
|
}
|
||||||
if txPid == victimConn.Pid {
|
if txPid == victimConn.Pid {
|
||||||
t.Error("Expected txPid to defer from killed conn pid, but it didn't")
|
t.Error("Expected txPid to defer from killed conn pid, but it didn't")
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue