mirror of https://github.com/jackc/pgx.git
Fix cases where net conn write failure was not marking connection as dead
Also added loop to run these timing sensitive tests multiple times.pull/95/head
parent
bc4742b80a
commit
6e5fa60c4c
2
conn.go
2
conn.go
|
@ -483,6 +483,7 @@ func (c *Conn) Prepare(name, sql string) (ps *PreparedStatement, err error) {
|
||||||
|
|
||||||
_, err = c.conn.Write(wbuf.buf)
|
_, err = c.conn.Write(wbuf.buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
c.die(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,6 +545,7 @@ func (c *Conn) Deallocate(name string) (err error) {
|
||||||
|
|
||||||
_, err = c.conn.Write(wbuf.buf)
|
_, err = c.conn.Write(wbuf.buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
c.die(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -229,64 +229,69 @@ func TestPoolAcquireAndReleaseCycleAutoConnect(t *testing.T) {
|
||||||
func TestPoolReleaseDiscardsDeadConnections(t *testing.T) {
|
func TestPoolReleaseDiscardsDeadConnections(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
maxConnections := 3
|
// Run timing sensitive test many times
|
||||||
pool := createConnPool(t, maxConnections)
|
for i := 0; i < 50; i++ {
|
||||||
defer pool.Close()
|
func() {
|
||||||
|
maxConnections := 3
|
||||||
|
pool := createConnPool(t, maxConnections)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
var c1, c2 *pgx.Conn
|
var c1, c2 *pgx.Conn
|
||||||
var err error
|
var err error
|
||||||
var stat pgx.ConnPoolStat
|
var stat pgx.ConnPoolStat
|
||||||
|
|
||||||
|
if c1, err = pool.Acquire(); err != nil {
|
||||||
|
t.Fatalf("Unexpected error acquiring connection: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if c1 != nil {
|
||||||
|
pool.Release(c1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if c2, err = pool.Acquire(); err != nil {
|
||||||
|
t.Fatalf("Unexpected error acquiring connection: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if c2 != nil {
|
||||||
|
pool.Release(c2)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err = c2.Exec("select pg_terminate_backend($1)", c1.Pid); err != nil {
|
||||||
|
t.Fatalf("Unable to kill backend PostgreSQL process: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do something with the connection so it knows it's dead
|
||||||
|
rows, _ := c1.Query("select 1")
|
||||||
|
rows.Close()
|
||||||
|
if rows.Err() == nil {
|
||||||
|
t.Fatal("Expected error but none occurred")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c1.IsAlive() {
|
||||||
|
t.Fatal("Expected connection to be dead but it wasn't")
|
||||||
|
}
|
||||||
|
|
||||||
|
stat = pool.Stat()
|
||||||
|
if stat.CurrentConnections != 2 {
|
||||||
|
t.Fatalf("Unexpected CurrentConnections: %v", stat.CurrentConnections)
|
||||||
|
}
|
||||||
|
if stat.AvailableConnections != 0 {
|
||||||
|
t.Fatalf("Unexpected AvailableConnections: %v", stat.CurrentConnections)
|
||||||
|
}
|
||||||
|
|
||||||
if c1, err = pool.Acquire(); err != nil {
|
|
||||||
t.Fatalf("Unexpected error acquiring connection: %v", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if c1 != nil {
|
|
||||||
pool.Release(c1)
|
pool.Release(c1)
|
||||||
}
|
c1 = nil // so it doesn't get released again by the defer
|
||||||
}()
|
|
||||||
|
|
||||||
if c2, err = pool.Acquire(); err != nil {
|
stat = pool.Stat()
|
||||||
t.Fatalf("Unexpected error acquiring connection: %v", err)
|
if stat.CurrentConnections != 1 {
|
||||||
}
|
t.Fatalf("Unexpected CurrentConnections: %v", stat.CurrentConnections)
|
||||||
defer func() {
|
}
|
||||||
if c2 != nil {
|
if stat.AvailableConnections != 0 {
|
||||||
pool.Release(c2)
|
t.Fatalf("Unexpected AvailableConnections: %v", stat.CurrentConnections)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if _, err = c2.Exec("select pg_terminate_backend($1)", c1.Pid); err != nil {
|
|
||||||
t.Fatalf("Unable to kill backend PostgreSQL process: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// do something with the connection so it knows it's dead
|
|
||||||
rows, _ := c1.Query("select 1")
|
|
||||||
rows.Close()
|
|
||||||
if rows.Err() == nil {
|
|
||||||
t.Fatal("Expected error but none occurred")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c1.IsAlive() {
|
|
||||||
t.Fatal("Expected connection to be dead but it wasn't")
|
|
||||||
}
|
|
||||||
|
|
||||||
stat = pool.Stat()
|
|
||||||
if stat.CurrentConnections != 2 {
|
|
||||||
t.Fatalf("Unexpected CurrentConnections: %v", stat.CurrentConnections)
|
|
||||||
}
|
|
||||||
if stat.AvailableConnections != 0 {
|
|
||||||
t.Fatalf("Unexpected AvailableConnections: %v", stat.CurrentConnections)
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.Release(c1)
|
|
||||||
c1 = nil // so it doesn't get released again by the defer
|
|
||||||
|
|
||||||
stat = pool.Stat()
|
|
||||||
if stat.CurrentConnections != 1 {
|
|
||||||
t.Fatalf("Unexpected CurrentConnections: %v", stat.CurrentConnections)
|
|
||||||
}
|
|
||||||
if stat.AvailableConnections != 0 {
|
|
||||||
t.Fatalf("Unexpected AvailableConnections: %v", stat.CurrentConnections)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
39
conn_test.go
39
conn_test.go
|
@ -945,27 +945,32 @@ func TestFatalRxError(t *testing.T) {
|
||||||
func TestFatalTxError(t *testing.T) {
|
func TestFatalTxError(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
conn := mustConnect(t, *defaultConnConfig)
|
// Run timing sensitive test many times
|
||||||
defer closeConn(t, conn)
|
for i := 0; i < 50; i++ {
|
||||||
|
func() {
|
||||||
|
conn := mustConnect(t, *defaultConnConfig)
|
||||||
|
defer closeConn(t, conn)
|
||||||
|
|
||||||
otherConn, err := pgx.Connect(*defaultConnConfig)
|
otherConn, err := pgx.Connect(*defaultConnConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unable to establish connection: %v", err)
|
t.Fatalf("Unable to establish connection: %v", err)
|
||||||
}
|
}
|
||||||
defer otherConn.Close()
|
defer otherConn.Close()
|
||||||
|
|
||||||
_, err = otherConn.Exec("select pg_terminate_backend($1)", conn.Pid)
|
_, err = otherConn.Exec("select pg_terminate_backend($1)", conn.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unable to kill backend PostgreSQL process: %v", err)
|
t.Fatalf("Unable to kill backend PostgreSQL process: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = conn.Query("select 1")
|
_, err = conn.Query("select 1")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected error but none occurred")
|
t.Fatal("Expected error but none occurred")
|
||||||
}
|
}
|
||||||
|
|
||||||
if conn.IsAlive() {
|
if conn.IsAlive() {
|
||||||
t.Fatal("Connection should not be live but was")
|
t.Fatalf("Connection should not be live but was. Previous Query err: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue