Мы ограничивали собственные сервисы по частоте запросов
API Mythex допускает 300 запросов в минуту с одного сетевого адреса. Это ограничение существует, чтобы защитить нас от одного человека или скрипта, который засыпает API запросами. В продакшене оно начало отказывать нашим собственным сервисам — части нашего edge, которая отдаёт опубликованные приложения, и сервису, который запускает AI-агента, — потому что каждый из них обращается к API лишь с небольшого числа адресов. Теперь запросы с нашим внутренним ключом освобождены от ограничения, а все остальные ограничиваются точно так же, как раньше.
Симптом
API стал отвечать на некоторые наши внутренние вызовы кодом 429 Too Many Requests. Попали под это три типа вызовов, и каждый важен для тех, кто пользуется Mythex:
- Поиск того, какое приложение живёт на собственном домене. Когда посетитель открывает опубликованное приложение на его домене, наш edge спрашивает API, какой это сайт. Ошибка 429 здесь — это посетитель, который не может открыть сайт.
- Сохранение хода работающего агента. Пока агент работает, его прогресс сохраняется по ходу, чтобы ответ пережил обновление страницы или разорванное соединение. Ошибка 429 здесь — прогресс, который не сохранился.
- Проверка доступа к превью. Превью приложения, которое вы создаёте, закрыто, и этот замок обращается к API. Ошибка 429 здесь — превью, которое не открывается.
Ни один из этих вызовов не был обращением людей к API. Все они были нашими.
Почему так вышло
Ограничение числа запросов с одного адреса делает одно допущение: один адрес — примерно один вызывающий. Для людей это почти верно. Для нашей собственной инфраструктуры — сильно нет.
- Сервис edge, который отдаёт опубликованные приложения и превью, обращается к API с нескольких общих исходящих адресов. Каждый посетитель каждого опубликованного приложения и каждая проверка превью выглядят так, будто пришли с одного из этих немногих адресов.
- Оркестратор — сервис, который запускает ходы агента, — обращается к API с одного адреса контейнера. Каждый работающий ход, для каждого пользователя, использует один и тот же адрес.
Таким образом бюджет, рассчитанный на одного человека, делился на весь трафик целого сервиса. Эти сервисы исчерпывали его между собой, и ограничитель сделал то, что ему велели: отказал им.
Исправление
Наши собственные сервисы и так доказывают, кто они. Каждый внутренний вызов несёт внутренний токен, и каждый маршрут во внутреннем разделе API проверяет этот токен, прежде чем что-либо делать.
Теперь ограничитель использует ту же проверку. Запрос, токен которого совпадает с внутренним, попадает в список разрешённых и не учитывается в бюджете на адрес. Всё остальное считается ровно так же, как раньше: 300 запросов в минуту на адрес.
Две детали не дали этому стать дырой:
- Неверный токен пропуска не получает. Список разрешённых совпадает только с настоящим внутренним токеном. Запрос с выдуманным токеном учитывается и ограничивается как любой другой, а внутренние маршруты по-прежнему отвергают его с
401 Unauthorized. - Само ограничение не менялось. Мы не подняли число, чтобы освободить место. Повышение скрыло бы проблему до следующего роста трафика и ослабило бы защиту в том случае, ради которого ограничение существует.
Мы вынесли настройки ограничителя в одну небольшую функцию и написали тесты к ней. Один отправляет 20 запросов с внутренним токеном на ограничитель, настроенный на 5, и ожидает, что все 20 пройдут. Другой отправляет обычные запросы с одного адреса и ожидает, что первые пять пройдут, а следующие будут отклонены. Третий отправляет запросы с подделанным токеном и ожидает, что они будут отклонены так же.
Что мы из этого вынесли
- У ограничений есть допущения. «Один адрес — один вызывающий» заложено в ограничение на адрес. Оно перестаёт быть верным, как только ваши собственные сервисы сидят за общими адресами — а так бывает часто: балансировщики нагрузки, edge-сети, контейнеры, офисные сети.
- Отличайте свой трафик по идентичности, а не по адресу. Адрес — это то, как трафик приходит, а не то, кто его отправил. У нас уже был ключ, который доказывает, что вызов наш; ограничитель просто на него не смотрел.
- Не решайте это повышением числа. Большее ограничение убрало бы ошибки 429 на время, защищало бы меньше и снова подвело бы позже.
- Смотрите, кого ограничивают. Ошибка 429, которая попадает на ваш собственный сервис, — это сигнал о ваших лимитах, а не признак того, что ограничитель работает.
Что это значит, если вы строите на Mythex
Со своей стороны ничего менять не нужно. Опубликованные приложения на собственных доменах, ходы агента и превью больше не зависят от того, насколько загружен остальной Mythex. Трафик вашего приложения — дело вашего приложения: ограничение, о котором здесь речь, стоит на нашем API, а не на опубликованных вами приложениях.
Если у вас есть собственный бэкенд, та же ловушка касается любого ограничения частоты, которое вы добавите. Хорошая отправная точка — наши руководства что такое API и чек-лист безопасности для приложений, созданных с помощью AI. А про другой случай, когда наша собственная инфраструктура мешала сама себе, см. как мы добились нуля ошибок 502 при деплоях.