Ein Timeout bedeutet nicht, dass eine Operation fehlgeschlagen ist: Es bestätigt lediglich, dass der Client innerhalb der Frist keine Antwort erhalten hat. Der Server kann die Bestellung bereits erstellt haben, der Zahlungsanbieter kann die Belastung akzeptiert haben oder ein asynchroner Prozess kann weiterhin ausgeführt werden. Wenn der Client unkontrolliert erneut versucht, kann dieselbe Geschäftsabsicht doppelte Auswirkungen erzeugen.
Die Idempotenz in PHP wandelt eine technische Wiederholung in eine Abfrage oder in die Rückgabe des bereits erzielten Ergebnisses um. Sie besteht nicht darin, alle Duplikate zu ignorieren oder sich allein darauf zu verlassen, dass der Benutzer nicht zweimal klickt. Sie ist ein expliziter Vertrag zwischen Client, API, Persistenz und, sofern zutreffend, externen Systemen.
Das Problem: Die Antwort geht verloren, aber die Wirkung bleibt bestehen

Betrachten Sie einen Endpoint, der einen Kauf bestätigt. Die Anwendung validiert die Anfrage, erfasst die Bestellung, fordert die Belastung an und bereitet eine Antwort vor. Die Verbindung wird genau unterbrochen, bevor der Client sie erhält. Beim erneuten Absenden desselben Formulars kann der Endpoint nicht anhand des Inhalts ableiten, dass es sich um denselben Kauf handelt: Zwei Bestellungen mit denselben Produkten können gültige, unterschiedliche Absichten sein.
Das Problem tritt auch bei der Anlage von Benutzern, der Zuweisung von Guthaben, der Ausstellung von Dokumenten, Synchronisierungen, Webhooks und administrativen Aktionen auf. Drei Elemente sollten getrennt betrachtet werden:
- Geschäftsabsicht: „Ich möchte diesen konkreten Kauf bestätigen“.
- Technische Anfrage: eine HTTP-Übermittlung mit Headern, Body und Authentifizierungskontext.
- Ausführungsversuch: jede interne Verarbeitung, jeder erneute Queue-Versuch oder jeder Aufruf eines Anbieters.
Der Idempotenzschlüssel identifiziert die Absicht, nicht eine HTTP-Verbindung oder jeden Serverversuch. Deshalb muss er Netzwerk-Wiederholungsversuche und, wenn der Ablauf es erfordert, Prozessneustarts überdauern.
Welche Operationen Idempotenz benötigen und welche nicht
Priorisieren Sie Operationen, die eine Ressource mit relevanten Folgen erstellen, bestätigen, abrechnen, versenden, reservieren, benachrichtigen oder ändern. Ein POST /payments, die Bestätigung einer Bestellung oder der Empfang eines Webhooks sind eindeutige Kandidaten. Dasselbe gilt für einen Queue-Job, der mehr als einmal zugestellt werden kann.
Ein reiner Lesezugriff benötigt normalerweise keinen Idempotenzschlüssel. Ein Update kann eine andere Semantik haben: Einen gewünschten Zustand festzulegen, etwa mit PUT /profiles/42, kann von vornherein idempotent sein, wenn dieselbe Repräsentation die Ressource unverändert lässt. Dagegen wird eine Aktion wie „Guthaben erhöhen“ nicht allein durch die Verwendung eines bestimmten Verbs idempotent.
Ein Schlüssel darf auch nicht als Ersatz für andere Regeln verwendet werden. Um zwei kompatible Reservierungen in einem begrenzten Bestand zu verhindern, werden Domäneninvarianten, Parallelitätskontrolle und eine Reservierungsrichtlinie benötigt. Um eine Aufgabe in einer verteilten Umgebung nur einmal auszuführen, erfolgt die tatsächliche Zustellung häufig mindestens einmal; der Consumer muss Duplikate tolerieren.
Gestaltung des Schlüssels und des persistenten Datensatzes
Der Client sollte einen undurchsichtigen und ausreichend unvorhersehbaren Schlüssel erzeugen, wenn die Geschäftsabsicht entsteht, ihn aufbewahren, solange er Wiederholungsversuche durchführen kann, und ihn beispielsweise in Idempotency-Key senden. Wenn der Server ihn bei jedem Empfang erzeugt, kann er eine spätere Wiederholung nicht zuordnen. In internen Abläufen kann der Schlüssel aus einer stabilen Kennung des Geschäftsereignisses abgeleitet werden.
Sein Geltungsbereich muss den Akteur oder Tenant und die Operation umfassen. Dieselbe Zeichenfolge sollte weder zwischen zwei Konten noch zwischen „Bestellung erstellen“ und „Rückerstattung ausstellen“ kollidieren. Definieren Sie eine Aufbewahrungsdauer, die am tatsächlichen Zeitraum für Wiederholungsversuche und an den Risiken der Domäne ausgerichtet ist. Wird der Datensatz zu früh gelöscht, öffnet dies erneut die Tür für Duplikate; eine unbegrenzte Aufbewahrung erhöht die Kosten und erfordert eine Richtlinie für Datenschutz und Löschung.
Ein minimales Persistenzmodell umfasst:
- Sicherheits- oder Tenant-Geltungsbereich, Operationsname und Idempotenzschlüssel;
- kryptografischen Fingerabdruck einer normalisierten Nutzlast;
- Status:
processing,completed,failedoderpending, wenn die externe Bestätigung unsicher ist; - Code und Response-Body, die wiederholbar zurückgegeben werden;
- Kennungen der erstellten Ressource, interne Korrelation und Referenz des externen Anbieters;
- Erstellungs-, Aktualisierungs- und Ablaufzeitpunkte.
Der Fingerabdruck verhindert einen wichtigen Fehler: denselben Schlüssel mit unterschiedlichen Daten wiederzuverwenden. Antworten Sie in dieser Situation mit einem Konflikt und verarbeiten Sie die neue Nutzlast nicht. Damit der Vergleich zuverlässig ist, normalisieren Sie Felder, deren Reihenfolge keine Bedeutung hat, und schließen Sie veränderliche Metadaten aus, die nicht Teil der Absicht sind.
PHP-Ablauf: Reservieren, bevor die Wirkung erzeugt wird
Der Schutz muss durch eine eindeutige Datenbankeinschränkung für Geltungsbereich, Operation und Schlüssel abgesichert sein. Zuerst abzufragen und anschließend einzufügen reicht nicht aus: Zwei gleichzeitige Anfragen können das Fehlen des Datensatzes feststellen und gleichzeitig fortfahren.
Der empfohlene Ablauf besteht darin, atomar zu reservieren. Wenn das Einfügen erfolgreich ist, ist dieser Prozess der anfängliche Eigentümer der Ausführung. Bei einem Eindeutigkeitskonflikt wird der vorhandene Datensatz gelesen, der Fingerabdruck überprüft und entsprechend seinem Status gehandelt. Ein abgeschlossenes Ergebnis gibt exakt die persistierte Antwort zurück; eine laufende Operation kann einen ausstehenden Status zurückgeben oder nur eine begrenzte Zeit warten, bevor sie erneut abfragt.
begin transaction
insert idempotency_records(scope, operation, key, payload_hash, status)
values (?, 'create_order', ?, ?, 'processing')
-- die eindeutige Einschränkung bestimmt den Eigentümer
commit
if reservation_was_created:
result = execute_business_operation()
persist_completed_response(result)
else:
record = load_existing_record()
assert_same_payload_hash(record)
return replay_or_pending(record)Halten Sie während eines langsamen Aufrufs an einen Anbieter weder eine Transaktion noch eine Zeilensperre offen. Das reduziert die Kapazität und kann lang andauernde Sperren erzeugen. Reservieren Sie stattdessen und bestätigen Sie den lokalen Status in kurzen Transaktionen. Wenn die externe Wirkung und der lokale Datensatz koordiniert werden müssen, speichern Sie zusätzlich einen Sendeauftrag in einer transaktionalen Tabelle und verarbeiten Sie ihn getrennt. Dieses Muster beseitigt Wiederholungsversuche nicht, ermöglicht aber die Wiederherstellung ausstehender Arbeit, ohne die erfasste Absicht zu verlieren.
Parallelität, Timeouts und unsichere Zustände
Zwei Anfragen mit demselben Schlüssel können im Abstand von Millisekunden eintreffen. Die eindeutige Einschränkung legt fest, welche die Operation reserviert. Die zweite darf keine weitere externe Wirkung auslösen. Sie kann 202 antworten, solange der Status processing oder pending ist, einschließlich einer Kennung zur Abfrage des Ergebnisses; wenn der Vertrag eine synchrone Antwort verlangt, kann sie begrenzt warten und den Datensatz erneut lesen.
Ein Fehler vor dem Start irgendeiner Wirkung ermöglicht es, failed mit einem reproduzierbaren Fehler zu markieren. Ein Timeout beim Aufruf eines externen Systems erzeugt jedoch Unsicherheit: Es ist nicht korrekt, automatisch als fehlgeschlagen zu markieren oder einen Auftrag einfach erneut zu senden. Speichern Sie, sofern vorhanden, die Referenz der gesendeten Anfrage, fragen Sie den Anbieter über diese Referenz ab und gleichen Sie das Ergebnis ab. Solange keine Bestätigung vorliegt, behalten Sie pending bei und teilen mit, dass das Ergebnis noch nicht endgültig ist.
Auch der externe Aufruf benötigt eine stabile Referenz. Wenn der Anbieter einen eigenen Idempotenzschlüssel unterstützt, übergeben Sie einen Schlüssel, der derselben Absicht zugeordnet ist. Wenn er ihn nicht unterstützt, verwenden Sie Händlerkennungen, nachträgliche Abfragen, regelmäßigen Abgleich und Betriebsverfahren für mehrdeutige Fälle. Keine lokale Transaktion kann einen Datenbankschreibvorgang und eine unabhängige Remote-API atomar machen.
Was ein Idempotenzschlüssel nicht löst
Idempotenz verhindert die Wiederholung einer erkannten Absicht; sie entscheidet nicht, wie eine irreversible Wirkung rückgängig gemacht wird. Ein physischer Versand, eine bereits abgewickelte Überweisung oder eine von einem Benutzer gesehene Benachrichtigung können Kompensation, Stornierung oder manuelle Bearbeitung erfordern. Gestalten Sie diese Aktionen als explizite Geschäftsprozesse mit Berechtigungen, Zuständen und Auditierung.
Verwechseln Sie auch eine Korrektur nicht mit einem Wiederholungsversuch. Wenn der Benutzer nach einem Fehler Adresse, Betrag oder Produkte ändert, liegt eine neue Absicht vor und es muss ein neuer Schlüssel verwendet werden. Die Wiederverwendung des vorherigen Schlüssels mit einer anderen Nutzlast muss einen Konflikt erzeugen, nicht die ursprüngliche Operation stillschweigend aktualisieren.
Tests, Beobachtbarkeit und Checkliste

