Как избежать утечки ресурсов при использовании пакетных (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 — лучший способ это гарантировать.

Ответ 18+ 🔞

А, слушай, вот эта штука с пакетными операциями в pgx — это вообще отдельная песня, блядь. Представь себе: ты создаёшь этот Batch, а он, сука, как жадный мудак, сразу хватает себе соединение из пула и память выделяет. И сидит, ждёт, пока ты его накормишь своими запросами.

А теперь главный пиздец: если ты его после использования не прикроешь, он так и будет висеть, как маньяк в шкафу, пожирая твои ресурсы. Соединения кончатся, память утечёт — и всё, приехали, ебать-копать. Сервер начнёт хрипеть, а ты будешь чесать репу, гадая, что за хуйня.

Вот поэтому, ёпта, святое правило: как только создал batch — сразу пиши defer batch.Close(). Это как поставить замок на холодильник от жадного соседа. Даже если твой код посередине накроется медным тазом с паникой, defer всё равно выстрелит и приберёт за тобой, как верный пес. Идиома Го, блядь, красота!

Но и это ещё не всё, охуеть теперь! В pgx (версии v4/v5, если что) есть подлянка. Метод SendBatch возвращает тебе не просто плевок, а целый объект BatchResults. И у этой твари, внимание, тоже есть свой метод Close()! И его закрыть — это не просто рекомендация, это, блядь, требование, иначе соединение обратно в пул не вернётся, будет болтаться, как хуй на проволоке.

Смотри, как это выглядит в коде, чтобы не быть полупидором:

// Допустим, '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()

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

_, err = results.Exec()
if err != nil {
    log.Printf("И вторая тоже обосралась: %v", err)
}

Запомни раз и навсегда, чувак: после SendBatch твой лучший друг — это defer results.Close(). Даже если результаты тебе нахуй не сдались и ты их не читаешь, закрыть надо. Иначе ресурсы не освободятся, и рано или поздно твоё приложение накроется таким медным тазом, что мало не покажется. Всё, вопрос закрыт, иди работай.