RussiaAPI

Надёжность API

Повторные запросы AI API без дублирования задач

Сеть может оборвать ответ уже после того, как AI API принял работу. Если приложение бездумно отправит тот же запрос повторно, пользователь получит две генерации, две записи в базе или двойное внутреннее списание. Идемпотентность помогает отличить «ответ не дошёл» от «операция не запускалась» и делает повтор контролируемым.

Опубликовано 12 августа 2026 · 12 минут чтения · Ключевой запрос: идемпотентность запросов AI API

Граница сервиса. RussiaAPI — независимый сторонний API gateway, а не официальный сервис OpenAI, Anthropic, Google или производителя модели. Совместимость интерфейса не означает совпадение всех возможностей. Используйте только собственный ключ RussiaAPI, не передавайте ключи вышестоящих поставщиков и не используйте интеграцию для обхода ограничений, правил или применимых требований.

Короткий ответ

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

Почему обычный retry создаёт дубли

HTTP-клиент видит только свою сторону соединения. Тайм-аут может случиться до отправки, во время обработки или после успешного ответа. Второй запрос с тем же текстом не сообщает API, что это продолжение первого. Для текста это означает два разных ответа; для видео — две длительные задачи; для приложения с биллингом — риск двойного учёта. Нельзя решать проблему сменой ключей, параллельными запросами или повтором каждую секунду.

Идемпотентность — не свойство магического заголовка, а договор внутри вашего приложения. Вам нужны уникальный идентификатор действия, место для хранения состояния, атомарное создание записи и понятное правило жизни результата. Внешний endpoint может поддерживать специальный заголовок, но его наличие всегда проверяют в актуальной документации и на тестовом контуре. Не заявляйте поддержку, если вы её не подтвердили.

Схема операции и срок хранения

Практичная таблица содержит operation_id, хеш нормализованного входа, пользователя или проект, статус, внешний request ID, результат или ссылку на него, время создания и истечения. Не кладите туда API Key, заголовок Authorization, исходный документ клиента или полный prompt без необходимости. Для чата можно хранить обезличенный хеш и итоговый текст согласно своей политике данных; для больших результатов — ссылку на защищённое хранилище.

Ключ должен описывать именно намерение пользователя. Если пользователь нажал «Отправить» два раза для одной формы, операция та же. Если он изменил prompt и отправил его снова, это новая операция. UUID, созданный сервером или клиентом для одного действия, удобнее составного ключа из текста: текст может содержать персональные данные, а хеш требует чёткой нормализации. Срок хранения выбирают по продукту: достаточно долгий для повторов и обновления страницы, но не бесконечный.

Безопасный серверный пример

Ниже пример Node.js 20+. Он демонстрирует локальную логику и предполагает, что функции operations выполняют атомарную работу с вашей БД. Названия модели и endpoint надо сверить с текущим каталогом RussiaAPI. Код запускается на сервере; переменная с ключом не должна попадать в браузер, Git или логи.

import crypto from 'node:crypto';

export async function createOnce(userId, prompt, suppliedId) {
  const operationId = suppliedId || crypto.randomUUID();
  const op = await operations.insertIfAbsent({
    operationId, userId, status: 'pending'
  });
  if (!op.created) return operations.read(operationId);

  try {
    const response = await fetch('https://russiaapi.com/v1/chat/completions', {
      method: 'POST', signal: AbortSignal.timeout(20_000),
      headers: { Authorization: `Bearer ${process.env.RUSSIAAPI_API_KEY}`,
        'Content-Type': 'application/json', 'X-Client-Operation': operationId },
      body: JSON.stringify({ model: process.env.RUSSIAAPI_MODEL,
        messages: [{ role: 'user', content: prompt }] })
    });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    const body = await response.json();
    return operations.complete(operationId, body);
  } catch (error) {
    return operations.failOrKeepPending(operationId, String(error));
  }
}

Этот пример не обещает, что внешний сервис интерпретирует пользовательский заголовок как ключ идемпотентности: он нужен вашему журналу и корреляции. Критична операция insertIfAbsent с уникальным индексом. Два одновременных воркера не должны оба увидеть «пусто» и отправить две работы. Для SQL это часто INSERT ... ON CONFLICT или транзакция с уникальным ограничением; конкретный вариант зависит от базы.

Как повторять после ошибки

Сначала классифицируйте ошибку. 401 и 403 обычно требуют проверки собственного ключа, endpoint и прав, а не retry. Ошибка в JSON, неизвестная модель и нарушение валидации также не лечатся ожиданием. Для 429 и некоторых временных 5xx допустима ограниченная политика с exponential backoff, jitter, максимумом попыток и общим дедлайном. Подробная базовая схема есть в статье про 429, retry и backoff.

После тайм-аута не отправляйте новую генерацию немедленно. Прочитайте состояние своей операции. Если есть внешний task ID, опросите его по правилам соответствующего API либо дождитесь callback. Если статуса узнать нельзя, пометьте работу как unknown, дайте оператору или очереди безопасный путь проверки и покажите пользователю честное сообщение. Именно этот промежуточный статус не даёт скрыть неопределённость новой платной задачей.

Очередь, отмена и наблюдаемость

Идемпотентность работает вместе с очередью. Один consumer берёт операцию в работу, ставит блокировку или lease с истечением и периодически продлевает её. При падении процесса другой worker не стартует работу сразу: сначала проверяет текущий статус, срок lease и внешний идентификатор. Отмена пользователем тоже не всегда отменяет уже принятую генерацию. Сохраните запрос отмены и следуйте возможностям конкретного endpoint, не обещая мгновенного результата.

В метриках отслеживайте число повторных обращений к одной операции, длительность pending, долю unknown, дубли, конечные статусы и время до результата. Коррелируйте по operation ID и request ID, но маскируйте секреты и персональные данные. Такой журнал помогает доказать, что проблема в клиентской сети, очереди или конфигурации, не требуя от пользователя прислать ключ.

Типичные ошибки

Для асинхронных задач особенно полезно совместить operation ID с жизненным циклом task ID и защищённым webhook. А миграционные проекты стоит сначала прогнать на небольшом тестовом наборе: план смены совместимого endpoint объясняет, как задать пороги остановки и отката.

Проверьте защиту от дублей на тестовом контуре

Создайте отдельный собственный ключ RussiaAPI для тестовой среды, сверьте доступные модели и воспроизведите двойной клик, тайм-аут и повтор из очереди. Переходите к production только после проверки статусов и журналов.

Открыть консоль RussiaAPI

FAQ

Можно ли повторить чат-запрос после тайм-аута?

Можно только после проверки состояния операции. Тайм-аут клиента не доказывает, что сервер не принял запрос. Сохраните operation ID, получите его статус и создавайте новую работу лишь при безопасном результате проверки.

Нужен ли один idempotency key на весь аккаунт?

Нет. Ключ уникален для одного бизнес-действия и имеет ограниченный срок хранения. Сочетание пользователя, операции и случайного UUID надёжнее глобального предсказуемого счётчика.

Нужно ли передавать ключ провайдера для диагностики дублей?

Нет. Не передавайте ключи, cookie или секреты. Для расследования достаточно времени, request ID, ID операции, статуса и обезличенного лога без заголовка Authorization.

Читайте также