Мок-API

Лимиты

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

На этой странице · 6

Лимиты в песочнице — эмуляция поведения боевого API, а не защита APIStend. Смысл в том, чтобы код, который в бою упрётся в лимит, упёрся в него и на стенде, и обработал это правильно: у Bitrix24 — по коду 503, у остальных — по 429.

Схема: дырявое ведро

У каждой пары «ключ + сервис» своё ведро — а у Apify на каждый класс путей своё, см. ниже. Ёмкость — burst; ведро пополняется плавно, со скоростью limit запросов за windowMs. Запрос забирает один токен; если токенов нет — превышение.

Профили лимитов

СервисСкорость пополненияЁмкость (burst)Код превышенияПауза до повтора
Bitrix242 запроса в секунду50503 QUERY_LIMIT_EXCEEDEDне сообщается
Ozon Seller API50 запросов в секунду50429не сообщается
Wildberries300 запросов в минуту300429X-Ratelimit-Retry: 20
Apify60 запросов в секунду и больше60 и больше429не сообщается

Ведро — не украшение. Без него мок оказался бы в двадцать пять раз строже боевого Bitrix24: приложение, которое при старте вызывает app.info, profile и placement.bind, получало бы 503 на третьем вызове.

У Apify лимит принадлежит методу, а не сервису

Единственный сервис в стенде, где одного числа мало. Документация Apify называет базовые 60 запросов в секунду на ресурс, 200 на записи key-value store и 400 на запуски и датасеты; живой ответ /v2/users/me добавляет к этому 90 на профиль пользователя.

КлассЛимитПути
Запуски и датасеты400/с/v2/actor-runs, /v2/datasets, запуски и сборки внутри актора и задачи
Key-value store200/с/v2/key-value-stores
Профиль пользователя90/с/v2/users
Базовый60/свсё остальное, включая карточку актора

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

Глобальный потолок Apify — 250 000 запросов в минуту на пользователя — мок не воспроизводит: до него не доберётся никакая отладочная нагрузка, а ведро на четверть миллиона токенов означало бы держать состояние ради события, которое не наступит.

Как это выглядит

Пятьдесят один запрос подряд к Bitrix24 проходит, дальше — 503:

for i in $(seq 1 60); do
  curl -s -o /dev/null -w "%{http_code} " -X POST \
    localhost:8080/b24/rest/crm.deal.fields.json -H "X-Mock-Key: $KEY" -H "X-Mock-Delay: 0"
done
# 200 200 … 200 503 503 503 503 503 503 503 503 503
HTTP/1.1 503 Service Unavailable
content-type: application/json; charset=utf-8
x-apistend-retry-after: 1
x-apistend-scenario: rate_limit

{"error":"QUERY_LIMIT_EXCEEDED","error_description":"Too many requests"}

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

У Bitrix24 это 503, а не 429

Самая частая ошибка в обработке лимита Bitrix24 — ловить 429. Портал при превышении отвечает 503 QUERY_LIMIT_EXCEEDED и не присылает Retry-After; интервал повтора клиент выбирает сам. Мок повторяет это буквально.

Заголовки лимита есть не у всех

Это первое, что ломает ожидания: единого набора заголовков у сервисов нет, и APIStend не приписывает их тем, у кого их не бывает.

СервисЧто приходитГде смотреть остаток
WildberriesX-Ratelimit-Limit, X-Ratelimit-Remaining, X-Ratelimit-Reset, при превышении X-Ratelimit-Retryв заголовках
Ozon Seller APIничегонигде: считайте сами по коду 429
Bitrix24ничегов теле, поля time.operating и time.operating_reset_at
Apifyтолько X-RateLimit-Limitнигде: остаток и сброс Apify не сообщает

В X-Ratelimit-Limit стоит ёмкость ведра, а не средняя скорость: клиент по этому числу считает, сколько запросов может отправить сразу. X-Ratelimit-Reset — через сколько секунд ведро наполнится целиком, X-Ratelimit-Retry — через сколько разрешён следующий запрос.

Рекомендуемую паузу там, где боевой сервис её не сообщает, шлюз отдаёт своим заголовком X-APIStend-Retry-After. Он начинается с x-apistend-, и это сознательно: чужой заголовок подделывать нельзя, а подсказать — можно.

Не пишите обработку лимита по заголовкам Wildberries для всех трёх

Код вида «прочитать X-Ratelimit-Retry и поспать столько секунд» на Ozon и Bitrix24 не сработает ни на стенде, ни в бою: заголовка там нет. Для них нужен собственный откат — экспоненциальный или по time.operating_reset_at у Bitrix24.

Проверить обработку, не расходуя ведро

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

curl -i localhost:8080/wb/api/v3/warehouses \
  -H "X-Mock-Key: $KEY" -H "X-Mock-Scenario: rate_limit"

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

Подводные камни

  • Ведро считается на ключ и сервис по отдельности. Два ключа одной песочницы — два независимых ведра; исчерпать лимит Ozon, стуча в Bitrix24, нельзя.
  • У локального приложения Bitrix24 ведро своё — на приложение, а не на ключ.
  • Счётчик живёт в памяти процесса. После перезапуска API все вёдра полные; на нескольких инстансах лимит считается на каждом отдельно.
  • Токен тратится на любом запросе, включая тот, что закончится 404 или ошибкой авторизации, и включая запрос с X-Mock-Scenario.
  • Порядок такой: сначала лимит, потом сценарий. Если ведро пусто, заказанный заголовком сценарий уступает место rate_limit.
  • Отдельно от этого существует лимит Management API (/api/v1/…) — он ограничивает нагрузку на сам APIStend и к эмуляции боевых лимитов отношения не имеет.