Fix data race with Rows and ConnPool
In an effort to reduce memory allocations, Rows was stored on the Conn. This caused a race condition where Rows are closed and this returns the Conn to the Pool. The Pool could then give out the Conn again. Rows would then be reanimated and the original Rows could reclose it.
This commit is contained in:
@@ -53,7 +53,6 @@ type Conn struct {
|
|||||||
alive bool
|
alive bool
|
||||||
causeOfDeath error
|
causeOfDeath error
|
||||||
logger Logger
|
logger Logger
|
||||||
rows Rows
|
|
||||||
mr msgReader
|
mr msgReader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -410,6 +410,50 @@ func TestConnPoolQuery(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConnPoolQueryConcurrentLoad(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
pool := createConnPool(t, 10)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
go func(i int) {
|
||||||
|
var rowCount int32
|
||||||
|
|
||||||
|
rows, err := pool.Query("select generate_series(1,$1)", 1000)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(i, err)
|
||||||
|
t.Fatalf("pool.Query failed: %v", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var n int32
|
||||||
|
err = rows.Scan(&n)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(i, err)
|
||||||
|
t.Fatalf("rows.Scan failed: %v", err)
|
||||||
|
}
|
||||||
|
if n != rowCount+1 {
|
||||||
|
fmt.Println(i, err)
|
||||||
|
t.Fatalf("Expected n to be %d, but it was %d", rowCount+1, n)
|
||||||
|
}
|
||||||
|
rowCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.Err() != nil {
|
||||||
|
fmt.Println(i, err)
|
||||||
|
t.Fatalf("conn.Query failed: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rowCount != 1000 {
|
||||||
|
fmt.Println(i, err)
|
||||||
|
t.Error("Select called onDataRow wrong number of times")
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestConnPoolQueryRow(t *testing.T) {
|
func TestConnPoolQueryRow(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -354,8 +354,7 @@ func (rows *Rows) Values() ([]interface{}, error) {
|
|||||||
// be returned in an error state. So it is allowed to ignore the error returned
|
// be returned in an error state. So it is allowed to ignore the error returned
|
||||||
// from Query and handle it in *Rows.
|
// from Query and handle it in *Rows.
|
||||||
func (c *Conn) Query(sql string, args ...interface{}) (*Rows, error) {
|
func (c *Conn) Query(sql string, args ...interface{}) (*Rows, error) {
|
||||||
c.rows = Rows{conn: c, startTime: time.Now(), sql: sql, args: args, logger: c.logger}
|
rows := &Rows{conn: c, startTime: time.Now(), sql: sql, args: args, logger: c.logger}
|
||||||
rows := &c.rows
|
|
||||||
|
|
||||||
ps, ok := c.preparedStatements[sql]
|
ps, ok := c.preparedStatements[sql]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
Reference in New Issue
Block a user