go-pg/pg

Why is it complaining 'FATAL #53300 sorry, too many clients already' when I am using only one client?

sayak-dev opened this issue · 6 comments

I have a global instance to Postgres database which is being used throughout the code.

There are 500 goroutines inserting some data into the database using the same instance.

Expected Behavior

My understanding is that, there is just one instance of the database which is being created by ConnectToDB() method when it is called for the first time & on the subsequent calls, it merely returns this instance instead of creating a new one.

Finally when main ends, that one single instance is closed.

Current Behavior

It fails with panic: FATAL #53300 sorry, too many clients already

Steps to Reproduce

go run main.go

package main

import (
    "github.com/go-pg/pg/v10"
    "github.com/go-pg/pg/v10/orm"
    "sync"
)

var db *pg.DB
var once sync.Once


type DummyTable struct {
    IdOne int
    IdTwo int
}

func main() {
    var wg sync.WaitGroup

    db := ConnectToDB()
    defer db.Close()

    for i := 0; i < 500; i++ {
        wg.Add(1)
        go insertIntoDB(&wg)
    }
    wg.Wait()
}

func insertIntoDB(wg *sync.WaitGroup) {
    defer wg.Done()
    localDb := ConnectToDB()

    _, err := localDb.Model(&DummyTable{
        IdOne: 2,
        IdTwo: 3,
    }).Insert()
    if err != nil {
        panic(err)
    }
}

func createSchema(db *pg.DB) error {
    models := []interface{}{
        (*DummyTable)(nil),
    }

    for _, model := range models {
        err := db.Model(model).CreateTable(&orm.CreateTableOptions{
            Temp: false,
        })
        if err != nil {
            return err
        }
    }
    return nil
}

func ConnectToDB() *pg.DB {
    once.Do(func() {
        db = pg.Connect(&pg.Options{
            User:     "username",
            Database: "dbName",
        })
        err := createSchema(db)
        if err != nil {
            panic(err)
        }
    })
    return db
}

I am unable to understand what could possibly cause the creation of more than one client

DB is a database handle representing a pool of zero or more underlying connections. It's safe for concurrent use by multiple goroutines. See https://pkg.go.dev/github.com/go-pg/pg/v10#DB

@vmihailenco well even in that case, why is it complaining that too many clients are using it? Is my understanding wrong? There is just one client, so how could it have the problem of too many clients using it?

The DB object will create multiple "clients" from PostgreSQL's perspective. A client is essentially a single network connection from your program to the PostgreSQL database.

You are getting this error because go-pg's pool is spawning many connections to handle all the queries you are sending. You need to reduce the number of go routines you are using. Or you need to increase the maximum number of connections your PostgreSQL server allows. Information about that can be found here: https://www.postgresql.org/docs/13/runtime-config-connection.html

@vmihailenco what are your thoughts of doing something like SHOW max_connections; and limit the size of the pool in go-pg to whatever that returns?

The DB object will create multiple "clients" from PostgreSQL's perspective. A client is essentially a single network connection from your program to the PostgreSQL database.

You are getting this error because go-pg's pool is spawning many connections to handle all the queries you are sending. You need to reduce the number of go routines you are using. Or you need to increase the maximum number of connections your PostgreSQL server allows. Information about that can be found here: https://www.postgresql.org/docs/13/runtime-config-connection.html

Okay thanks, that makes sense

the problem of too many clients using it

Postgres calls incoming connections (sockets) clients. The error probably should be "too many connections already".

You need to reduce the number of go routines you are using.

Even better would be to use PgBouncer.

@vmihailenco what are your thoughts of doing something like SHOW max_connections; and limit the size of the pool in go-pg to whatever that returns?

With PgBouncer it is not a problem / limiting factor. Besides we need to create a conn pool before we can issue a query...