Повторные запросы: что безопасно повторять, а что расходует квоту

Источник: AudD•

Повторные запросы: что безопасно повторять, а что расходует квоту

Какие сбои API AudD безопасно повторять, какие расходуют запрос и как настроить бюджет повторных попыток в SDK без двойной тарификации или дублирования записей в каталоге.

Повторный запрос — это ставка на то, что тот же вызов сработает со второй попытки. Для API распознавания музыки эта ставка обычно оказывается верной: вызов распознавания идемпотентен, поэтому повтор при кратковременном сбое стоит вам лишь небольшой задержки. Однако некоторые вызовы небезопасно повторять вслепую, а некоторые ошибки никогда не исчезнут, сколько бы раз вы их ни повторяли. На этой странице ошибки AudD разделены на категории «повторить», «не повторять» и «сделать паузу» (back off), а также объясняется, что SDK уже делают за вас, чтобы вы не накладывали второй цикл повторов поверх существующего.

Кратко (TL;DR)

  • Официальные официальные SDK уже автоматически повторяют временные сбои (ошибки соединения и ошибки сервера 5xx) с экспоненциальной задержкой для идемпотентных вызовов распознавания. Обычно вам не нужно добавлять собственные повторы поверх этого.
  • Вызовы распознавания безопасно повторять. Отправка того же аудио повторно возвращает аналогичный результат.
  • Отмена вызова распознавания в процессе выполнения все равно может расходовать запрос. Как только ваши байты отправлены, тарифицируемая работа уже может выполняться. Не считайте отмененный вызов бесплатным.
  • Загрузку в пользовательский каталог (/upload/) нельзя автоматически повторять вслепую. Наивный повтор может создать дубликаты записей в каталоге. Сделайте повторы загрузки идемпотентными на своей стороне или проверяйте состояние перед повторной загрузкой.
  • При ошибках ограничения частоты запросов следует делать паузу, а не бомбардировать сервер. Стандартная конечная точка распознавания не имеет лимитов частоты, поэтому в AudD они возникают из-за лимитов потоков (ошибка 611) или ограничений параллелизма на корпоративной конечной точке. Немедленный повтор вызова только усугубит ситуацию.
  • Правило принятия решений по категориям ошибок: повторять при проблемах с соединением и сервере; делать паузу при ограничениях частоты; не повторять при проблемах с аутентификацией, квотой, подпиской, неверным запросом или неверным аудио.

Почему это важно

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

Есть две особенности, из-за которых повторные попытки в API распознавания заслуживают осмысления, а не простого копирования кода. Во-первых, SDK уже выполняют повторы за вас, поэтому написанный вручную цикл поверх них может умножить число попыток (ваши 3 попытки, умноженные на 3 попытки SDK, дают 9) и превратить вежливую задержку в лавину. Во-вторых, вызовы тарифицируются, поэтому повторная попытка не бесплатна — и отмененный вызов также не гарантирует бесплатности. Понимание того, какие сбои стоят второй попытки, а с какими SDK уже справляется, избавит вас от двойной оплаты ни за что или дублирования записей в каталоге.

Что SDK уже делают

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

Практическое следствие:

Не оборачивайте вызовы распознавания в собственный цикл повторов по умолчанию. SDK уже повторяет ошибки соединения и сервера с задержкой. Второй цикл поверх этого умножает количество попыток и может превратить задержку в лавину. Настройте бюджет повторных попыток SDK вместо добавления собственного слоя.

Не оборачивайте вызовы распознавания в собственный цикл повторов по умолчанию. SDK уже повторяет ошибки соединения и сервера с задержкой. Второй цикл поверх этого умножает количество попыток и может превратить задержку в лавину. Настройте бюджет повторных попыток SDK вместо добавления собственного слоя.

То, что SDK не повторяет за вас молча — это ошибки, которые не исчезнут при повторе (аутентификация, квота, подписка, неверный запрос, неверное аудио), а также ответы о превышении лимита частоты запросов, для которых требуется задержка, которую вы можете захотеть контролировать. Эти ошибки передаются в ваш код в виде типизированных исключений, чтобы вы могли принять решение.