Testen Sie mehr als nur den Erfolgsfall. Unterbrechen Sie die Antwort nach der Persistierung des Ergebnisses, wiederholen Sie denselben Schlüssel parallel, starten Sie einen Worker nach der Reservierung des Datensatzes neu und simulieren Sie ein Timeout nach dem Senden einer externen Anfrage. Überprüfen Sie, dass nur eine Geschäftsressource existiert, dass die wiederholte Antwort dasselbe Ergebnis beibehält und dass eine andere Nutzlast mit demselben Schlüssel nicht akzeptiert wird.
Protokollieren Sie, ohne sensible Daten offenzulegen, den Schlüssel oder eine daraus abgeleitete sichere Kennung, den Geltungsbereich, den Status, die Korrelation und die externe Referenz. Metriken zu Schlüsselkonflikten, zu zu lange ausstehenden Operationen und zu ungelösten Abgleichen helfen Support und Betrieb dabei, einen normalen Wiederholungsversuch von einem Vorfall zu unterscheiden.
- Repräsentiert der Schlüssel eine Geschäftsabsicht und hat er einen definierten Geltungsbereich?
- Gibt es eine eindeutige Einschränkung, die zwei gleichzeitige Reservierungen verhindert?
- Wird ein Fingerabdruck der Nutzlast verglichen und werden Änderungen der Absicht abgelehnt?
- Wird eine Antwort oder ein Ergebnis persistiert, das konsistent wiederholt werden kann?
- Ermöglichen unsichere Zustände Abfrage und Abgleich, bevor erneut versucht wird?
- Verfügt jede externe Wirkung über Referenz, Wiederherstellung und eine betriebliche Alternative?
- Wurden Duplikate, Ausfälle, Queue-Wiederholungsversuche und echte Parallelität getestet?
So angewendet verspricht Idempotenz nicht, dass ein Netzwerk zuverlässig ist. Sie sorgt dafür, dass unvermeidliche Fehler ein kontrollierbares, nachvollziehbares und für das Geschäft konsistentes Ergebnis haben.



