LLM API и интеграция

Rate limit в API нейросетей: что делать при отказе

Rate limit в API нейросетей: что делать при отказе

Отказ по частоте запросов — самая недооценённая ошибка в работе с API моделей. Её обычно чинят самым вредным способом: повтором сразу же, без паузы. Это ровно то действие, которое усугубляет ситуацию, потому что сервис уже перегружен, а вы добавляете нагрузки. Между тем лимиты — не поломка и не признак плохого провайдера: это нормальный механизм, без которого один клиент со всплеском трафика ухудшал бы работу всем остальным. Разбираем, откуда берутся ограничения, чем повтор отличается от очереди, и почему правильная архитектура делает лимиты почти незаметными.

Откуда берутся ограничения

Причина физическая, и понимание её помогает выбрать реакцию.

Мощности под инференс конечны и дороги. Видеокарта обрабатывает ограниченное число запросов одновременно, и это число не растягивается по желанию. Когда обращений больше, чем ёмкость, варианта ровно два: ставить всех в очередь, ухудшая время ответа для всех, либо отказывать части, сохраняя качество обслуживания остальным.

Лимит — это выбор второго варианта с понятными правилами. Он делает поведение системы предсказуемым: вы знаете границу заранее, вместо того чтобы обнаруживать её по растущим задержкам.

Ограничивают обычно по нескольким осям одновременно: число запросов в единицу времени, число токенов в единицу времени, число одновременных обращений. Упереться можно в любую, и по коду ошибки не всегда видно, в какую именно.

Почему повтор сразу же — худшее решение

Разберём, что происходит при наивной обработке.

Запрос отклонён. Код немедленно отправляет его снова. Сервис по-прежнему перегружен, отклоняет снова. Код повторяет.

Получается цикл, в котором вы генерируете нагрузку с максимальной скоростью, на которую способны, — и делаете это ровно в момент, когда система и так не справляется. Хуже того, так же поступают все остальные клиенты, и перегрузка продлевается коллективными усилиями.

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

Второе обязательное условие — предел числа попыток. Три обычно покрывают подавляющее большинство временных сбоев. Десять не покрывают ничего сверх этого, зато превращают короткий всплеск в долгую деградацию вашего же сервиса: пользователь ждёт минуту вместо честного сообщения об ошибке.

Третье, что стоит добавить, — небольшой случайный разброс в паузе. Без него все ваши процессы, отказавшие одновременно, одновременно же и повторят, воспроизведя всплеск.

Очередь вместо повторов

Здесь начинается архитектурная часть, и она решает проблему принципиально, а не смягчает.

Повтор — это попытка протолкнуть запрос сейчас. Очередь — признание, что сейчас не получится, и перенос на потом.

Что даёт очередь:

Всплеск сглаживается. Пришло тысяча запросов за минуту, обрабатываете вы двести — остальные восемьсот не теряются и не долбятся в стену, а ждут.

Скорость становится вашим параметром. Вы сами задаёте, с какой частотой доставать из очереди, и держитесь ниже границы вместо того, чтобы упираться в неё.

Отказ перестаёт быть событием. Если отказ всё-таки произошёл, запрос возвращается в очередь, а не теряется.

Разделение простое: интерактивные запросы — напрямую, фоновые — через очередь. Пользователь, ждущий ответа на экране, не переживёт очереди. Ночная обработка накопленных документов не переживёт отказов.

Кстати, именно из-за этого разделения появляются тарифы, зависящие от времени: провайдеры пытаются экономически подтолкнуть фоновую нагрузку в непиковые часы. Мы разбирали такой случай в материале про тариф по часам суток.

Как понять, во что именно вы упёрлись

Диагностика, без которой лечение выбирается наугад.

Ограничения обычно устроены по нескольким осям сразу, и симптом у них один — отказ. Но причины разные, и меры тоже.

Упёрлись в число запросов. Много коротких обращений подряд. Лечится ограничителем скорости и объединением мелких запросов там, где это уместно.

Упёрлись в число токенов. Запросов немного, но каждый тащит огромный контекст. Лечится обрезкой истории и уменьшением того, что вы отправляете, — а заодно и удешевляет работу.

Упёрлись в одновременность. Запросов в секунду немного, но все они длинные и висят одновременно. Лечится ограничением параллелизма, а не паузами между запусками.

Отличить их друг от друга помогает простое наблюдение: посмотрите, что у вас в момент отказа было велико — количество, объём или число незавершённых обращений. Обычно ответ очевиден с первого взгляда на график, если такой график вообще есть.

Отсюда и минимальный набор того, что стоит измерять: запросов в минуту, токенов в минуту, одновременных обращений в пике. Три числа, которые показывают приближение к границе до того, как вы в неё упрётесь.