Повторяемые: ошибки соединения и сервера

Они носят временный характер. Запрос был корректно сформирован и авторизован; что-то на пути между вами и результатом временно вышло из строя.

  • Ошибка соединения (AudDConnectionError) — сбой DNS, разорванный сокет, тайм-аут рукопожатия TLS, запрос так и не дошел до сервера. SDK повторяет такие запросы.
  • Ошибки сервера (AudDServerError, возвращающие код 5xx) — сервер принял запрос, но не смог его завершить. По своей природе временные; SDK также повторяет их.

Поскольку распознавание идемпотентно, повторный вызов возвращает тот же тип ответа — совпадение либо result: null / пустой список при отсутствии совпадений. Вы не рискуете получить другой результат из-за повтора; вы тратите только время ожидания и, если исходный запрос уже выполнил тарифицируемую работу, возможно, запрос (см. ниже).

Неповторяемые: проблема в самом запросе

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

  • Аутентификация (AudDAuthenticationError) — отсутствует, поврежден или невернен api_token. Каждый повтор завершается одинаковой ошибкой. Сообщайте об этом громко при запуске, а не для каждого запроса.
  • Квота (AudDQuotaError) — вы исчерпали лимит запросов. Дополнительные попытки не восстановят его. Передайте информацию операторам; пополните баланс или дождитесь сброса окна.
  • Подписка (AudDSubscriptionError) — функция не включена в ваш тарифный план (например, вызов корпоративной конечной точки без доступа к корпоративному тарифу). Повторная попытка не может предоставить доступ.
  • Неверный запрос (AudDInvalidRequestError) — поврежденный параметр, отсутствующее обязательное поле. Запрос неверен; его повтор отправляет тот же неверный запрос.
  • Неверное аудио (AudDInvalidAudioError) — файл не является декодируемым аудио или видео. Байты не декодируются и со второй попытки. Рассматривайте это как ошибку пользователя/ввода: отклоните загрузку с понятным сообщением.

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

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

Ошибка ограничения частоты (код 429 в стиле «слишком много запросов») представляет собой отдельную категорию. Это не постоянный сбой вроде аутентификации и не бесплатный повтор вроде сбоя соединения. Сервер явно просит вас замедлиться.

В AudD распознавание на стандартной конечной точке не имеет лимитов частоты: вы можете отправлять запросы параллельно, вплоть до миллионов в час (при больших объемах сообщите об этом AudD заранее по адресу [email protected]). С чем вы можете столкнуться, так это с дневным лимитом потока (ошибка 611) или лимитом корпоративной конечной точки по умолчанию на параллельные запросы, который AudD увеличивает по запросу.

Правильная реакция — подождать и повторить попытку с возрастающей задержкой (экспоненциальный бэкофф, в идеале со случайным разбросом/jitter, чтобы группа воркеров не выполняла повторы одновременно). Немедленный повтор только усугубляет ситуацию: вы добавляете нагрузку на конечную точку, которая и так от нее избавляется, и можете продлить окно троттлинг-ограничений.

Ограничение частоты означает «замедлитесь», а не «остановитесь» или «повторите прямо сейчас». Делайте паузу с экспоненциальной задержкой и разбросом. Если вы сталкиваетесь с лимитами постоянно, а не всплесками, решением будет уменьшение числа параллельных запросов или обращение в AudD с просьбой увеличить лимит, а не более частый цикл повторов.

Лимиты запросов означают замедление, а не остановку и не немедленный повтор. Используйте экспоненциальную задержку с добавлением случайности (jitter). Если вы регулярно упираетесь в лимиты, а не в пиковые моменты, решением будет уменьшение количества одновременных запросов или обращение в AudD с просьбой повысить лимит, а не более частый цикл повторных попыток.

Отмена не гарантирует отсутствие списания

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

Как только ваши аудиобайты поступили на сервер, тарифицируемый процесс распознавания уже может выполняться. Отмена соединения с вашей стороны прекращает ожидание для вас, но не обязательно останавливает работу сервера. Поэтому относитесь к отмененному вызову как к потенциально обработанному, а не обязательно бесплатному.

