Бонусы за приглашения

Как работают бонусы за приглашения

Поделитесь ссылкой. Когда друг зарегистрируется по ней и пополнит баланс, вы получите указанный бонус за его последующие пополнения.

Upstream Request Timeout: что значит и как безопасно повторить запрос

Разбираем значение Upstream Request Timeout, определяем слой обрыва и настраиваем безопасный повтор без двойной операции.

Содержание

Upstream Request Timeout: что значит и как безопасно повторить запрос

Upstream Request Timeout переводится как «шлюз не дождался ответа вышестоящего сервиса за отведённое время». Это не доказывает, что модель недоступна: лимит мог истечь в клиенте, reverse proxy, API gateway или на участке между gateway и upstream-моделью.

Правильный порядок — определить слой обрыва по исключению, HTTP status, времени и идентификатору запроса, а затем менять только соответствующий timeout. Увеличение всех лимитов сразу скрывает причину и может привести к повтору уже принятой операции.

Если запрос шёл через BetterToken, до retry откройте Dashboard и сопоставьте время, модель, status и расход Token. Это сразу отделяет запрос, дошедший до API, от сбоя до gateway и снижает риск повторить неизвестный результат.


Где происходит обрыв: фазы сетевого таймаута

Сетевой запрос к API больших языковых моделей проходит через несколько независимо ограниченных этапов:

[Пул клиента] --- Pool wait ---> [Свободное соединение]
[Клиент] --- Connect / Write ---> [API Gateway]
[Gateway] --- Upstream wait ---> [Модель]
[Клиент] <--- Read / Stream --- [Чанки ответа]
[Приложение] --- Общий deadline ---> [Вся операция]
  1. Connect timeout ограничивает установление DNS/TCP/TLS-соединения.
  2. Write timeout действует, пока клиент отправляет очередную часть тела запроса.
  3. Read Timeout (таймаут чтения): Время ожидания ответа сервера или очередного чанка в SSE-потоке.
  4. Pool Timeout (таймаут пула): Ожидание свободного сокета в пуле соединений при высокой конкурентности.
  5. Общий deadline приложения ограничивает всю операцию, а upstream timeout принадлежит gateway или провайдеру. Изменение одного таймера не продлевает остальные.

Диагностическая матрица таймаутов

Симптом / ИсключениеФаза сбояВероятная причинаИнженерное решение
httpx.ConnectTimeout, HTTP-ответа нетDNS, TCP или TLSМаршрут, сертификаты, proxy или исходящий firewallПовторить из того же окружения и сравнить DNS, CA и proxy до изменения connect timeout
httpx.ReadTimeout до или между чанкамиClient read/idleЗа время таймера не пришёл следующий чанкСравнить время до первого байта и между чанками, проверить idle/read timeout промежуточных proxy
Есть HTTP status и тело ошибкиGateway или upstreamЗапрос дошёл до HTTP-сервераСохранить status, body и request ID; разбирать контракт ошибки, а не сеть подключения
httpx.PoolTimeoutПул клиентаНе освободилось соединениеИзмерить конкурентность и загрузку пула; менять лимиты только после подтверждения насыщения
Отмена всегда через одинаковое общее времяDeadline приложенияОперацию остановил caller, job runner или reverse proxyНайти владельца deadline и сопоставить его со всеми нижележащими таймерами

Приоритетный порядок диагностики

  1. Соберите сигнал до повтора. Запишите начало и конец, класс исключения, HTTP status и body, request ID, факт получения хотя бы одного чанка, модель, endpoint и окружение запуска.
  2. Проверьте клиент и сеть. Повторите тест из того же контейнера или сервера. Если другое окружение работает, сравните DNS, CA-сертификаты, proxy-переменные и исходящий firewall.
  3. Проверьте промежуточные узлы. Сопоставьте read timeout клиента с idle/read timeout корпоративного и reverse proxy. Увеличение SDK timeout не продлит более короткий лимит proxy.
  4. Отделите gateway от upstream. Наличие HTTP-ответа означает, что сервер ответил. Используйте status, body и request ID, чтобы не смешивать upstream timeout, 429 и ошибку подключения.
  5. Меняйте одну переменную. Оставьте неизменными модель, запрос, сеть, endpoint и proxy — иначе сравнение не покажет причину.

Настройка гранулярных таймаутов в Python (httpx)