Почему всплески опаснее среднего

Наблюдение, которое объясняет, почему «у нас небольшая нагрузка» и «мы упираемся в лимит» прекрасно сосуществуют.

Средняя нагрузка почти ничего не говорит. Тысяча запросов за час — это меньше одного в секунду в среднем, и звучит безобидно. Но если они приходят пачкой за две минуты, мгновенная скорость в тридцать раз выше средней.

Такие всплески возникают сами собой: по расписанию запустилась обработка, пользователи пришли после рассылки, повторы после сбоя наложились друг на друга. Последний случай особенно неприятен, потому что сбой порождает всплеск, который порождает новые сбои.

Отсюда практическое: измерять надо пик, а не среднее. И закладывать ограничитель скорости даже там, где по средним цифрам он не нужен, — именно он и превращает пачку в ровный поток.

Как не упираться в лимит

Пять приёмов, отсортированных по отношению эффекта к трудозатратам.

  1. Считайте свою скорость. Большинство упирается в лимит не из-за нагрузки, а из-за отсутствия ограничителя на своей стороне: цикл по списку отправляет запросы так быстро, как может.
  2. Распараллеливайте с ограничением. Не «в один поток» и не «все сразу», а фиксированное число одновременных обращений. Это одна из самых частых недоработок в интеграциях.
  3. Уберите лишние запросы. Часть нагрузки — это повторные обращения за тем, что уже спрашивали. Кэш результатов на своей стороне снимает её без всякой борьбы с лимитами.
  4. Разведите нагрузку по ключам. Отдельный ключ под каждый сценарий даёт не только учёт расхода, но и изоляцию: фоновая задача не съест лимит интерактивного сценария.
  5. Не держите один запрос ради экономии. Соблазн упаковать двадцать задач в один огромный запрос понятен, но такой запрос дороже по контексту и дольше по времени.

Третий пункт стоит развернуть: доля повторяющихся запросов в реальных сервисах обычно выше, чем кажется. Прежде чем повышать лимиты, полезно посмотреть, сколько обращений можно было бы не делать вовсе.

Что с этим у нас

Проговорим для ясности.

Отказ по частоте приходит кодом rate_limit_exceeded. Он относится к тому классу отказов, при которых повтор осмыслен — в отличие от некорректного запроса или закрытого маршрута, где повторять бессмысленно, пока вы не измените код. Полный разбор всех кодов — в материале про отказы маршрута.

Деньги за отказ, случившийся до обращения к сервису модели, не списываются. Но если модель уже начала работу, за проделанную часть счёт возможен — ещё один довод не устраивать бесконечные повторы дорогих запросов.

Отдельный ключ под каждый сценарий заводится за минуту и решает сразу две задачи: видно, кто сколько тратит, и один сценарий не мешает другому.

Чего мы не утверждаем

Не приводим конкретных числовых лимитов. Они зависят от условий и меняются; актуальные значения нужно смотреть в своём кабинете и в документации, а не в статье.

Не обещаем, что очередь решит всё. Она сглаживает всплески, но не увеличивает пропускную способность. Если средняя нагрузка выше ёмкости, очередь просто растёт.

Не описываем поведение других провайдеров. У каждого свои оси ограничений и своя реакция на превышение.

Что держать в голове

Лимит — это механизм честного распределения конечной ёмкости, а не поломка. Реакция на него определяется тем, интерактивный запрос или фоновый: первый повторяют с растущей паузой и жёстким пределом, второй ставят в очередь.

Самый действенный шаг обычно не в обработке отказов, а до неё: ограничитель скорости на своей стороне и кэш повторяющихся обращений убирают большую часть упоров в лимит вместе с причиной.

Как мы проверяем факты и почему у каждого стоит источник — на странице о проекте. Отдельный ключ с собственным лимитом под каждый сценарий: keydealer.ru/login.

Частые вопросы

Что означает ошибка rate_limit_exceeded?

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

Зачем вообще нужны лимиты?

Мощности под инференс конечны. Без ограничения один клиент со всплеском нагрузки испортил бы работу всем остальным.

Как правильно повторять запрос?

С увеличивающейся паузой и жёстким пределом числа попыток. Повтор сразу же усугубляет перегрузку, а бесконечные попытки превращают короткий сбой в долгую деградацию.

Чем очередь лучше повторов?

Очередь сглаживает всплеск: запросы не теряются, а выполняются с задержкой. Повторы же создают дополнительную нагрузку ровно в тот момент, когда её и так много.

Списываются ли деньги за отказ по лимиту?

Нет, если отказ произошёл до обращения к сервису модели.

Источники