mirror of https://github.com/jackc/pgx.git
Make ConnPool.Acquire() non blocking, feedback
parent
7f9373c7c0
commit
809be4bfcb
51
conn_pool.go
51
conn_pool.go
|
@ -121,9 +121,10 @@ func (p *ConnPool) acquire(deadline *time.Time) (*Conn, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a deadline then start a timeout timer
|
// If there is a deadline then start a timeout timer
|
||||||
|
var timer *time.Timer
|
||||||
if deadline != nil {
|
if deadline != nil {
|
||||||
timer := time.AfterFunc(deadline.Sub(time.Now()), func() {
|
timer = time.AfterFunc(deadline.Sub(time.Now()), func() {
|
||||||
p.cond.Signal()
|
p.cond.Broadcast()
|
||||||
})
|
})
|
||||||
defer timer.Stop()
|
defer timer.Stop()
|
||||||
}
|
}
|
||||||
|
@ -147,29 +148,34 @@ func (p *ConnPool) acquire(deadline *time.Time) (*Conn, error) {
|
||||||
// there is no room in the list of allConnections
|
// there is no room in the list of allConnections
|
||||||
// (invalidateAcquired may remove our placeholder), try to re-acquire
|
// (invalidateAcquired may remove our placeholder), try to re-acquire
|
||||||
// the connection.
|
// the connection.
|
||||||
if len(p.allConnections) >= p.maxConnections {
|
if len(p.allConnections) < p.maxConnections {
|
||||||
c.Close()
|
// Put the new connection to the list.
|
||||||
return p.acquire(deadline)
|
c.poolResetCount = p.resetCount
|
||||||
|
p.allConnections = append(p.allConnections, c)
|
||||||
|
return c, nil
|
||||||
}
|
}
|
||||||
// Put the new connection to the list.
|
// There is no room for the just created connection.
|
||||||
c.poolResetCount = p.resetCount
|
// Close it and try to re-acquire.
|
||||||
p.allConnections = append(p.allConnections, c)
|
c.Close()
|
||||||
return c, nil
|
} else {
|
||||||
}
|
// All connections are in use and we cannot create more
|
||||||
|
if p.logLevel >= LogLevelWarn {
|
||||||
// All connections are in use and we cannot create more
|
p.logger.Warn("All connections in pool are busy - waiting...")
|
||||||
if p.logLevel >= LogLevelWarn {
|
}
|
||||||
p.logger.Warn("All connections in pool are busy - waiting...")
|
|
||||||
}
|
// Wait until there is an available connection OR room to create a new connection
|
||||||
|
for len(p.availableConnections) == 0 && len(p.allConnections) == p.maxConnections {
|
||||||
// Wait until there is an available connection OR room to create a new connection
|
if p.deadlinePassed(deadline) {
|
||||||
for len(p.availableConnections) == 0 && len(p.allConnections) == p.maxConnections {
|
return nil, errors.New("Timeout: All connections in pool are busy")
|
||||||
if p.deadlinePassed(deadline) {
|
}
|
||||||
return nil, errors.New("Timeout: All connections in pool are busy")
|
p.cond.Wait()
|
||||||
}
|
}
|
||||||
p.cond.Wait()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop the timer so that we do not spawn it on every acquire call.
|
||||||
|
if timer != nil {
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
return p.acquire(deadline)
|
return p.acquire(deadline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,9 +317,8 @@ func (p *ConnPool) createConnectionUnlocked() (*Conn, error) {
|
||||||
// afterConnectionCreated executes (if it is) afterConnect() callback and prepares
|
// afterConnectionCreated executes (if it is) afterConnect() callback and prepares
|
||||||
// all the known statements for the new connection.
|
// all the known statements for the new connection.
|
||||||
func (p *ConnPool) afterConnectionCreated(c *Conn) (*Conn, error) {
|
func (p *ConnPool) afterConnectionCreated(c *Conn) (*Conn, error) {
|
||||||
var err error
|
|
||||||
if p.afterConnect != nil {
|
if p.afterConnect != nil {
|
||||||
err = p.afterConnect(c)
|
err := p.afterConnect(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.die(err)
|
c.die(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package pgx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func compareConnSlices(slice1, slice2 []*Conn) bool {
|
||||||
|
if len(slice1) != len(slice2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, c := range slice1 {
|
||||||
|
if c != slice2[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnPoolRemoveFromAllConnections(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
pool := ConnPool{}
|
||||||
|
conn1 := &Conn{}
|
||||||
|
conn2 := &Conn{}
|
||||||
|
conn3 := &Conn{}
|
||||||
|
|
||||||
|
// First element
|
||||||
|
pool.allConnections = []*Conn{conn1, conn2, conn3}
|
||||||
|
pool.removeFromAllConnections(conn1)
|
||||||
|
if !compareConnSlices(pool.allConnections, []*Conn{conn2, conn3}) {
|
||||||
|
t.Fatal("First element test failed")
|
||||||
|
}
|
||||||
|
// Element somewhere in the middle
|
||||||
|
pool.allConnections = []*Conn{conn1, conn2, conn3}
|
||||||
|
pool.removeFromAllConnections(conn2)
|
||||||
|
if !compareConnSlices(pool.allConnections, []*Conn{conn1, conn3}) {
|
||||||
|
t.Fatal("Middle element test failed")
|
||||||
|
}
|
||||||
|
// Last element
|
||||||
|
pool.allConnections = []*Conn{conn1, conn2, conn3}
|
||||||
|
pool.removeFromAllConnections(conn3)
|
||||||
|
if !compareConnSlices(pool.allConnections, []*Conn{conn1, conn2}) {
|
||||||
|
t.Fatal("Last element test failed")
|
||||||
|
}
|
||||||
|
}
|
|
@ -176,23 +176,20 @@ func TestPoolNonBlockingConections(t *testing.T) {
|
||||||
t.Fatalf("Expected NewConnPool not to fail, instead it failed with")
|
t.Fatalf("Expected NewConnPool not to fail, instead it failed with")
|
||||||
}
|
}
|
||||||
|
|
||||||
done := make(chan bool)
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(maxConnections)
|
||||||
|
|
||||||
startedAt := time.Now()
|
startedAt := time.Now()
|
||||||
for i := 0; i < maxConnections; i++ {
|
for i := 0; i < maxConnections; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
_, err := pool.Acquire()
|
_, err := pool.Acquire()
|
||||||
done <- true
|
wg.Done()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Acquire() expected to fail but it did not")
|
t.Fatal("Acquire() expected to fail but it did not")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
wg.Wait()
|
||||||
// Wait for all the channels to succeedd
|
|
||||||
for i := 0; i < maxConnections; i++ {
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prior to createConnectionUnlocked() use the test took
|
// Prior to createConnectionUnlocked() use the test took
|
||||||
// maxConnections * openTimeout seconds to complete.
|
// maxConnections * openTimeout seconds to complete.
|
||||||
|
|
Loading…
Reference in New Issue