From f572b336b11ee16ea4abbaa1891070f321bb1635 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 4 May 2019 17:34:09 -0500 Subject: [PATCH] Improve pool Acquire / Release performance Release was using a goroutine every time. Now it only starts a goroutine when doing something that may take a while. (Destroy and afterRelease) --- bench_test.go | 37 ++++++++++++++++++++++++++++++++++++- pool/bench_test.go | 15 +++++++++++++++ pool/conn.go | 21 +++++++++++++-------- pool/pool.go | 8 +++++--- 4 files changed, 69 insertions(+), 12 deletions(-) diff --git a/bench_test.go b/bench_test.go index 83c84221..0722ae6a 100644 --- a/bench_test.go +++ b/bench_test.go @@ -26,7 +26,7 @@ func BenchmarkMinimalPreparedSelect(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - err = conn.QueryRow(context.Background(), "ps1", int64(i)).Scan(&n) + err = conn.QueryRow(context.Background(), "ps1", i).Scan(&n) if err != nil { b.Fatal(err) } @@ -37,6 +37,41 @@ func BenchmarkMinimalPreparedSelect(b *testing.B) { } } +func BenchmarkMinimalPgConnPreparedSelect(b *testing.B) { + conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE"))) + defer closeConn(b, conn) + + pgConn := conn.PgConn() + + _, err := pgConn.Prepare(context.Background(), "ps1", "select $1::int8", nil) + if err != nil { + b.Fatal(err) + } + + encodedBytes := make([]byte, 8) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + + rr := pgConn.ExecPrepared(context.Background(), "ps1", [][]byte{encodedBytes}, []int16{1}, []int16{1}) + if err != nil { + b.Fatal(err) + } + + for rr.NextRow() { + for i := range rr.Values() { + if bytes.Compare(rr.Values()[0], encodedBytes) != 0 { + b.Fatalf("unexpected values: %s %s", rr.Values()[i], encodedBytes) + } + } + } + _, err = rr.Close() + if err != nil { + b.Fatal(err) + } + } +} + func BenchmarkPointerPointerWithNullValues(b *testing.B) { conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE"))) defer closeConn(b, conn) diff --git a/pool/bench_test.go b/pool/bench_test.go index ad76fe4a..580bb5ec 100644 --- a/pool/bench_test.go +++ b/pool/bench_test.go @@ -10,6 +10,21 @@ import ( "github.com/stretchr/testify/require" ) +func BenchmarkAcquireAndRelease(b *testing.B) { + pool, err := pool.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE")) + require.NoError(b, err) + defer pool.Close() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + c, err := pool.Acquire(context.Background()) + if err != nil { + b.Fatal(err) + } + c.Release() + } +} + func BenchmarkMinimalPreparedSelectBaseline(b *testing.B) { config, err := pool.ParseConfig(os.Getenv("PGX_TEST_DATABASE")) require.NoError(b, err) diff --git a/pool/conn.go b/pool/conn.go index 8d2ea262..351ca6bb 100644 --- a/pool/conn.go +++ b/pool/conn.go @@ -26,14 +26,19 @@ func (c *Conn) Release() { res := c.res c.res = nil - go func() { - now := time.Now() - if !conn.IsAlive() || conn.PgConn().TxStatus != 'I' || (now.Sub(res.CreationTime()) > c.p.maxConnLifetime) { - res.Destroy() - return - } + now := time.Now() + if !conn.IsAlive() || conn.PgConn().TxStatus != 'I' || (now.Sub(res.CreationTime()) > c.p.maxConnLifetime) { + res.Destroy() + return + } - if c.p.afterRelease == nil || c.p.afterRelease(conn) { + if c.p.afterRelease == nil { + res.Release() + return + } + + go func() { + if c.p.afterRelease(conn) { res.Release() } else { res.Destroy() @@ -66,7 +71,7 @@ func (c *Conn) Begin(ctx context.Context, txOptions *pgx.TxOptions) (*pgx.Tx, er } func (c *Conn) Conn() *pgx.Conn { - return c.res.Value().(*connResource).conn + return c.connResource().conn } func (c *Conn) connResource() *connResource { diff --git a/pool/pool.go b/pool/pool.go index 5da61e11..c2a9634c 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -150,9 +150,11 @@ func ConnectConfig(ctx context.Context, config *Config) (*Pool, error) { return cr, nil }, func(value interface{}) { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - value.(*connResource).conn.Close(ctx) - cancel() + go func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + value.(*connResource).conn.Close(ctx) + cancel() + }() }, config.MaxConns, )