Sofortantwort
Idempotenz einfach erklärt
Eine Operation liefert dasselbe Ergebnis, egal wie oft sie ausgeführt wird.
- Kurz gesagt
- Idempotente Operationen können sicher wiederholt werden – bei Netzwerkfehlern kein Problem
- Typischer Einsatz
- Zahlungsabwicklung, Webhook-Verarbeitung, Retry-Logik
- Wichtig zu wissen
- Idempotency Keys ermöglichen idempotente POST-Anfragen
Idempotenz im Überblick
In verteilten Systemen passieren Netzwerkfehler. Eine Anfrage wird gesendet, aber die Antwort kommt nie an – war die Anfrage erfolgreich oder nicht? Die sichere Lösung: einfach nochmal senden. Aber nur, wenn die Operation idempotent ist.
Nicht idempotent: POST /orders – zweimal gesendet = zwei Bestellungen
Idempotent: PUT /orders/123/status mit {"status": "shipped"} – zehnmal gesendet = Status ist „shipped”
Das ist der Kern: Idempotente Operationen können sicher wiederholt werden, ohne unerwünschte Nebeneffekte.
At-least-once vs. Exactly-once
In verteilten Systemen gibt es drei Delivery-Garantien:
| Garantie | Bedeutung | Risiko |
|---|---|---|
| At-most-once | Höchstens einmal geliefert | Datenverlust möglich |
| At-least-once | Mindestens einmal geliefert | Duplikate möglich |
| Exactly-once | Genau einmal geliefert | Sehr aufwändig, selten wirklich garantiert |
At-least-once + Idempotenz = sicheres System: Wenn Duplikate sicher ignoriert werden, ist At-least-once ausreichend und viel einfacher als Exactly-once.
Technisch betrachtet
HTTP-Methoden im Überblick
| Methode | Idempotent | Sicher | Typische Verwendung |
|---|---|---|---|
| GET | ✅ | ✅ | Daten lesen |
| PUT | ✅ | ❌ | Ressource ersetzen |
| DELETE | ✅ | ❌ | Ressource löschen |
| POST | ❌ | ❌ | Ressource erstellen |
| PATCH | ⚠️ | ❌ | Teilweise aktualisieren |
Idempotency Key implementieren
import json
import redis
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
cache = redis.Redis()
@app.post("/payments")
async def create_payment(
payment: PaymentRequest,
idempotency_key: str = Header(..., alias="Idempotency-Key")
):
cache_key = f"idem:{idempotency_key}"
# Bereits verarbeitet? → Gespeichertes Ergebnis zurückgeben
cached = cache.get(cache_key)
if cached:
return json.loads(cached)
# Noch nicht verarbeitet → Zahlung durchführen
try:
result = await process_payment(payment)
except Exception as e:
# Fehler NICHT cachen – Retry soll erneut versuchen
raise HTTPException(status_code=500, detail=str(e))
# Ergebnis 24h cachen (nur bei Erfolg)
cache.setex(cache_key, 86400, json.dumps(result))
return result
Idempotente Datenbankoperationen
-- ❌ Nicht idempotent: Jedes Mal wird ein neuer Datensatz erstellt
INSERT INTO orders (user_id, product_id, amount)
VALUES (123, 456, 99.99);
-- ✅ Idempotent: Nur einfügen wenn noch nicht vorhanden
INSERT INTO orders (id, user_id, product_id, amount)
VALUES ('ord_abc123', 123, 456, 99.99)
ON CONFLICT (id) DO NOTHING;
-- ✅ Idempotent: Upsert – einfügen oder aktualisieren
INSERT INTO order_status (order_id, status, updated_at)
VALUES ('ord_abc123', 'shipped', NOW())
ON CONFLICT (order_id) DO UPDATE SET
status = EXCLUDED.status,
updated_at = EXCLUDED.updated_at;
Idempotente Webhook-Verarbeitung
processed_events = set() # In Production: Redis oder DB
@app.post("/webhooks/stripe")
async def handle_stripe_webhook(request: Request):
payload = await request.body()
sig = request.headers.get("Stripe-Signature")
# Signatur verifizieren
event = stripe.Webhook.construct_event(payload, sig, WEBHOOK_SECRET)
# Bereits verarbeitet?
if event.id in processed_events:
return {"status": "already_processed"}
# Verarbeiten
if event.type == "payment_intent.succeeded":
await fulfill_order(event.data.object)
processed_events.add(event.id)
return {"status": "ok"}
Stripe-Beispiel
curl https://api.stripe.com/v1/charges \
-H "Idempotency-Key: a8f3b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c" \
-d amount=2000 \
-d currency=eur
# Zweiter Aufruf mit gleichem Key → gleiche Antwort, keine zweite Zahlung
Wann ist Idempotenz besonders wichtig?
- Zahlungen: Doppelte Abbuchungen sind kritisch
- E-Mail-Versand: Doppelte Willkommens-Mails sind ärgerlich
- Lagerbestand: Doppeltes Reduzieren führt zu negativem Bestand
- Webhook-Handler: Webhooks werden oft mehrfach gesendet (Retry-Logik des Senders)
- Message Queues: Kafka, RabbitMQ liefern At-least-once – Consumer müssen idempotent sein
Schritt für Schritt
Wie idempotente Abläufe zuverlässig entstehen
Wirkung einer Anfrage definieren
Beschreibe, welchen Zustand oder welches Ergebnis eine Operation herstellen soll. Entscheidend ist nicht, ob eine Anfrage technisch zweimal ankommt, sondern ob ihre reale Wirkung bei Wiederholung gleich bleibt.
Eindeutige Kennung und Zustand wählen
Nutze eine stabile Kennung oder einen Zielzustand, um Wiederholungen zu erkennen. Speichere ausreichend Kontext, damit das System eine erneute Anfrage sicher von einem neuen, ähnlichen Vorgang unterscheiden kann.
Nebenwirkungen absichern
Behandle Zahlungen, Nachrichten, externe Aufrufe und Statuswechsel besonders sorgfältig. Transaktionen, Outbox-Muster oder kontrollierte Reihenfolgen können verhindern, dass ein Retry dieselbe Wirkung zweimal auslöst.
Störungen und Parallelität testen
Simuliere Timeouts, doppelte Zustellung und gleichzeitige Anfragen. Beobachte, ob das System den richtigen Zustand bewahrt, verständlich antwortet und bei unklaren Fällen sicher an eine Klärung übergibt.
Konkretes Beispiel
Eine Bestellung bleibt bei Retry einmalig
Eine Kundin löst eine Bestellung aus, doch die Antwort erreicht ihren Browser nicht. Sie versucht es erneut, während die erste Anfrage möglicherweise bereits verarbeitet wurde.
Unsicherer Vorgang
Der Server kann nicht erkennen, ob die zweite Anfrage eine Wiederholung oder eine neue Bestellung ist. Ohne Schutz könnten Zahlung, Auftrag oder Bestätigung doppelt entstehen.
Eindeutige Wirkung
Beide Anfragen tragen dieselbe Idempotenzkennung. Das System liefert bei Wiederholung den ursprünglichen Status zurück, statt eine zweite Bestellung anzulegen; unklare Fälle werden protokolliert und geprüft.
Einordnung
Vorteile und Grenzen
Das spricht dafür
- Macht Wiederholungen bei Timeouts und verteilter Zustellung sicherer und besser nachvollziehbar.
- Reduziert doppelte Nebenwirkungen bei wichtigen Statuswechseln und externen Aufrufen.
- Unterstützt robuste Retries und klarere Fehlerbehandlung in verteilten Systemen.
Das solltest du beachten
- Die richtige Kennung und Lebensdauer hängen stark von Fachlogik und möglicher Wiederholung ab.
- Idempotenz verhindert nicht jede Race Condition oder fachliche Konfliktsituation.
- Zusätzlicher Zustand und Speicherdauer bringen Datenschutz-, Kosten- und Pflegefragen mit sich.
Vertiefung · für FortgeschritteneWiederholung ohne doppelte Wirkung
Gleicher Zustand bei Retry
- API
- Schnittstelle zur Kommunikation zwischen Softwaresystemen.
- Webhook
- Ein Server sendet automatisch HTTP-Anfragen bei Ereignissen.
- Message Queue
- Warteschlange für asynchrone Service-Kommunikation.
- Error Recovery Patterns
- UX-Patterns für graceful Fehlerbehandlung in KI-Systemen.
- Event Sourcing
- Zustand als Sequenz von Events speichern und bei Bedarf rekonstruieren.