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
Le header
httpPOST /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
Quelles méthodes sont couvertes
| Méthode | Le middleware d'idempotence s'exécute-t-il ? |
|---|---|
| GET | no — safe by design |
| HEAD | no — safe by design |
| OPTIONS | no — safe by design |
| POST | yes |
| PATCH | yes |
| PUT | yes |
| DELETE | yes |
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 :
httpHTTP/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
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" }
httpHTTP/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
| Dimension | Comportement |
|---|---|
| Backend | Redis, with SETNX + TTL for the in-progress lock |
| TTL | 24 hours from first write |
| Scope key | (api_key_fingerprint, method, path, idempotency_key) |
| Visibility | Each API key has its own namespace — no cross-credential bleed |
| Cleanup | Automatic via Redis expiry — no manual eviction needed |
Une boucle de retry correcte
pythonimport 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
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.