Повторите вызов, не делая работу дважды

PHP

Дайте запуску или движению кредитов ключ, который называет его единицу работы, — тогда на повтор, от SDK, от вашей очереди или от вашего кода, ответят, а не сделают работу заново.

5 мин чтения

Запуск флоу и движение кредитов несут ключ идемпотентности. Ключ называет одну единицу работы. Когда тот же вызов приходит ещё раз под тем же ключом, Slen отвечает на него, а не делает работу заново, — поэтому вызов, повторённый после таймаута, падения или повтора вашей очереди, — это не второе выполнение, не вторая трата и не второе пополнение.

Ключ, который создаёт SDK, и ключ, который передаёте вы

Ключ принимают пять вызовов: operations.execute(), operations.executeAsync(), customers.topUp(), customers.charge() и customers.adjust(). Ключ — это idempotencyKey опций вызова: ExecuteOptions запуска, MovementOptions движения кредитов — одни и те же имена в обоих SDK. Когда вы не передаёте ключ, SDK создаёт для этого вызова новый случайный.

  • Ключ, созданный SDK, покрывает только собственные повторы SDK. SDK отправляет запрос ещё раз под тем же ключом, когда ответа не было, после 429 или 503 — Повторы и таймауты.

  • Вызов, который ваш код делает ещё раз, — повтор вашей очереди, второе нажатие кнопки — получает новый случайный ключ, а новый ключ — это новая работа: флоу выполняется снова, кредиты движутся снова.

  • Пустой ключ, который вы передали, отклоняется до отправки запроса, с кодом invalid_request. SDK никогда не подставляет на его место свой.

Предупреждение:

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

Выбрать ключ

Делайте ключ из единицы работы, чтобы каждый повтор работы нёс тот же ключ, а следующая единица работы получала новый: заказ и его шаг (order-42:email), счёт на оплату и пополнение по нему (invoice-7781:topup) или задача вашей очереди, которая отправляет вызов.

PHP
use Slen\Sdk\IdempotencyKeys;

// The id of the job of your own queue that sends this request.
$jobId = 'a1f7c2e9';
$key = IdempotencyKeys::forJob($jobId, 'generate_email');

IdempotencyKeys::forJob() делает job:<id>:<operation> — один ключ на все попытки задачи. Задача, которая выполняется снова, получает обратно первое выполнение. IdempotencyKeys::generate() делает случайный ключ — тот, который SDK создаёт, когда вы не передали свой.

  • Ключ — не длиннее 190 символов. Более длинный отклоняется с 422, прежде чем что-либо выполнится.

  • Ключ принадлежит проекту API-ключа. Внутри проекта давайте каждой единице работы свой ключ.

Чем отвечают на повтор

PHP
use Slen\Sdk\Modules\Operations\Options\ExecuteOptions;

$input = ['messages' => [['role' => 'user', 'content' => 'Write a short follow-up email.']]];

$execution = $slen->operations()->execute('generate_email', $input, new ExecuteOptions(idempotencyKey: $key));

echo $execution->status->value, PHP_EOL;
PHP
// The queue runs the same job again, so the key is the same.
$again = $slen->operations()->execute('generate_email', $input, new ExecuteOptions(idempotencyKey: $key));

if ($again->replayed) {
    echo "Execution {$again->executionId} ran once. This answer is a replay.", PHP_EOL;
}
ВызовПовтор под его ключом
ЗапускВыполнение, которое начал первый запрос, в том состоянии, в каком оно находится в этот момент, с replayed, равным true. Флоу не выполняется снова
Пополнение, списание или корректировкаПервый ответ, в течение 24 часов. После этого — счёт таким, каким он стал. Кредиты движутся один раз. Отклонённое движение не сохраняется, и его повтор — новая попытка
  • На выполнение, которое не закончилось, отвечают 202 и статусом queued, waiting или running — running, когда выполнение первого запроса ещё идёт.

  • На выполнение, которое закончилось, отвечают 200 и его исходом. Так и тогда, когда первый запрос закончился ошибкой: повтор читает статус выполнения и его error_code.

  • На повторе может прийти cost.actual, когда в первом ответе его не было: расчёт за вызовы моделей идёт после конца выполнения.

Остальные вызовы SDK ключа не принимают. Чтение ничего не меняет, а создание клиента, настройки счёта, блокировка и разблокировка оставляют одно и то же состояние, сколько бы раз их ни отправили. Удаление, которое SDK повторил, не получив ответа, считается сделанным, когда клиента уже нет, — Повторы и таймауты. Те же ответы в терминах API приводит статья Идемпотентность.

Тот же ключ с другим запросом

PHP
use Slen\Sdk\Exceptions\IdempotencyConflictException;

try {
    $slen->operations()->execute(
        'generate_email',
        ['messages' => [['role' => 'user', 'content' => 'Write a short thank-you email.']]],
        new ExecuteOptions(idempotencyKey: $key),
    );
} catch (IdempotencyConflictException $exception) {
    echo "Refused: {$exception->errorCode}", PHP_EOL;
}

Ключ, который уже называет одну единицу работы, не принимает другую: API отвечает 409 с кодом idempotency_conflict, ничего не выполняется и ничего не движется, а SDK выбрасывает ошибку.

Исключение — IdempotencyConflictException. idempotency_in_progress приходит тем же исключением: первый запрос под этим ключом ещё не закончился. Отправьте вызов ещё раз позже, под тем же ключом.

  • Для запуска другой вход, другой контекст, другой клиент или другой режим — это другой запрос.

  • Для движения кредитов другой клиент, тип кредитов, вид или сумма — это другой запрос, а в первые 24 часа — и другое примечание.

  • Выполнение, которое упало, сохраняет свой ключ. Чтобы выполнить работу снова, после того как вы исправили запрос, отправьте его под новым ключом.

Что читать дальше