Ответ
В Go для отслеживания и обработки ошибок соединения с БД используется комбинация нескольких подходов, так как пакет database/sql абстрагирует пул соединений.
-
Проверка при инициализации (
sql.Open)sql.Openне устанавливает соединение с БД, а лишь подготавливает структуру*sql.DB. Ошибка на этом этапе означает неверные параметры (например, неправильный DSN или несуществующий драйвер).db, err := sql.Open("postgres", "user=... password=... ...") if err != nil { // Ошибка конфигурации, а не сети log.Fatalf("Invalid DB config: %v", err) } -
Проверка реального соединения (
db.PingContext) Это основной способ проверить, что соединение с базой данных может быть установлено. Его вызывают сразу послеsql.Open.ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := db.PingContext(ctx); err != nil { log.Fatalf("DB connection failed: %v", err) } -
Обработка ошибок при выполнении запросов Любой вызов (
QueryContext,ExecContext,BeginTx) может вернуть ошибку, связанную с потерей соединения. Пакетdatabase/sqlпытается автоматически переподключиться при некоторых временных ошибках, но если это не удается, ошибка будет возвращена приложению.rows, err := db.QueryContext(ctx, "SELECT id FROM users") if err != nil { // Здесь может быть ошибка сети, таймаут или другая проблема с соединением log.Printf("Query failed: %v", err) // Здесь можно реализовать логику повторных попыток (retry) return } -
Реализация логики повторных попыток (Retry) Для временных сбоев сети полезно реализовать механизм
retryс экспоненциальной выдержкой (exponential backoff), чтобы не перегружать БД запросами на переподключение.// Псевдокод for i := 0; i < maxRetries; i++ { err = db.Ping() if err == nil { break // Успешно } // Ожидаем (1s, 2s, 4s, ...) time.Sleep(time.Second * (1 << i)) } -
Мониторинг состояния пула соединений
*sql.DBпредоставляет статистику пула, которая помогает отслеживать его здоровье.stats := db.Stats() // stats.OpenConnections - количество открытых соединений // stats.WaitCount - сколько раз запросы ждали свободного соединения // stats.WaitDuration - общее время ожиданияЭти метрики полезно экспортировать в систему мониторинга (Prometheus, Grafana) для долгосрочного анализа.
Ответ 18+ 🔞
А, слушай, вот этот ваш Go с его базой данных — это же просто отдельный вид искусства, блядь! Ну, типа, как будто ты пытаешься наладить связь с космосом, а там тебе отвечают на языке древних мартышек, ёпта.
Смотри, вот ты пишешь sql.Open — и думаешь, что всё, хуй с горы, соединение установлено! Ан нет, сука! Эта функция — она просто как будто говорит: «О, привет, я тут теоретически знаю, как подключиться, но реально я нихуя делать не буду, пока ты не попросишь». Это как взять телефон и не набрать номер, а просто держать его в руке, надеясь, что само позвонит. Ошибка тут будет только если ты вообще не ту строку подключения вписал или драйвер забыл, ну, полный распиздяй.
db, err := sql.Open("postgres", "user=... password=... ...")
if err != nil {
// Вот тут ошибка — это ты просто конфиг накосячил, мудак
log.Fatalf("Invalid DB config: %v", err)
}
А чтобы реально проверить, жива ли база, надо ей пингануть! db.PingContext — это как крикнуть в тёмную пещеру: «Эй, там кто есть?!» И если через пять секунд (таймаут, блядь, не забудь!) тебе не ответили эхом — всё, пиздец, связи нет.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
log.Fatalf("DB connection failed: %v", err) // Вот тут уже реальная проблема, сеть легла или база сдохла
}
Дальше — самое весёлое. Ты делаешь запрос, а он тебе возвращает ошибку. И кто виноват? Может, сеть отвалилась, может, база ушла в запой, а может, таймаут просто выстрелил. Пакет database/sql — он, конечно, умный, пытается сам переподключиться, но не всегда, блядь, получается. Так что готовься ловить эти ошибки на каждом углу.
rows, err := db.QueryContext(ctx, "SELECT id FROM users")
if err != nil {
// А вот тут уже может быть всё что угодно: от 'connection reset by peer' до 'i'm a teapot', ёпта
log.Printf("Query failed: %v", err)
// И вот тут самое время включить логику повторных попыток, если ошибка временная
return
}
И про повторные попытки — это вообще отдельная песня, блядь! Нельзя просто так тупо слать запросы раз в секунду. Надо делать exponential backoff — это когда ты ждёшь сначала секунду, потом две, потом четыре, и так далее. Как будто ты стучишься в дверь к соседу, который, возможно, просто в наушниках: сначала тихо, потом громче, потом уже ногой, блядь!
// Псевдокод, но суть ясна
for i := 0; i < maxRetries; i++ {
err = db.Ping()
if err == nil {
break // Ура, прорвались!
}
// Ждём: 1с, 2с, 4с... чтоб не спамить как долбоёб
time.Sleep(time.Second * (1 << i))
}
И напоследок, чтобы вообще спать спокойно, смотри статистику пула соединений. Это как датчики в машине: если OpenConnections зашкаливает, а WaitDuration растёт как сумасшедший — значит, твоя база задыхается, и скоро будет большой-большой бум, нахуй!
stats := db.Stats()
// stats.OpenConnections — сколько соединений висит
// stats.WaitCount — сколько запросов в очереди тупо ждали
// stats.WaitDuration — сколько в сумме все прождали, можно в Prometheus засунуть и паниковать на графиках
Короче, работа с БД в Go — это не «написал и забыл». Это постоянная слежка, как за капризным ребёнком, который то сеть роняет, то таймауты выставляет, то вообще молчит как Герасим из того рассказа, блядь. Муму-муму, сука, только и слышно!