Последствия:

  • Не создавайте паттерн «отменить и немедленно повторить для экономии денег». Отмененная попытка могла стоить одного запроса, а повторная стоит еще одного.
  • Устанавливайте клиентские таймауты с запасом выше ожидаемого времени ответа. Стандартный эндпоинт отвечает быстрее чем за 2 секунды; корпоративные запросы для длинных файлов занимают больше времени, пропорционально объему отправленного на обработку аудио. Прекращение медленного, но работающего запроса может привести к оплате работы, результат которой вы затем выбросите.

Загрузка — это особый случай: никогда не выполняйте слепые автоматические повторы

Загрузка в пользовательский каталог (POST api.audd.io/upload/) добавляет трек в частную базу данных цифровых отпечатков вашего аккаунта. Она не идемпотентна в отличие от распознавания: слепой повтор после неопределенного сбоя может создать дубликат записи в каталоге, который затем будет срабатывать дважды при каждом последующем распознавании.

Опасный сценарий — неопределенный сбой: загрузка на стороне сервера прошла успешно, но ответ был утерян (соединение разорвалось после завершения работы). Наивный повтор загружает тот же трек во второй раз.

Сделайте повторы загрузки безопасными на вашей стороне:

  • Проверяйте перед повторной загрузкой. Перед новой попыткой запросите из каталога трек (по вашему внешнему ID или хешу содержимого) и пропустите загрузку, если он уже там есть.
  • Используйте собственный ключ идемпотентности. Отслеживайте каждую предполагаемую загрузку по стабильному идентификатору в вашей системе и отмечайте ее как выполненную только после подтвержденного успеха, чтобы перезапуск воркера не привел к повторной отправке.
  • Не помещайте /upload/ в тот же общий оберточный механизм автоматических повторов, что и распознавание. У них разные характеристики безопасности.

Пример из практики: один воркер, три сбоя

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

Важная закономерность: кратковременный сбой → возврат в очередь (без встроенного цикла), превышение лимита → экспоненциальная задержка с последующим возвратом в очередь, все остальное → остановка и вывод ошибки. Внутренние повторы SDK обрабатывают быстрое восстановление прямо во время вызова; ваша очередь обрабатывает более медленное восстановление по принципу «вернитесь позже»; а ошибки, которые нельзя повторять, полностью исключаются из механизма повторов.

Настройка бюджета повторов SDK

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

Разумная базовая стратегия:

  • Небольшое ограниченное количество попыток (значение по умолчанию в SDK — разумная отправная точка).
  • Экспоненциальная задержка с добавлением случайности.
  • Использование очереди, а не встроенного цикла, для уровня «попробовать снова через 30 секунд».

Распространенные ошибки

  • Нанизывание собственного цикла повторов поверх цикла SDK. Это увеличивает количество попыток и превращает задержку в лавинообразный шквал запросов. Вместо этого настройте бюджет SDK.
  • Повторные попытки при ошибках аутентификации, квот, подписки или неверных запросах. Они никогда не улучшатся от повторения. Обнаруживайте их и останавливайтесь.
  • Повторные попытки при неверном формате аудио. Байты не декодируются со второй попытки. Отклоняйте такие входные данные.
  • Атака запросами при превышении лимита. Немедленные повторы продлевают блокировку. Используйте задержку с добавлением случайности.
  • Отмена и повторный вызов для «экономии» запроса. Отмененный вызов уже мог быть тарифицирован, а повторный стоит еще одного запроса. Отмененный вызов — это потенциально потраченный запрос, а не бесплатный.
  • Слепые автоматические повторы /upload/. Создают дубликаты записей в каталоге при неопределенном сбое. Сначала проверяйте или используйте ключ идемпотентности.
  • Неограниченные повторы для тарифицируемого вызова. Всегда ограничивайте бюджет.
  • Стандартные, корпоративные или потоковые запросы: как выбрать
  • Справочник ошибок
  • Контроль стоимости корпоративного эндпоинта
  • Справочник API

О чём эта статья