Как в Go отслеживать и обрабатывать ошибки соединения с базой данных?

Ответ

В Go для отслеживания и обработки ошибок соединения с БД используется комбинация нескольких подходов, так как пакет database/sql абстрагирует пул соединений.

  1. Проверка при инициализации (sql.Open) sql.Open не устанавливает соединение с БД, а лишь подготавливает структуру *sql.DB. Ошибка на этом этапе означает неверные параметры (например, неправильный DSN или несуществующий драйвер).

    db, err := sql.Open("postgres", "user=... password=... ...")
    if err != nil {
        // Ошибка конфигурации, а не сети
        log.Fatalf("Invalid DB config: %v", err)
    }
  2. Проверка реального соединения (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)
    }
  3. Обработка ошибок при выполнении запросов Любой вызов (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
    }
  4. Реализация логики повторных попыток (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))
    }
  5. Мониторинг состояния пула соединений *sql.DB предоставляет статистику пула, которая помогает отслеживать его здоровье.

    stats := db.Stats()
    // stats.OpenConnections - количество открытых соединений
    // stats.WaitCount - сколько раз запросы ждали свободного соединения
    // stats.WaitDuration - общее время ожидания

    Эти метрики полезно экспортировать в систему мониторинга (Prometheus, Grafana) для долгосрочного анализа.