Idempotenzschlüssel in einer SMS-API: Duplikate bei Wiederholungen und Timeouts vermeiden
Ein Idempotenzschlüssel kann helfen, Wiederholungen einer HTTP-Anfrage zu kontrollieren – aber nur, wenn die API seinen Geltungsbereich und sein Verhalten definiert. Erfahre, was vor einem erneuten Sendeversuch geklärt werden sollte und wie sich die Annahme einer Anfrage vom Zustellstatus trennen lässt.

Welches Problem Idempotenz beim SMS-Versand löst
Ein Client kann eine HTTP-Anfrage senden und die Verbindung verlieren, bevor er eine Antwort erhält. In diesem Moment weiß er nicht unbedingt, ob der Server die Anfrage verarbeitet hat. Wiederholt er blind einen Versand, den die API als neuen Vorgang behandelt, kann dies zu einer zweiten Annahme und möglicherweise zu einer doppelten Nachricht führen.
Ein Idempotenzschlüssel ist ein Mechanismus, den eine API anbieten kann, um mehrere Anfragen als denselben Vorgang zu erkennen. Sein Nutzen hängt vom konkreten Vertrag dieser API ab: Der HTTP-Standard definiert nicht von sich aus einen Idempotenzschlüssel oder dessen Regeln für einen SMS-Sendeendpunkt.
- Ziel: verhindern, dass eine Wiederholung desselben Vorgangs als neue Anfrage behandelt wird.
- Einschränkung: Eine doppelte Annahme zu vermeiden, ist nicht gleichbedeutend mit der Garantie, dass die Nachricht nur einmal zugestellt wird.
- Prüfe vor der Implementierung von Wiederholungen in der API-Dokumentation, ob Schlüssel unterstützt werden, welchen Geltungsbereich sie haben und welche Antwort bei ihrer Wiederverwendung erfolgt.

Eine HTTP-Anfrage zu wiederholen bedeutet nicht immer, denselben Versand zu wiederholen
Die HTTP-Semantik unterscheidet zwischen idempotenten und nicht idempotenten Methoden. Gemäß RFC 9110 ist eine Operation idempotent, wenn mehrere identische Anfragen dieselbe beabsichtigte Wirkung auf dem Server haben wie eine einzelne Anfrage. Die Spezifikation nennt Methoden wie PUT und DELETE; POST wird nicht standardmäßig als idempotent eingestuft.
Daher sollte nicht angenommen werden, dass das erneute Senden eines SMS-Versand-POST sicher ist. Wird die Verbindung unterbrochen, bevor die Antwort eingeht, kann der Client nicht wissen, ob die Anfrage ausgeführt wurde. RFC 9110 empfiehlt, eine nicht idempotente Operation nicht automatisch zu wiederholen, es sei denn, ihre Semantik ist bekanntermaßen idempotent oder es lässt sich feststellen, dass die ursprüngliche Anfrage nicht angewendet wurde.
- Ein Timeout beschreibt, was der Client beobachtet hat, nicht unbedingt, was auf dem Server passiert ist.
- Eine erhaltene Ablehnung unterscheidet sich von einer verlorenen Antwort: Wenn die API geantwortet hat, entscheide anhand des Antwortcodes und des API-Vertrags über den nächsten Schritt.
- Wandle nicht jeden HTTP-Fehler, Verbindungsabbruch oder Timeout in eine automatische erneute Übermittlung um.

