Idempotency keys в B2B order API 2026: deep dive с UUIDv4 и dedup-окном
Idempotency key — это техника, которая делает retry-safe-ом любой POST-запрос в B2B API. Без неё каждый network timeout на стороне клиента превращается в вопрос «заказ создался или нет?» — и в 30% случаев ответ «оба раза». В этой статье разбираем production-паттерны, заимствованные у Stripe, и применённые в FoxReload Orders API.
1. Почему один retry создаёт два заказа
Простая последовательность: ваш ERP делает POST /v1/orders с payload {sku: "PSN_TR_50", qty: 1}. FoxReload создаёт заказ, начинает delivery, но TCP-ответ теряется (network blip). Ваш HTTP-клиент через 30s делает retry — FoxReload видит новый запрос, создаёт второй заказ, списывает второй раз с баланса. Результат: дубль.
С idempotency key такой сценарий невозможен — сервер видит тот же ключ, возвращает оригинальный response, второй заказ не создаётся.
2. Генерация UUIDv4 на клиенте
Ключ обязан быть глобально уникальным и непредсказуемым. UUIDv4 — стандартный выбор:
import { randomUUID } from 'crypto';
import axios from 'axios';
async function createOrder(sku: string, qty: number) {
const idempotencyKey = randomUUID(); // UUIDv4
return axios.post('https://api.foxreload.com/v1/orders', {
sku, qty, currency: 'USD',
}, {
headers: {
'Authorization': `Bearer ${process.env.FOXRELOAD_KEY}`,
'Idempotency-Key': idempotencyKey,
},
timeout: 30000,
});
}
Сохраняйте idempotencyKey в вашей БД до того, как делать запрос. При retry читайте тот же ключ — не генерируйте новый.
3. Server-side dedup window
FoxReload хранит каждый ключ в Redis с TTL 24h. Логика обработки:
// Псевдокод server-side
async function handleCreateOrder(key: string, payload: OrderPayload) {
const existing = await redis.get(`idem:${key}`);
if (existing) {
const stored = JSON.parse(existing);
if (hash(payload) !== stored.payloadHash) {
return { status: 422, error: 'idempotency_conflict' };
}
return stored.response; // повтор — возвращаем оригинал
}
const response = await createOrderInternal(payload);
await redis.set(`idem:${key}`, JSON.stringify({
payloadHash: hash(payload),
response,
}), 'EX', 86400);
return response;
}
Race condition между двумя одновременными запросами с одним ключом решается через Redis SETNX + Postgres advisory lock на idempotency_key.
4. Сравнение dedup-storages
| Storage | Throughput | Latency | TTL | Стоимость |
|---|---|---|---|---|
| Redis (hot) | 200k/s | <1ms | 24h | $0.40/M |
| Postgres UNIQUE | 30k/s | 5–8ms | ∞ | $0.10/M |
| DynamoDB | 100k/s | 8–12ms | 24h | $1.25/M |
| Memcached | 500k/s | <1ms | 24h | $0.30/M |
FoxReload использует Redis-кластер из 6 нод, sharded по hash(idempotency_key). Это даёт <2ms p99 latency на dedup-проверке.
CTA
Idempotency-Key поддерживается на всех POST/PUT/DELETE-эндпоинтах FoxReload API. Полный референс — после онбординга, запросите доступ.
