Я работаю над проектом 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!!")
Некоторое время я получаю максимальные соединения, и приложение умирает.
sql.DB
— это пул множества подключений, который управляется внутренне. Возможно, вы генерируете достаточно трафика, чтобы исчерпать пул? Дополнительная информация: alexedwards.net/blog/configuring-sqldb@mkopriva Я закрываю соединение только тогда, когда приложение останавливается (открывать и закрывать соединения все время, конечно, плохо для приложения). Но проблема в том, что то, как я это делаю, не должно открывать много соединений, но некоторые из них (например, те, которые используются в блоке кода) все еще используются после запроса, и приложению нужно открывать новые, чтобы все еще работать . Я использую tx, QueryRow, Get и Select, но не все запросы приложений имеют эту проблему, только некоторые фрагменты кода, подобные этому.
@CosmicOssifrage, для работы приложению просто нужно открыть 2 соединения для каждого пользователя. Но если эти все еще используются, он откроет другие и достигнет предела (максимальное количество подключений), и приложение умрет.
@MarceloGonçalves DB
это не связь, это пул соединений. Каждый раз, когда вы вызываете такой метод, как QueryRow
, Query
, Get
, Select
, соединение извлекается из пула, и когда метод выполняется, соединение возвращается в пул, в большинстве случаев это делается автоматически, однако в некоторых случаях вам нужно чтобы явно указать, когда вы закончите соединение, если вы этого не сделаете, оно никогда не будет возвращено в пул, и через некоторое время ваш пул станет пустым, и ваше приложение выйдет из строя.
Спасибо @mkopriva!
Я до сих пор не знаю, почему соединения все еще используются, но для решения моей проблемы я использовал Tx
и создал функцию для defer
commit
или rollback
(фиксация и откат возвращают ошибку, у меня были проблемы с закрытием связи потому что - в Tx
случаях):
func commitORRollback(db *sqlx.Tx, ROLLBACK *bool) {
if *ROLLBACK {
if err := db.Rollback(); err != nil {
...
}
} else {
...
}
}
Это единственный код, который использует соединение с БД? Если ваше приложение использует только
Get
, то кто знает, в чем проблема, но если ваше приложение использует другие функции типа*sqlx.DB
, то проблема может заключаться в этом, например, в том, что значенияsql.Rows
,sql.Stmt
илиsql.Tx
не закрываются.