pgxpool.Connect返回的连接池为nil,或者在没有错误的情况下迅速变为nil。

huangapple go评论75阅读模式
英文:

The pool returned by pgxpool.Connect is nil or becomes nil quickly without error

问题

我有以下代码用于连接到Postgres数据库:

func connectToPostgres(ctx context.Context, url string) (*pgxpool.Pool, error) {
	var err error
	for i := 0; i < 5; i++ {
		p, err := pgxpool.Connect(ctx, url)
		if err != nil || p == nil {
			time.Sleep(3 * time.Second)
			continue
		}
		log.Printf("pool returned from connect: %s", p)
		return p, nil
	}
	return nil, errors.Wrap(err, "timed out waiting to connect postgres")
}

使用场景是在使用docker-compose启动服务器时等待Postgres可用。即使代码在p == nil时休眠,但在第一个返回之前的日志中打印出:pool returned from connect: %!s(*pgxpool.Pool=<nil>)

是否有可能pgxpool中的后台进程会使p == nil

对于为什么会发生这种情况,有什么想法吗?

编辑:这似乎只发生在通过docker-compose运行我的应用程序和Postgres时。我使用以下compose文件:

services:
    app:
        build: .
        ports:
            - "8080:8080"
        depends_on:
            - "db"

    db:
        image: postgres
        restart: always
        environment:
            - POSTGRES_DB=demo_db
            - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
        ports:
            - "8081:5432"

以及我的应用程序的Dockerfile:

FROM golang:1.17

WORKDIR /

COPY go.mod .
COPY go.sum .
COPY *.go .

RUN go mod download
RUN go build

EXPOSE 8080

CMD ["./app"]

还有一个最小可重现的示例go文件:

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/jackc/pgx/v4/pgxpool"
	"github.com/pkg/errors"
)

func main() {
	log.Printf("connecting to postgres...")
	pgpool, err := connectToPostgres(context.Background(), "postgresql://localhost:5432/demo_db")
	log.Printf("pool: %s", pgpool)
	if err != nil {
		log.Fatalln(err)
	}
	log.Printf("successfully connected to postgres")

	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
	log.Println("stopped")
}

func connectToPostgres(ctx context.Context, url string) (*pgxpool.Pool, error) {
	var err error
	for i := 0; i < 5; i++ {
		p, err := pgxpool.Connect(ctx, url)
		if err != nil || p == nil {
			time.Sleep(3 * time.Second)
			continue
		}
        log.Printf("pool returned from connect: %s", p)
		return p, nil
	}
	return nil, errors.Wrap(err, "timed out waiting to connect postgres")
}
英文:

I have the following code for connecting to a Postgres database:

func connectToPostgres(ctx context.Context, url string) (*pgxpool.Pool, error) {
	var err error
	for i := 0; i &lt; 5; i++ {
		p, err := pgxpool.Connect(ctx, url)
		if err != nil || p == nil {
			time.Sleep(3 * time.Second)
			continue
		}
		log.Printf(&quot;pool returned from connect: %s&quot;, p)
		return p, nil
	}
	return nil, errors.Wrap(err, &quot;timed out waiting to connect postgres&quot;)
}

The use case is to wait for Postgres to become available when starting my server with docker-compose. Even though the code sleeps if p == nil, the log just before the first return prints out: pool returned from connect: %!s(*pgxpool.Pool=&lt;nil&gt;)

Is there some way that a background process in pgxpool could make p == nil?

Any thoughts on why this would happen?

EDIT: This appears to only happen while running my app and Postgres via docker-compose. I'm using the following compose file:

services:
    app:
        build: .
        ports:
            - &quot;8080:8080&quot;
        depends_on:
            - &quot;db&quot;

    db:
        image: postgres
        restart: always
        environment:
            - POSTGRES_DB=demo_db
            - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
        ports:
            - &quot;8081:5432&quot;

and the Dockerfile for my app:

FROM golang:1.17

WORKDIR /

COPY go.mod .
COPY go.sum .
COPY *.go .

RUN go mod download
RUN go build

EXPOSE 8080

CMD [ &quot;./app&quot; ]

And a minimally reproducible example go file:

package main

import (
	&quot;context&quot;
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;net/http&quot;
	&quot;time&quot;

	&quot;github.com/jackc/pgx/v4/pgxpool&quot;
	&quot;github.com/pkg/errors&quot;
)

