// OpenFromConnPool takes the existing *pgx.ConnPool pool and returns a *sql.DB // with pool as the backend. This enables full control over the connection // process and configuration while maintaining compatibility with the // database/sql interface. In addition, by calling Driver() on the returned // *sql.DB and typecasting to *stdlib.Driver a reference to the pgx.ConnPool can // be reaquired later. This allows fast paths targeting pgx to be used while // still maintaining compatibility with other databases and drivers. // // pool connection size must be at least 2. func OpenFromConnPool(pool *pgx.ConnPool) (*sql.DB, error) { d := &Driver{Pool: pool} name := fmt.Sprintf("pgx-%d", openFromConnPoolCount) openFromConnPoolCount++ sql.Register(name, d) db, err := sql.Open(name, "") if err != nil { return nil, err } // Presumably OpenFromConnPool is being used because the user wants to use // database/sql most of the time, but fast path with pgx some of the time. // Allow database/sql to use all the connections, but release 2 idle ones. // Don't have database/sql immediately release all idle connections because // that would mean that prepared statements would be lost (which kills // performance if the prepared statements constantly have to be reprepared) stat := pool.Stat() if stat.MaxConnections <= 2 { return nil, errors.New("pool connection size must be at least 2") } db.SetMaxIdleConns(stat.MaxConnections - 2) db.SetMaxOpenConns(stat.MaxConnections) return db, nil }
func unlistenAndRelease(pool *pgx.ConnPool, conn *pgx.Conn, channel string) { _, err := conn.Exec(fmt.Sprintf(sqlUnlisten, channel)) if err != nil { conn.Close() return } pool.Release(conn) }
func setupStressDB(t *testing.T, pool *pgx.ConnPool) { _, err := pool.Exec(` drop table if exists widgets; create table widgets( id serial primary key, name varchar not null, description text, creation_time timestamptz ); `) if err != nil { t.Fatal(err) } }
func txInsertRollback(pool *pgx.ConnPool, actionNum int) error { tx, err := pool.Begin() if err != nil { return err } sql := ` insert into widgets(name, description, creation_time) values($1, $2, $3)` _, err = tx.Exec(sql, fake.ProductName(), fake.Sentences(), time.Now()) if err != nil { return err } return tx.Rollback() }
func listenAndPoolUnlistens(pool *pgx.ConnPool, actionNum int) error { conn, err := pool.Acquire() if err != nil { return err } defer pool.Release(conn) err = conn.Listen("stress") if err != nil { return err } _, err = conn.WaitForNotification(100 * time.Millisecond) if err == pgx.ErrNotificationTimeout { return nil } return err }
func txMultipleQueries(pool *pgx.ConnPool, actionNum int) error { tx, err := pool.Begin() if err != nil { return err } defer tx.Rollback() errExpectedTxDeath := errors.New("Expected tx death") actions := []struct { name string fn func() error }{ {"insertUnprepared", func() error { return insertUnprepared(tx, actionNum) }}, {"queryRowWithoutParams", func() error { return queryRowWithoutParams(tx, actionNum) }}, {"query", func() error { return query(tx, actionNum) }}, {"queryCloseEarly", func() error { return queryCloseEarly(tx, actionNum) }}, {"queryErrorWhileReturningRows", func() error { err := queryErrorWhileReturningRows(tx, actionNum) if err != nil { return err } return errExpectedTxDeath }}, } for i := 0; i < 20; i++ { action := actions[rand.Intn(len(actions))] err := action.fn() if err == errExpectedTxDeath { return nil } else if err != nil { return err } } return tx.Commit() }
func truncateAndClose(pool *pgx.ConnPool) { if _, err := pool.Exec("TRUNCATE TABLE que_jobs"); err != nil { panic(err) } pool.Close() }
func notify(pool *pgx.ConnPool, actionNum int) error { _, err := pool.Exec("notify stress") return err }