Как избежать HTTP 500 ошибки при таймауте запроса к базе или другому сервису
Что проверяют
Понимаете ли вы, что таймаут внешнего вызова это ожидаемая ситуация, а не баг, и умеете ли отличить её от настоящей ошибки сервера. Следом обычно спрашивают, откуда берётся сам дедлайн и что происходит, если клиент уже ушёл.
Как отвечать
Каркас из четырёх частей:
- дедлайн от запроса:
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)и сразуdefer cancel(). Контекст наследуется отr.Context(), поэтому запрос к базе отменится и при разрыве соединения клиентом; - контекст до самого низа:
db.QueryContext(ctx, ...), методыpgxсctxпервым аргументом,http.NewRequestWithContextдля исходящих вызовов. Функция безctxтаймаут просто не увидит; - разбор ошибки:
errors.Is(err, context.DeadlineExceeded)превращается в 504 Gateway Timeout, перегрузка или открытый circuit breaker дают 503 с заголовкомRetry-After, аcontext.Canceledозначает, что клиент ушёл и отвечать уже некому. Всё остальное честно остаётся 500; - деградация: данные из кэша, частичный ответ без второстепенного блока, retry с экспоненциальной задержкой только для идемпотентных операций.
Добавьте таймауты сервера и клиента. У http.Server есть ReadHeaderTimeout, ReadTimeout, WriteTimeout и IdleTimeout, а http.TimeoutHandler сам ответит 503, если обработчик не уложился. У http.Client по умолчанию таймаута нет вовсе, поэтому голый http.Get к зависшему сервису может ждать бесконечно.
И последнее: бюджет времени. Если вызывающий ждёт вас 3 секунды, ваши вызовы вниз должны укладываться с запасом, иначе вы досчитаете ответ, который уже никто не ждёт.
Где спотыкаются
Создают таймаут от context.Background(). Отмена клиента до базы не доходит, и тяжёлый запрос продолжает работать впустую.
Сравнивают err == context.DeadlineExceeded. Драйверы оборачивают ошибки в свои типы, равенство не срабатывает, нужен errors.Is или проверка Timeout() у net.Error через errors.As.
Маскируют всё под 503. Если упал ваш собственный код, это 500, и мониторинг должен это видеть, а не списывать на «внешний сервис тормозит».