Alloovium

Concepts fondamentaux

Idempotence

Attachez un header Idempotency-Key à toute requête mutante et Alloovium s'assurera qu'elle ne s'exécute jamais deux fois — même si votre client retente après une erreur réseau ou crashe en plein vol.

Pourquoi vous en avez besoin

Les réseaux mentent. Un client envoie path, la requête atteint le serveur et crée un projet, mais la réponse 201 est perdue en transit. Le client expire et retente. Sans clé d'idempotence, vous avez maintenant deux projets identiques.

Avec une clé d'idempotence, Alloovium met en cache la réponse originale pendant 24 heures en utilisant key. La deuxième requête rejoue la première réponse verbatim — statut, headers et corps — et n'exécute pas à nouveau le handler.

C'est le pattern Stripe

Si vous avez utilisé les clés d'idempotence de Stripe, celle d'Alloovium se comporte de manière identique : même fenêtre de 24 heures, mêmes sémantiques "corps différent ⇒ conflit", même verrouillage en cours.

Le header

http
POST /api/v2/vault/projects HTTP/1.1 Host: api.alloovium.com Authorization: Bearer ak_live_... Idempotency-Key: create-tower-2026-04-08 Content-Type: application/json {"name": "Downtown Tower", "project_type": "commercial"}

La clé doit correspondre au pattern pattern — 1 à 255 caractères de chars. Tout autre chose retourne error.

Choisissez des clés uniques à l'intention

Un UUID par opération logique est idéal. "create-tower-2026-04-08" convient pour un cron nocturne. Ne réutilisez PAS une seule clé statique pour des appels non liés — vous obtiendrez des réponses en cache pour la mauvaise opération.

Quelles méthodes sont couvertes

MéthodeLe middleware d'idempotence s'exécute-t-il ?
GETno — safe by design
HEADno — safe by design
OPTIONSno — safe by design
POSTyes
PATCHyes
PUTyes
DELETEyes

Les GETs ignorent entièrement le header. Si vous attachez header à un GET, l'API ne le mettra ni en cache ni ne le rejettera.

Comportement de replay

Quand une clé correspond à une réponse 2xx en cache, le serveur retourne la réponse originale complète et ajoute :

http
HTTP/1.1 201 Created Content-Type: application/json Idempotent-Replayed: true {"id": "8f2e3c1a-...", "name": "Downtown Tower", ...}

Le header header est le signal qu'aucun travail n'a été effectué. Utilisez-le pour l'observabilité — loggez-le, exposez-le dans les traces — mais ne vous branchez pas dessus. Le corps de réponse est exactement ce que le premier appel a retourné.

Seules les réponses 2xx sont mises en cache

Les réponses 4xx et 5xx contournent entièrement le cache. Cela signifie que si votre premier appel retourne code, corriger le corps et réessayer avec la même clé exécutera une nouvelle requête. Seuls les résultats réussis sont figés.

Corps non concordant

Si vous réutilisez une clé d'idempotence mais envoyez un corps différent, le serveur ne remplace pas l'original. À la place vous obtenez :

json
{ "type": "https://api.alloovium.com/errors/idempotency_key_reused", "title": "Idempotency key reused", "status": 422, "detail": "The Idempotency-Key was previously used with a different request body.", "code": "idempotency_key_reused" }

C'est un contrat fort : une fois qu'une clé est liée à un corps, cette liaison est immuable pour la fenêtre de 24 heures. Le serveur hache une forme canonique du corps à la première écriture et compare à chaque replay.

Collisions en cours

Si la requête originale est toujours en cours quand un retry arrive avec la même clé, le serveur retourne :

json
{ "type": "https://api.alloovium.com/errors/idempotency_in_progress", "title": "Idempotency key in progress", "status": 409, "detail": "A request with this Idempotency-Key is still in progress. Retry shortly.", "code": "idempotency_in_progress" }
http
HTTP/1.1 409 Conflict Retry-After: 5

Le verrou en cours a un TTL de 60 secondes — si le handler original meurt sans mettre en cache une réponse, le verrou expire et le prochain retry s'exécute pour de vrai. Respectez le header header ; un backoff de 5 secondes est généralement suffisant pour attraper le résultat en cache.

Limites de taille

L'idempotence nécessite de tamponner le corps de requête pour que le serveur puisse le hacher. Les corps au-dessus de limit contournent entièrement l'idempotence — ils s'exécutent normalement et n'écrivent jamais dans le cache, peu importe si vous avez attaché une clé.

Les uploads de fichiers n'ont pas d'idempotence

upload et fill envoient des corps multipart qui sont généralement bien plus grands que 64 Kio. Réessayer ceux-ci est de votre responsabilité — voir field sur le job d'ingestion pour une clé de déduplication naturelle.

Stockage et portée

DimensionComportement
BackendRedis, with SETNX + TTL for the in-progress lock
TTL24 hours from first write
Scope key(api_key_fingerprint, method, path, idempotency_key)
VisibilityEach API key has its own namespace — no cross-credential bleed
CleanupAutomatic via Redis expiry — no manual eviction needed

Une boucle de retry correcte

python
import time, uuid, httpx def create_project(client, name): idem_key = f"create-project-{uuid.uuid4()}" body = {"name": name, "project_type": "commercial"} for attempt in range(5): resp = client.post( "https://api.alloovium.com/api/v2/vault/projects", json=body, headers={ "Authorization": f"Bearer {API_KEY}", "Idempotency-Key": idem_key, }, ) # Success (fresh or replayed) — both are fine if resp.is_success: if resp.headers.get("Idempotent-Replayed") == "true": log.info("recovered from earlier success") return resp.json() # Original still in progress — wait and retry with the SAME key if resp.status_code == 409 and resp.json().get("code") == "idempotency_in_progress": time.sleep(int(resp.headers.get("Retry-After", "5"))) continue # Rate limited — honour Retry-After if resp.status_code == 429: time.sleep(int(resp.headers.get("Retry-After", "5"))) continue # 5xx transient — exponential backoff, same key if 500 <= resp.status_code < 600: time.sleep(min(30, 2 ** attempt)) continue # 4xx other than 429/409 — do not retry resp.raise_for_status() raise RuntimeError("max retries exhausted")

Même clé à chaque retry

L'erreur la plus courante : générer un nouveau UUID à chaque retry. Cela défait tout le mécanisme. Générez la clé une seule fois, en dehors de la boucle de retry, et gardez-la stable à travers chaque tentative pour la même opération logique.

Observabilité

Chaque requête obtient un header header dans la réponse — voir Erreurs. Quand un replay idempotent se produit, la réponse rejouée porte l'ID de requête originale, pas celle en cours. Si vous déboguez un scénario de double-soumission, loggez à la fois l'ID de requête entrant (depuis votre propre client) et le xheader retourné par le serveur — ils correspondront au replay et divergeront sur un nouvel appel.

Voir aussi

  • Limites de débit — stratégie de retry pour 429 et 5xx.
  • Erreurs — référence complète des codes d'erreur incluant les variantes codes.