jackc / jackc/pgx

connect_timeout is broken (+dsn parsing error)

Open
#1,327 8 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Go
Stars
14.3k
Forks
1.1k
Avg merge
6d 9h
Merged PRs (30d)
11

Description

```Go
package main

import (
"context"
"fmt"
"time"

"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgxpool"
)

type Pool struct {
listener *pgx.Conn // dedicated notifications listener
events chan string
*pgxpool.Pool
}

func PreparePool(ctx context.Context, connectionString string, listenChannels []string) (*Pool, error) {
var p Pool
p.events = make(chan string, 20)
poolConfig, err := pgxpool.ParseConfig(connectionString)
if err != nil {
return nil, err
}

listenerConfig := poolConfig.ConnConfig.Copy()
listenerConfig.OnNotification = func(_ *pgconn.PgConn, n *pgconn.Notification) {
p.events <- fmt.Sprintf("notification: %s, payload: %s", n.Channel, n.Payload)
}

listenerConfig.AfterConnect = func(ctx context.Context, conn *pgconn.PgConn) error {
go func() {
if len(listenChannels) == 0 {
return
}
var q string
for _, ch := range listenChannels {
q = fmt.Sprintf("%slisten %s; ", q, ch)
}
res := conn.Exec(ctx, q)
res.Close()

for {
err := conn.WaitForNotification(ctx)
if err != nil {
p.events <- err.Error() + "\nnotifications listener stopped!"
break
}
}

}()
return nil
}

p.listener, err = pgx.ConnectConfig(ctx, listenerConfig)
if err != nil {
return nil, err
}

poolConfig.ConnConfig.OnNotice = func(_ *pgconn.PgConn, n *pgconn.Notice) {
p.events <- (*pgconn.PgError)(n).Error()
}

p.Pool, err = pgxpool.NewWithConfig(ctx, poolConfig)
if err != nil {
p.listener.Close(ctx)
return nil, err
}

return &p, nil
}

func tryConnectionString(id int, cs string) {
fmt.Println("--- ", id)
ctx, cancel := context.WithCancel(context.Background())

p, err := PreparePool(ctx, cs, []string{"test"})
if err != nil {
fmt.Println(err)
cancel()
return
}

go func() {
for {
select {
case e := <-p.events:
fmt.Println(e)
case <-ctx.Done():
fmt.Println("finished")
return
}
}
}()

p.AcquireFunc(ctx, func(c *pgxpool.Conn) error {
_, err := c.Exec(ctx, `do
$$
begin
raise notice 'raise notice test';
perform pg_notify('test', 'payload');
raise warning 'raise warning test';
end
$$`)
return err
})

time.Sleep(100 * time.Millisecond)
cancel()
time.Sleep(100 * time.Millisecond)
}

func main() {
cs := "host=127.0.0.1 port=5432 user=postgres dbname=postgres password=qwe"
tryConnectionString(1, cs)
tryConnectionString(2, cs+" connect_timeout=60")
tryConnectionString(3, cs+"\t\n")
}
```

```
--- 1
NOTICE: raise notice test (SQLSTATE 00000)
WARNING: raise warning test (SQLSTATE 01000)
notification: test, payload: payload
finished
--- 2
timeout: context already done: context canceled
notifications listener stopped!
NOTICE: raise notice test (SQLSTATE 00000)
WARNING: raise warning test (SQLSTATE 01000)
finished
--- 3
cannot parse `host=127.0.0.1 port=5432 user=postgres dbname=postgres password=xxxxx`: failed to parse as DSN (invalid dsn)
```

Contributor guide

Open the contributing guide

Research direction

Start with the pgxpool.ParseConfig and pgx.ConnectConfig entry points shown in the reproducer, then run the three connection-string cases against PostgreSQL. Trace the connect_timeout and trailing-whitespace parsing behavior; done means the timeout case connects and the whitespace-terminated DSN parses without the reported errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, postgresql
Domain
databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.