Перейти к содержимому

Опрос и идемпотентность

GET /serving-tasks/?status=offered — то, как вы находите работу. Webhook’и, если зарегистрированы, являются оптимизацией задержки — но не правдой. Опрашивайте, пока ваш парк на смене, с той частотой, которую вам даёт Sitora.

Одна страница отдаёт не более limit задач — 50 по умолчанию, максимум 100. На загруженной кассе предложений может быть больше, чем показывает одна страница, поэтому поднимайте limit, а не читайте короткий список как тихую кухню. Список доставок webhook’ов принимает тот же параметр: 100 по умолчанию, максимум 200.

Каждый ответ со списком задач — и 200, и 304 — сообщает, сколько секунд подождать перед следующим запросом:

X-Sitora-Poll-After: 2
Retry-After: 2

Оба заголовка несут одно и то же число. Подождите столько и опрашивайте снова.

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

Retry-After в успешном ответе — это темп, а не ошибка. 200 или 304 с этим заголовком означает ровно то, что написано: вот ответ, и вот когда вернуться.

Heartbeat и снимок парка так не темпируются. Продолжайте слать heartbeat по своему таймеру, примерно раз в 30 секунд, как описано в heartbeat и состоянии парка.

Список задач несёт ETag. Отправляйте его обратно как If-None-Match; неизменившийся список вернёт 304 Not Modified без тела. Это делает плотные циклы опроса почти бесплатными для обеих сторон — реализуйте это обязательно.

Список доставок webhook’ов ETag не несёт. Листайте его через since_id.

Лимиты строгие и считаются по учётным данным:

Поверхность Лимит
Опрос задач 60/минуту (~1/с)
Принятие/отчёт по задаче 120/минуту
Heartbeat 30/минуту

Контроллер, которому нужно больше, ведёт себя неправильно, а не занят. 429 — это отказ Sitora, а не темп: его Retry-After обязателен к соблюдению, а само попадание в него означает, что ваш цикл проигнорировал заголовок частоты выше.

Каждая мутация принимает заголовок Idempotency-Key (или поле тела client_idempotency_key). Повторный запрос с тем же ключом возвращает исходный результат и ничего нового не записывает.

Всегда отправляйте его. Сеть рвётся посреди запроса; с ключом вы можете свободно повторять и никогда не примените дважды принятие или отчёт.

Используйте один ключ на действие, а не один на задачу: тот же ключ для повторов того же логического действия и новый ключ для каждого следующего. Ключ, уже потраченный на другой переход той же задачи, отклоняется с 409 (idempotency_key_reused). Ключи ограничены 128 символами.

Тела запросов тоже ограничены — необязательные объекты telemetry и summary в heartbeat должны быть JSON-объектами не более 4 КБ, вложенностью не более 5 уровней и не более чем с 50 записями в любом объекте или массиве.

Отказ, который поднимает сама поверхность робототехники, возвращает одну форму:

{ "detail": "Понятное человеку объяснение.", "code": "machine_readable_code" }
HTTP-статус Коды
400 unknown_status (неизвестное значение в фильтре status), invalid_limit, invalid_since_id, unknown_failure_code
404 task_not_found, delivery_not_found
409 already_claimed, robotics_paused, platform_frozen, arrival_dwell_pending, idempotency_key_reused, конфликты порядка task_<current_status>
410 offer_expired
503 content_key_missing — эта установка Sitora сконфигурирована неверно, повтор не поможет

Ветвитесь по code, а не по тексту detail — формулировка detail может меняться; коды контрактны.

Три отказа отвечают ещё до того, как запрос дойдёт до поверхности робототехники, поэтому поля code у них нет. Клиент, который безусловно читает body["code"], падает на всех трёх — читайте это поле защитно.

  • Некорректное тело отклоняется валидацией поля, и тело ответа ключуется по проблемному полю: {"status": ["\"loaded\" is not a valid choice."]}.
  • 401 и 403 возвращают {"detail": "..."}. 401 означает, что ключ недействителен или отозван. 403 означает, что ключ верен, но действовать им нельзя: либо парк утратил правомочность — сертификация, активация или право по тарифу — либо у ключа нет нужного этому эндпоинту scope. Причина названа в тексте.
  • 429 возвращает {"detail": "..."} с заголовком Retry-After.