Ответ
Для защиты приложения от медленных ответов внешних систем, включая базу данных, используется несколько ключевых подходов:
1. Контекст с таймаутом (context.WithTimeout)
Это основной механизм в Go для контроля времени выполнения операций. context.Context передается во все функции, выполняющие I/O операции (например, запросы к БД, HTTP-запросы).
-
Принцип работы: Мы создаем контекст с ограниченным временем жизни. Если операция не завершается за указанное время, контекст отменяется, и функция возвращает ошибку
context.DeadlineExceeded. Это позволяет приложению не ждать бесконечно и освободить ресурсы. -
Пример:
// Устанавливаем таймаут в 3 секунды для запроса к БД ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() // Важно вызвать cancel, чтобы освободить ресурсы
var user User err := db.QueryRowContext(ctx, "SELECT id, name FROM users WHERE id = ?", userID).Scan(&user.ID, &user.Name)
if err != nil { // Проверяем, была ли ошибка вызвана именно таймаутом if errors.Is(err, context.DeadlineExceeded) { log.Println("Query to database timed out") // Здесь можно вернуть клиенту ошибку 504 Gateway Timeout return } log.Printf("Database query failed: %v", err) return }
**2. Настройка пула соединений (Connection Pool Tuning)**
Пакет `database/sql` управляет пулом соединений. Правильная настройка помогает избежать исчерпания ресурсов как на стороне приложения, так и на стороне БД.
* `db.SetMaxOpenConns(n)`: Ограничивает общее количество открытых соединений с БД.
* `db.SetMaxIdleConns(n)`: Ограничивает количество соединений, которые могут простаивать в пуле.
* `db.SetConnMaxLifetime(d)`: Устанавливает максимальное время жизни соединения, чтобы избежать проблем с устаревшими или закрытыми со стороны сети соединениями.
**3. Асинхронная обработка**
Если операция заведомо долгая (например, генерация сложного отчета), ее не следует выполнять в рамках синхронного HTTP-запроса. Такую задачу лучше поместить в очередь (например, **RabbitMQ** или **NATS**) и обработать в фоновом воркере. Пользователю же можно сразу вернуть ответ, что задача принята в обработку.