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

Ограничение запросов и повторы

ShopStory не публикует универсальные числовые лимиты для всех клиентов, API-методов и окружений. Согласуйте гарантированную квоту или SLA с ShopStory и зафиксируйте параметры в интеграционном контракте проекта.

Клиент должен обрабатывать HTTP 429, временные 5xx, timeout и сетевые ошибки.

Правила повторов

  1. Автоматически повторяйте только безопасные идемпотентные чтения.
  2. Учитывайте Retry-After, если сервер его прислал.
  3. Без Retry-After используйте экспоненциальную задержку со случайным разбросом (jitter).
  4. Ограничивайте число попыток и общую продолжительность запросов (deadline).
  5. Не повторяйте ошибки конфигурации, валидации и идентификации.
  6. Не повторяйте изменение корзины автоматически без idempotency-защиты на стороне ритейлера.

Для ShopStory API проверяйте и транспортный HTTP-код, и status внутри JSON-конверта. Бизнес-ошибка внутри HTTP 200 не является временным сетевым сбоем.

Пример для GET

request-with-retry.js
function retryDelay(response, attempt) {
const value = response?.headers.get('Retry-After');
if (value) {
const seconds = Number(value);
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);

const date = Date.parse(value);
if (Number.isFinite(date)) return Math.max(0, date - Date.now());
}

const backoff = Math.min(8000, 500 * 2 ** attempt);
return backoff + Math.floor(Math.random() * 300);
}

function wait(delay, signal) {
return new Promise((resolve, reject) => {
if (signal?.aborted) return reject(signal.reason);

const onAbort = () => {
clearTimeout(timer);
reject(signal.reason);
};
const timer = setTimeout(() => {
signal?.removeEventListener('abort', onAbort);
resolve();
}, delay);
signal?.addEventListener('abort', onAbort, { once: true });
});
}

async function getWithRetry(url, { attempts = 3, signal } = {}) {
for (let attempt = 0; attempt < attempts; attempt += 1) {
try {
const response = await fetch(url, {
headers: { Accept: 'application/json' },
signal,
});

const retryable = response.status === 429 || response.status >= 500;
if (!retryable) return response;
if (attempt === attempts - 1) return response;

await wait(retryDelay(response, attempt), signal);
} catch (error) {
if (signal?.aborted || attempt === attempts - 1) throw error;
await wait(retryDelay(null, attempt), signal);
}
}
}

Если попытки закончились, обработайте это до разбора JSON. Не показывайте устаревшие данные как подтверждение покупки.

Опрос API

  • Не запускайте отдельный бесконечный polling для каждой карточки товара.
  • Останавливайте запросы, когда экран скрыт или пользователь покинул страницу.
  • Увеличивайте интервал после ошибок и восстанавливайте его постепенно.
  • Объединяйте одинаковые одновременные запросы.
  • Для стандартного плеера используйте SDK: он сам управляет сетевыми запросами.
  • Частоту запросов кастомного live UI выбирайте с учётом допустимой задержки и квоты проекта.

Каталог и состояние плеера можно кратковременно кэшировать по правилам раздела Кэширование. При покупке merchant-система проверяет актуальный остаток заново.

Метрики

Отслеживайте:

  • долю 429, 5xx и timeout;
  • число попыток на исходный запрос;
  • время от первого запроса до завершения всех попыток;
  • возраст данных, показанных пользователю;
  • число переходов интерфейса в деградированный режим.

Не записывайте в метрики полный URL, cookies, токены и другие учётные данные.