HTTPX документирует отдельные connect, read, write и pool timeout. Значения ниже — пример для контролируемого теста, а не универсальная production-рекомендация; реальные лимиты определяют по измеренным фазам запроса и общему deadline приложения.

import os
import httpx
from openai import OpenAI

API_KEY = os.environ.get("BETTERTOKEN_API_KEY", "your_api_key_here")

custom_timeout = httpx.Timeout(
    connect=5.0,    # Пример: установка сокета и TLS
    read=120.0,     # Пример: максимальная пауза между чанками
    write=10.0,     # Пример: ожидание отправки очередного чанка
    pool=10.0       # Пример: ожидание соединения из пула
)

http_client = httpx.Client(
    timeout=custom_timeout,
    limits=httpx.Limits(max_keepalive_connections=50, max_connections=100)
)

client = OpenAI(
    base_url="https://www.bettertoken.ai/v1",
    api_key=API_KEY,
    http_client=http_client
)

response = client.chat.completions.create(
    model="YOUR_CURRENT_MODEL_ID",
    messages=[
        {"role": "system", "content": "You are a senior systems architect."},
        {"role": "user", "content": "Design a high-throughput distributed message broker."}
    ],
    stream=True
)

for chunk in response:
    delta = chunk.choices[0].delta.content or ""
    print(delta, end="", flush=True)

Безопасный retry на всём жизненном цикле SSE

После отправки запроса считайте timeout неизвестным результатом, пока обратное не подтверждено:

  1. Не повторяйте автоматически частично полученный stream. Повтор может создать вторую генерацию и дополнительный расход; сохраните частичный результат и сначала сопоставьте первый запрос.
  2. Экспоненциальная задержка (Full Jitter): Повтор запроса при сбое подключения должен происходить с нарастающей случайной паузой.
  3. Идемпотентность только с серверной поддержкой: локальный ID задачи помогает сопоставить логи, но не предотвращает повторную генерацию. Автоматический повтор допустим только при документированной поддержке idempotency key и возможности сверить статус запроса; иначе неизвестный результат нужно сначала сверить с журналом использования.
import time
import random
import httpx

def stream_with_safe_retry(send_request, outcome_known_not_accepted, max_attempts=3):
    """send_request() возвращает context-managed iterable response."""
    base_delay = 1.0
    for attempt in range(max_attempts):
        received_any = False
        try:
            with send_request() as response:
                for chunk in response:
                    received_any = True
                    yield chunk
            return
        except (httpx.ConnectTimeout, httpx.ReadTimeout,
                httpx.RemoteProtocolError) as err:
            # Сюда попадают и ошибки во время итерации SSE.
            if (received_any or attempt == max_attempts - 1
                    or not outcome_known_not_accepted(err)):
                raise
            sleep_time = random.uniform(0, base_delay * (2 ** attempt))
            time.sleep(sleep_time)

outcome_known_not_accepted должен опираться на документированное поведение сервера или внешнюю проверку статуса. Клиентский timeout сам по себе не доказывает, что запрос не принят. Ограничьте и число попыток, и общий deadline.

Два теста, которые подтверждают исправление

  1. Короткий контрольный запрос. Запросите небольшой ответ и запишите status, время до первого байта, общую длительность, request ID и завершение stream. Если тест не проходит, сначала проверяйте сеть, аутентификацию и endpoint.
  2. Контролируемый длинный запрос. После успеха короткого теста увеличьте только ожидаемый объём ответа или верните исходную нагрузку. Не меняйте модель, endpoint, сеть и proxy. Если ломается только этот тест, сравните read/idle timeout, общий deadline и лимиты промежуточных узлов.

Исправление подтверждено, когда короткий тест и один повтор исходного сценария завершаются с ожидаемым status и результатом без необъяснимого дубля. Если запрос дошёл до BetterToken, сопоставьте в Dashboard время, модель, status и расход Token, затем откройте API Reference и проверьте контракт endpoint перед изменением retry.

Если сервер вернул 429, используйте отдельный разбор лимитов и Retry-After. Если соединение устанавливается, но обрывается именно поток событий, продолжите с диагностикой SSE-стриминга.

Источники

Готовы оптимизировать LLM workflow?

Подключите единый API, управляйте ключами и контролируйте расходы на AI-модели в BetterToken.

Начать бесплатно