Как избежать утечки ресурсов при использовании пакетных (batch) операций с базой данных в Go?

Ответ

При работе с пакетными операциями (например, через pgx.Batch) критически важно всегда закрывать объект Batch, чтобы освободить выделенные под него ресурсы как в приложении, так и на стороне сервера базы данных.

Проблема: Каждый созданный Batch резервирует соединение из пула и выделяет память. Если не вызвать метод Close(), эти ресурсы не будут освобождены, что приведёт к утечке соединений и памяти.

Решение: Используйте defer для гарантированного вызова batch.Close() сразу после создания объекта Batch. Это идиоматический и самый надёжный способ в Go, который сработает даже в случае паники или ошибок.

Пример (с использованием pgx):

// Предполагается, что 'conn' — это активное соединение с БД
batch := &pgx.Batch{}

// Добавляем операции в пакет
batch.Queue("INSERT INTO users (name) VALUES ($1)", "Alice")
batch.Queue("UPDATE products SET stock = stock - 1 WHERE id = $1", 101)

// Отправляем пакет на выполнение
results := conn.SendBatch(context.Background(), batch)

// ОБЯЗАТЕЛЬНО закрываем ридер результатов, чтобы освободить соединение
defer results.Close()

// Обрабатываем результаты (даже если они не нужны, 
// нужно вызвать Close для освобождения ресурсов)
_, err := results.Exec()
if err != nil {
    log.Printf("Ошибка выполнения первой операции в пакете: %v", err)
}

_, err = results.Exec()
if err != nil {
    log.Printf("Ошибка выполнения второй операции в пакете: %v", err)
}

Ключевой момент: В pgx версии v4/v5, SendBatch возвращает BatchResults, у которого тоже есть метод Close(). Его обязательно нужно вызывать, чтобы вернуть соединение обратно в пул. Использование defer — лучший способ это гарантировать.