Ответ
Таймаут от БД — это ожидаемая ошибка в распределенных системах. Если ее не обработать, приложение вернет клиенту общую ошибку 500 Internal Server Error, что неинформативно. Правильная обработка включает несколько уровней защиты.
1. Использование context с таймаутом (Основное решение)
Для каждого входящего запроса создавайте context с дедлайном и передавайте его во все вызовы, взаимодействующие с внешними системами (БД, другие сервисы).
func handleRequest(w http.ResponseWriter, r *http.Request) {
// Устанавливаем таймаут на операцию с БД, например, 3 секунды
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
defer cancel()
var user User
// Передаем контекст в вызов к БД
err := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id = $1", 1).Scan(&user.Name)
if err != nil {
// Проверяем, была ли ошибка вызвана именно таймаутом контекста
if errors.Is(err, context.DeadlineExceeded) {
// Возвращаем клиенту корректный код ошибки
http.Error(w, "Database operation timed out", http.StatusGatewayTimeout) // 504
return
}
// Обрабатываем другие возможные ошибки БД
http.Error(w, "Internal Server Error", http.StatusInternalServerError) // 500
return
}
// ... успешный ответ
}
2. Дополнительные защитные меры
- Правильный HTTP-статус: Вместо
500 Internal Server Errorлучше использовать504 Gateway Timeoutили503 Service Unavailable. Это точнее описывает проблему для клиента и систем мониторинга. - Настройка пула соединений: Ограничьте максимальное количество открытых соединений (
SetMaxOpenConns) и время жизни соединения (SetConnMaxLifetime), чтобы предотвратить исчерпание ресурсов и использование "зависших" соединений. - Логика повторных запросов (Retry): Для временных ошибок можно реализовать механизм повторных запросов, желательно с экспоненциальной задержкой (
exponential backoff), чтобы не "завалить" запросами и без того нагруженную БД. - Паттерн Circuit Breaker (Предохранитель): Если БД стабильно не отвечает, Circuit Breaker временно прекращает отправку новых запросов, давая ей время на восстановление и предотвращая каскадные сбои в вашем сервисе.
Ответ 18+ 🔞
А, слушай, вот эта хуйня с таймаутами от базы данных — это ж классика, блядь! Приложение тупо падает в 500-ю ошибку, а клиент сидит, чешет репу: «Чё за пиздец?» Надо ж объяснить человеку, что база, сука, уснула, а не что сервер сгорел, ёпта!
1. Главный приём — context с дедлайном (Без этого нихуя)
Каждый запрос — это как поход в магазин за водкой. Если ты застрял в лифте на три часа — всем похуй, водку не принёс. Так и тут: ставим дедлайн, блядь.
func handleRequest(w http.ResponseWriter, r *http.Request) {
// Даём БД, скажем, 3 секунды, нехуй там чаи гонять
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
defer cancel() // Обязательно отменяем, а то ресурсы потекут
var user User
// Суём контекст прямо в запрос, как записку в дверь
err := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id = $1", 1).Scan(&user.Name)
if err != nil {
// А вот тут магия: ловим именно таймаут, а не любую хуйню
if errors.Is(err, context.DeadlineExceeded) {
// Говорим клиенту честно: «База тупит, жди»
http.Error(w, "Database operation timed out", http.StatusGatewayTimeout) // 504
return
}
// Если ошибка другая — ну, бывает, пиздец
http.Error(w, "Internal Server Error", http.StatusInternalServerError) // 500
return
}
// ... если дошло сюда — красота, блядь
}
2. Дополнительные подпорки, чтоб совсем не облажаться
- Правильный статус в ответ: Не вали всё в
500, как в помойку.504 Gateway Timeout— это как сказать: «Я, блядь, ждал-ждал, да нихуя не дождался». Клиенту понятнее, мониторингу — приятнее. - Настрой пул соединений: А то откроют овердохуища соединений, БД захлебнётся, как алкаш в луже.
SetMaxOpenConnsиSetConnMaxLifetime— твои лучшие друзья, чтоб не плодить зомби-коннекты. - Повторные попытки (Retry): Иногда база просто чихнула — можно попробовать ещё разок, но не долбить её, как дятел. Сделай экспоненциальную задержку, чтоб не добить окончательно.
- Предохранитель (Circuit Breaker): Если база совсем легла — прекращай слать запросы, дай ей отдышаться. А то все потоки в приложении повиснут в ожидании, и будет полный пиздец, в рот меня чих-пых!
Короче, суть в чём: не давай БД себя выебать. Контролируй время, ограничивай аппетиты, а если что — честно сообщай, кто именно обосрался.