func main() {
	log.Printf(&quot;connecting to postgres...&quot;)
	pgpool, err := connectToPostgres(context.Background(), &quot;postgresql://localhost:5432/demo_db&quot;)
	log.Printf(&quot;pool: %s&quot;, pgpool)
	if err != nil {
		log.Fatalln(err)
	}
	log.Printf(&quot;successfully connected to postgres&quot;)

	if err := http.ListenAndServe(&quot;:8080&quot;, nil); err != nil {
		log.Fatal(err)
	}
	log.Println(&quot;stopped&quot;)
}

func connectToPostgres(ctx context.Context, url string) (*pgxpool.Pool, error) {
	var err error
	for i := 0; i &lt; 5; i++ {
		p, err := pgxpool.Connect(ctx, url)
		if err != nil || p == nil {
			time.Sleep(3 * time.Second)
			continue
		}
        log.Printf(&quot;pool returned from connect: %s&quot;, p)
		return p, nil
	}
	return nil, errors.Wrap(err, &quot;timed out waiting to connect postgres&quot;)
}

答案1

得分: 4

问题在于在docker-compose网络中连接时,你必须连接到容器的主机名,即db

你也可以使用其他容器的IP,但需要额外的工作量,使用主机名更简单。

换句话说,你的连接字符串是错误的,当我连接到localhost时也遇到了这个问题。

正确的连接字符串如下:

 "postgres://postgres:mysecretpassword@db:5432/postgres"

它可以正常工作。

其余的日志如下:

db_1   | 2021-12-21 18:56:04.122 UTC [1] LOG:  database system is ready to accept connections
app_1  | 2021/12/21 18:56:06 pool returned from connect: &{%!s(*puddle.Pool=&{0xc00007c040 0xc0000280b0 [0xc00007c0c0] [0xc00007c0c0] 0x65cb60 0x65dc80 16 1 9872796 1 0 false}) %!s(*pgxpool.Config=&{0xc0000a2000 <nil> <nil> <nil> <nil> 3600000000000 1800000000000 16 0 60000000000 false true}) %!s(func(context.Context, *pgx.ConnConfig) error=<nil>) %!s(func(context.Context, *pgx.Conn) error=<nil>) %!s(func(context.Context, *pgx.Conn) bool=<nil>) %!s(func(*pgx.Conn) bool=<nil>) %!s(int32=0) %!s(time.Duration=3600000000000) %!s(time.Duration=1800000000000) %!s(time.Duration=60000000000) {%!s(uint32=0) {%!s(int32=0) %!s(uint32=0)}} %!s(chan struct {}=0xc000024060)}
app_1  | 2021/12/21 18:56:06 pool: &{%!s(*puddle.Pool=&{0xc00007c040 0xc0000280b0 [0xc00007c0c0] [0xc00007c0c0] 0x65cb60 0x65dc80 16 1 9872796 1 0 false}) %!s(*pgxpool.Config=&{0xc0000a2000 <nil> <nil> <nil> <nil> 3600000000000 1800000000000 16 0 60000000000 false true}) %!s(func(context.Context, *pgx.ConnConfig) error=<nil>) %!s(func(context.Context, *pgx.Conn) error=<nil>) %!s(func(context.Context, *pgx.Conn) bool=<nil>) %!s(func(*pgx.Conn) bool=<nil>) %!s(int32=0) %!s(time.Duration=3600000000000) %!s(time.Duration=1800000000000) %!s(time.Duration=60000000000) {%!s(uint32=0) {%!s(int32=0) %!s(uint32=0)}} %!s(chan struct {}=0xc000024060)}
app_1  | 2021/12/21 18:56:06 successfully connected to postgres
英文:

The issue is that when connecting in a docker-compose network, you have to connect to the hostname of the container, in this case db.

You could also use the other container's IP but would take additional amount of work, it's simpler to just use the hostname.

In other words, you have the wrong connection string, I got this as well when connecting to localhost

app_1  | 2021/12/21 18:53:28 pool: %!s(*pgxpool.Pool=&lt;nil&gt;)
app_1  | 2021/12/21 18:53:28 successfully connected to postgres

When connecting with the right connection string:

 &quot;postgres://postgres:mysecretpassword@db:5432/postgres&quot;

It works perfectly.

Rest of the logs

db_1   | 2021-12-21 18:56:04.122 UTC [1] LOG:  database system is ready to accept connections
app_1  | 2021/12/21 18:56:06 pool returned from connect: &amp;{%!s(*puddle.Pool=&amp;{0xc00007c040 0xc0000280b0 [0xc00007c0c0] [0xc00007c0c0] 0x65cb60 0x65dc80 16 1 9872796 1 0 false}) %!s(*pgxpool.Config=&amp;{0xc0000a2000 &lt;nil&gt; &lt;nil&gt; &lt;nil&gt; &lt;nil&gt; 3600000000000 1800000000000 16 0 60000000000 false true}) %!s(func(context.Context, *pgx.ConnConfig) error=&lt;nil&gt;) %!s(func(context.Context, *pgx.Conn) error=&lt;nil&gt;) %!s(func(context.Context, *pgx.Conn) bool=&lt;nil&gt;) %!s(func(*pgx.Conn) bool=&lt;nil&gt;) %!s(int32=0) %!s(time.Duration=3600000000000) %!s(time.Duration=1800000000000) %!s(time.Duration=60000000000) {%!s(uint32=0) {%!s(int32=0) %!s(uint32=0)}} %!s(chan struct {}=0xc000024060)}
app_1  | 2021/12/21 18:56:06 pool: &amp;{%!s(*puddle.Pool=&amp;{0xc00007c040 0xc0000280b0 [0xc00007c0c0] [0xc00007c0c0] 0x65cb60 0x65dc80 16 1 9872796 1 0 false}) %!s(*pgxpool.Config=&amp;{0xc0000a2000 &lt;nil&gt; &lt;nil&gt; &lt;nil&gt; &lt;nil&gt; 3600000000000 1800000000000 16 0 60000000000 false true}) %!s(func(context.Context, *pgx.ConnConfig) error=&lt;nil&gt;) %!s(func(context.Context, *pgx.Conn) error=&lt;nil&gt;) %!s(func(context.Context, *pgx.Conn) bool=&lt;nil&gt;) %!s(func(*pgx.Conn) bool=&lt;nil&gt;) %!s(int32=0) %!s(time.Duration=3600000000000) %!s(time.Duration=1800000000000) %!s(time.Duration=60000000000) {%!s(uint32=0) {%!s(int32=0) %!s(uint32=0)}} %!s(chan struct {}=0xc000024060)}
app_1  | 2021/12/21 18:56:06 successfully connected to postgres

huangapple
  • 本文由 发表于 2021年12月22日 01:15:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/70439214.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定