Einen stabilen Schlüssel pro Geschäftsvorgang erzeugen
Der Schlüssel sollte einen logischen Vorgang identifizieren, den das System bei allen Versuchen wiedererkennt, zum Beispiel eine mit einem konkreten internen Ereignis verknüpfte OTP-Anfrage. Wird bei jeder Wiederholung ein neuer Schlüssel erzeugt, hat die API keine gemeinsame Kennung, um die Anfragen einander zuzuordnen.
Der Schlüssel sollte nicht unnötige personenbezogene Daten oder Geheimnisse enthalten. Lege seine Erzeugung und Speicherung in deinem System fest und verwende ihn nicht für einen anderen Vorgang wieder. Die verfügbaren Quellen legen weder die genaue Erzeugungsmethode noch Format oder Entropie der Schlüssel fest: Diese Punkte müssen anhand der API-Dokumentation und der Sicherheitsanforderungen deiner Integration geklärt werden.
- Weise dem Geschäftsvorgang vor dem ersten HTTP-Versuch einen Schlüssel zu.
- Verwende für Wiederholungen desselben Vorgangs weiterhin denselben Schlüssel.
- Erzeuge für einen neuen Vorgang einen anderen Schlüssel, auch wenn Empfänger und Inhalt identisch sind.
- Nimm ohne Notwendigkeit keine Zugangsdaten, Tokens oder personenbezogenen Informationen auf.
Geltungsbereich und Dauer festlegen, bevor du dich auf den Schlüssel verlässt
Ein Schlüssel ist nur innerhalb des von der API festgelegten Geltungsbereichs eindeutig. Der Vertrag sollte klarstellen, ob er pro Konto, Endpunkt, Vorgang oder einer anderen Kombination ausgewertet wird und wie lange die Zuordnung zwischen Schlüssel und Anfrage gespeichert bleibt. Für SMS-APIs gibt es keine allgemein festgelegte Aufbewahrungsdauer.
Wiederholt der Client die Anfrage, nachdem die API den Schlüssel nicht mehr speichert, könnte der Server sie als neu behandeln. Das Aufbewahrungsfenster sollte daher den Zeitraum abdecken, in dem dein System eine verlorene Antwort möglicherweise noch wiederherstellen muss. Außerdem muss die Anwendung wissen, was nach Ablauf dieses Zeitraums zu tun ist.
- Bestätige den Geltungsbereich, innerhalb dessen ein Schlüssel eindeutig ist, und gehe nicht von einer globalen Eindeutigkeit aus.
- Dokumentiere das Aufbewahrungsfenster und das Verhalten nach dessen Ablauf.
- Stimme den Wiederholungszeitraum des Clients auf die vertraglich zugesicherte Aufbewahrungsdauer ab.
- Wenn die API diese Punkte nicht dokumentiert, hole eine Klärung ein, bevor du erneute Übermittlungen automatisierst.
Festlegen, was bei einer Wiederverwendung des Schlüssels mit anderem Payload geschieht
Wird ein Schlüssel mit anderen Daten wiederverwendet, ist das ein kritischer Fall. Der Vertrag sollte festlegen, wie die API Anfragen vergleicht und was geschieht, wenn derselbe Schlüssel mit einem inkompatiblen Payload eingeht. Eine vorsichtige Vorgehensweise ist, den Schlüssel in der Integration nicht zu verwenden, um Empfänger, Inhalt oder andere Felder zu ändern, die den Vorgang beeinflussen. Welche Antwort bei einem Konflikt erfolgt, muss der Dokumentation des Dienstes entnommen werden.
Es sollte außerdem geklärt werden, welches Ergebnis der Client erhält, wenn er exakt dieselbe Anfrage wiederholt. Manche APIs können ein der ersten Operation zugeordnetes Ergebnis zurückgeben; es gibt jedoch keine Grundlage für die Behauptung, dass alle APIs dies tun. Gehe ohne Bestätigung des Anbieters weder davon aus, dass die ursprüngliche Antwort erneut ausgegeben wird, noch von einem bestimmten Konfliktstatuscode.
- Lass den einer Schlüssel zugeordneten Payload während der Wiederholungen unverändert.
- Lege fest, welche Felder zur Identität des Vorgangs gehören.
- Prüfe das Verhalten, wenn ein Schlüssel mit einem anderen Payload wiederholt wird.
- Protokolliere die erhaltene Antwort, ohne sie als Zustellnachweis auf dem Endgerät zu interpretieren.
Timeouts und verlorene Antworten behandeln
Wenn keine Antwort eingeht, besteht der erste Schritt nicht einfach im erneuten Senden. Zunächst muss geklärt werden, ob die API ein sicheres Wiederholen der Anfrage mit demselben Schlüssel oder eine Abfrage des Vorgangs ermöglicht. Fehlen ein Idempotenzvertrag und eine Möglichkeit, das Ergebnis festzustellen, kann der Status unbestimmt bleiben; ein neuer POST könnte einen weiteren Vorgang anlegen.
Unterscheide in deiner Logik zwischen Fehlern, auf die eine Antwort eingegangen ist, und Fällen ohne Antwort. Wende bei Letzteren ausschließlich die von der API dokumentierten Möglichkeiten an. Behandle einen Timeout nicht als Beweis dafür, dass der Server die Anfrage nicht ausgeführt hat.
- Speichere den Schlüssel und die für die Zuordnung des Versuchs erforderlichen Daten vor dem Senden der Anfrage.
- Wiederhole eine Anfrage nach einer verlorenen Antwort nur dann mit demselben Schlüssel, wenn der Vertrag bestätigt, dass dies sicher ist.
- Wenn eine Abfrage des Vorgangs oder Status verfügbar ist, nutze sie, bevor du einen neuen Versand anlegst.
- Gibt es keinen dokumentierten Weg, die Unsicherheit zu beseitigen, vermeide blindes erneutes Senden und behandle den Fall als unbestimmt.
Gleichzeitige Anfragen und Persistenz kontrollieren
Zwei Prozesse können versuchen, denselben Vorgang gleichzeitig zu versenden, etwa wenn eine Warteschlange einen Auftrag erneut zustellt, während ein anderer Worker ihn noch verarbeitet. Die Integration sollte verhindern, dass lokale Parallelität für dasselbe Geschäftsereignis unterschiedliche Schlüssel erzeugt oder die Zuordnung zwischen Schlüssel und Payload verloren geht.
Die verfügbaren Informationen legen weder eine universelle Methode zur atomaren Speicherung noch einen konkreten Sperrmechanismus für eine SMS-API fest. Entwirf die Kontrolle auf Ebene deiner Anwendung und kläre, wie die API gleichzeitige Anfragen mit demselben Schlüssel behandelt. Gehe nicht davon aus, dass parallele Anfragen dedupliziert werden, sofern der Vertrag dies nicht ausdrücklich festlegt.
- Speichere den Schlüssel und die Identität des Vorgangs, bevor du den Versand auslöst.
- Sorge dafür, dass gleichzeitige Worker für denselben Vorgang denselben Schlüssel abrufen.
- Lege fest, welcher lokale Datensatz Vorrang hat, wenn zwei Prozesse den Vorgang gleichzeitig anzulegen versuchen.
- Teste gleichzeitige Anfragen und überprüfe das dokumentierte Verhalten des Endpunkts.
Die Annahme bestätigt nicht den endgültigen Nachrichtenstatus
Wenn die API ihn unterstützt, regelt ein Idempotenzschlüssel die Wiederholung einer Anfrage innerhalb eines festgelegten Vertrags. Er bestätigt nicht, dass die Nachricht das Endgerät erreicht hat, und ersetzt nicht die Statusverfolgung. Halte in Datenmodell und Berichten das Ergebnis der HTTP-Anfrage und spätere Informationen zur Nachricht getrennt.
Wenn die Anfrage angenommen wurde, der endgültige Status aber noch nicht bekannt ist, bewahre die von der API gelieferten Kennungen und Zuordnungsdaten auf und nutze die dokumentierten Abfragemöglichkeiten. Lege keinen neuen Versand an, nur weil sich der Status verzögert aktualisiert. Auch ein empfangener DLR sollte nicht als unabhängiger Nachweis des Empfangs auf dem Endgerät dargestellt werden, sofern eine solche Überprüfung nicht verfügbar ist.
- Protokolliere Idempotenzschlüssel, HTTP-Ergebnis und verfügbare Nachrichtenkennungen getrennt.
- Rufe Status über die von der API dokumentierten Funktionen ab oder gleiche sie damit ab.
- Verwechsle die Annahme, einen über die Übermittlungsroute gemeldeten Status und einen unabhängig bestätigten Empfang nicht miteinander.
- Versprich keine Ende-zu-Ende-Einmalzustellung allein auf Grundlage der Idempotenz der API.
Häufige Fragen
Garantiert ein Idempotenzschlüssel, dass die SMS nur einmal zugestellt wird?
Nein. Er kann helfen, zu verhindern, dass eine API denselben Vorgang mehr als einmal annimmt, sofern der Dienst einen entsprechenden Vertrag definiert und umsetzt. Er belegt weder den Empfang auf dem Endgerät noch garantiert er eine einmalige Zustellung von Ende zu Ende.
Sollte ich einen SMS-Versand bei einem Timeout wiederholen?
Nicht blind. Ein Timeout beweist nicht, dass der Server die Anfrage nicht verarbeitet hat. Wiederhole sie nur mit demselben Schlüssel, wenn die API dieses Verhalten dokumentiert, oder frage den Vorgang über einen dokumentierten Mechanismus ab.
Was passiert, wenn ich denselben Schlüssel mit einem anderen Payload verwende?
Das hängt vom API-Vertrag ab. Für SMS-APIs gibt es keine nachgewiesene allgemeingültige Regel. Lass den Payload für jeden Schlüssel unverändert und kläre, welche Antwort der Dienst bei einer inkompatiblen Anfrage zurückgibt.
Wie lange sollte ein Schlüssel gespeichert werden?
Das hängt von der API ab. Kläre ihr Aufbewahrungsfenster und stimme den Wiederholungszeitraum deines Clients darauf ab; gehe nicht von einer Standarddauer aus.
Bestätigt ein DLR, dass der Nutzer die Nachricht auf seinem Telefon erhalten hat?
Ein DLR sollte nicht automatisch als unabhängiger Nachweis des Empfangs auf dem Endgerät behandelt werden. Unterscheide zwischen einem gemeldeten DLR-Status und einer unabhängigen Bestätigung, sofern diese verfügbar ist.
Verwendete Quellen
- HTTP Semantics (RFC 9110)IETF
- SMPP Protocol Specification v3.4SMPP Developers Forum