Соединения с базой данных все еще используются после запроса на sqlx в проекте golang

Я работаю над проектом golang, используя sqlx и postgres. Когда приложение запускается, я открываю соединение с базой данных и использую его следующим образом:

var connRO *sqlx.DB
var connRW *sqlx.DB
...

/ GetInstance - will return the connection opened to the database
func GetInstance(readonly bool) *sqlx.DB {

    if readonly {
        return connRO
    }

    return connRW
}

Проблема в том, что в некоторых блоках кода соединение все еще используется, вот один пример:

instanceRW := database.GetInstance(false)
instanceRO := database.GetInstance(true)
...

err := instanceRO.Get(&idFuncionario, `
            SELECT id 
            FROM t_funcionario 
            WHERE codigo_externo = $1 `,
            i.FuncionarioID)
        if err != nil {
            log.Println(err)
            return errors.New("Erro ao identificar funcionário.")
        }

        // Verifica se o item é granel
        // Caso não seja
        if *i.ItemGranelID == 0 {
            // Verifica se o item está disponível
            err = instanceRO.Get(&localidade_id, `
                SELECT COALESCE(localidade_id, 0) 
                FROM t_item 
                WHERE id = $1
            `, i.ItemID)
            if err != nil {
                log.Println(err)
                return errors.New("Não foi possível identificar tipo do item.")
...
            }

Когда я пытаюсь протестировать его, делая что-то подобное, тест не открывает много соединений. Вот некоторый тестовый код:

i := 600
    for i != 0 {
        if true {
            err := db.Select(&item, `SELECT * FROM t_item LIMIT 10`)
            if err != nil {

            }
            err = db.Select(&categoria, `SELECT * FROM t_categoria LIMIT 10`)
            if err != nil {

            }
            err = db.QueryRow(`INSERT INTO t_categoria
                (
                    nome, 
                    ativo
                )
                VALUES ($1, $2)`, fmt.Sprintf("cateToTeste%v", i), true).Scan(&itemget)
            if err != nil {

            }

            err = db.Get(&itemget, `SELECT COALESCE(localidade_id, 0) 
                    FROM t_item 
                    WHERE id = $1`, 150)
            if err != nil {

            }

            log.Println("ok")
            i--
        }       
    }
    log.Println("Tudo ok!!")

Некоторое время я получаю максимальные соединения, и приложение умирает.

Это единственный код, который использует соединение с БД? Если ваше приложение использует только Get, то кто знает, в чем проблема, но если ваше приложение использует другие функции типа *sqlx.DB, то проблема может заключаться в этом, например, в том, что значения sql.Rows, sql.Stmt или sql.Tx не закрываются.

mkopriva 24.04.2019 14:34
sql.DB — это пул множества подключений, который управляется внутренне. Возможно, вы генерируете достаточно трафика, чтобы исчерпать пул? Дополнительная информация: alexedwards.net/blog/configuring-sqldb
Cosmic Ossifrage 24.04.2019 16:44

@mkopriva Я закрываю соединение только тогда, когда приложение останавливается (открывать и закрывать соединения все время, конечно, плохо для приложения). Но проблема в том, что то, как я это делаю, не должно открывать много соединений, но некоторые из них (например, те, которые используются в блоке кода) все еще используются после запроса, и приложению нужно открывать новые, чтобы все еще работать . Я использую tx, QueryRow, Get и Select, но не все запросы приложений имеют эту проблему, только некоторые фрагменты кода, подобные этому.

Marcelo Gonçalves 25.04.2019 19:37

@CosmicOssifrage, для работы приложению просто нужно открыть 2 соединения для каждого пользователя. Но если эти все еще используются, он откроет другие и достигнет предела (максимальное количество подключений), и приложение умрет.

Marcelo Gonçalves 25.04.2019 19:40

@MarceloGonçalves DB это не связь, это пул соединений. Каждый раз, когда вы вызываете такой метод, как QueryRow, Query, Get, Select, соединение извлекается из пула, и когда метод выполняется, соединение возвращается в пул, в большинстве случаев это делается автоматически, однако в некоторых случаях вам нужно чтобы явно указать, когда вы закончите соединение, если вы этого не сделаете, оно никогда не будет возвращено в пул, и через некоторое время ваш пул станет пустым, и ваше приложение выйдет из строя.

mkopriva 25.04.2019 19:42

... например, если вы используете Query/QueryContext или Queryx/QueryxContext, возвращаемый объект *sql.Rows должен быть явно Closed после того, как вы закончите с ним. Если вы не закроете объект rows, ваше приложение выйдет из строя.

mkopriva 25.04.2019 19:48

... Объекты Stmt такие же, они должны быть закрыты, когда они больше не нужны. Tx необходимо либо зафиксировать, либо откатить, в противном случае произойдет сбой приложения.

mkopriva 25.04.2019 19:52
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
0
7
1 226
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Спасибо @mkopriva! Я до сих пор не знаю, почему соединения все еще используются, но для решения моей проблемы я использовал Tx и создал функцию для defercommit или rollback (фиксация и откат возвращают ошибку, у меня были проблемы с закрытием связи потому что - в Tx случаях):

func commitORRollback(db *sqlx.Tx, ROLLBACK *bool) {
    if *ROLLBACK {
        if err := db.Rollback(); err != nil {
            ...
        }
    } else {
        ...
    }
}

Другие вопросы по теме