Zur Dokumentation springen
SDK

Wiederholungsversuche und Idempotenz

Was wiederholt wird, was bewusst nicht, und warum ein wiederholter Sendevorgang nichts doppelt verschicken kann.

Sendevorgänge

Der Client hängt an jeden Sendevorgang (emails.send, emails.sendBatch und templates.send) einen Idempotency-Key an, einmal pro **Aufruf** erzeugt und von den Wiederholungen dieses Aufrufs weiterverwendet. Die API belegt diesen Key, bevor sie irgendetwas ausliefert, eine Wiederholung spielt daher die ursprüngliche Nachricht erneut ab, statt eine zweite zu senden, während zwei bewusste send()-Aufrufe weiterhin zweimal senden. Das sind unterschiedliche Absichten, und sie bleiben unterschiedlich.

Übergeben Sie einen eigenen idempotencyKey, um diese Garantie über Prozessgrenzen hinweg auszudehnen, sodass ein Job, der abgestürzt und erneut gelaufen ist, seine Sendevorgänge erneut abspielt, statt sie zu wiederholen.

idempotency.ts
await openemail.emails.send(message, { idempotencyKey: `invoice:${invoice.id}` })

Leiten Sie ihn aus dem ab, was den Sendevorgang nötig gemacht hat. Nie aus einer Uhr. Ein Key, der mit einem anderen Body wiederverwendet wird, wird mit idempotency_key_reuse abgelehnt, statt stillschweigend erneut abgespielt zu werden.

Alles Übrige

Jeder Lesevorgang wird wiederholt. Ein Schreibvorgang wird nur dort wiederholt, wo ein zweiter identischer Request nichts anderes bedeuten kann als der erste, und ein Sendevorgang erfüllt das, weil sein Idempotency-Key aus einer Wiederholung ein erneutes Abspielen macht.

AufrufWiederholtWarum
Jeder LesevorgangJaEs ändert sich nichts.
`emails.send`, `emails.sendBatch`, `templates.send`JaEin Idempotency-Key macht aus einer Wiederholung ein erneutes Abspielen.
`emails.cancel`, `emails.reschedule`JaEin reines Setzen eines benannten Zustands.
`threads.update`, `threads.trash`JaEin Setzen von Labels. Zweimal anwenden ist dasselbe wie einmal anwenden.
`threads.snooze`, `threads.unsnooze`JaDer Weckzeitpunkt steht im Body und wird nicht aus dem Eingangszeitpunkt abgeleitet.
`labels.update`, `webhooks.update`, `settings.update`, `roles.update`, `members.update`JaEin reines Setzen benannter Felder.
`members.grantAddress`, `rules.reorder`JaDie Vergabe ist ein Upsert, und die Reihenfolge wird vollständig angegeben.
`templates.publish`JaDas Veröffentlichen eines bereits veröffentlichten Head gibt ihn unverändert zurück.
`templates.preview`, `rules.test`JaSie rendern oder werten aus und schreiben nichts.
`drafts.create`, `labels.create`, `webhooks.create`, `templates.create`, `rules.create`, `roles.create`, `tempMail.create`NeinEin Wiederholungsversuch hinterlässt zwei Objekte.
`drafts.update`NeinLesen Sie die ID aus dem Ergebnis jedes Schreibvorgangs, statt die gesendete erneut zu verwenden.
`drafts.delete`, `labels.delete`, `webhooks.delete`, `templates.delete`, `rules.delete`, `roles.delete`, `members.remove`, `members.revokeAddress`, `tempMail.delete`, `tempMail.deleteMessage`NeinEin Wiederholungsversuch nach einer verlorenen Antwort meldet einen Fehlschlag für Arbeit, die erfolgreich war.
`webhooks.rotateSecret`NeinEine zweite Rotation macht das Secret ungültig, das der erste Versuch zurückgegeben hat.
`webhooks.test`NeinSie würde eine zweite synthetische Zustellung senden.
`emails.translate`NeinSie verbraucht Modellaufrufe, daher bezahlt ein Wiederholungsversuch nach einer unbeantworteten Anfrage dieselbe Antwort zweimal.
Jeder andere SchreibvorgangNeinEinmal gesendet; ein Fehlschlag wird gemeldet statt wiederholt.

Der Backoff

  • Begrenzt durch maxRetries auf dem Client, standardmäßig zwei zusätzliche Versuche.
  • Nur nach einem Netzwerkfehler oder einem 408, 500, 502, 503 oder 504. Ein 429 wird nur wiederholt, wenn er ein Retry-After mitführt, und diese API sendet keines, sodass ein Rate Limit sofort einen Fehler auslöst. Jeder andere Status löst umgehend einen Fehler aus.
  • Exponentiell von einer halben Sekunde bis zu acht, mit Jitter, damit sich eine Flotte bei der Wiederherstellung nicht neu synchronisiert.
  • Getaktet durch Retry-After in beiden Formen, delay-seconds und HTTP-date. Nennt der Server eine Wartezeit, wartet der Client genau so lange, statt einen Backoff anzuwenden.
  • Fordert ein Server mehr als eine Minute, wird das als Aufforderung an den Client verstanden, aufzuhören, und nicht, zu schlafen; der Fehler wird daher mit retryAfterSeconds ausgelöst. Früher zurückzukommen als gefordert heißt, die Vorgabe nicht zu beachten.
  • Ein AbortSignal des Aufrufers wird nie wiederholt. Ein Abbruch löst sofort einen OpenEmailNetworkError aus, aus der Anfrage oder aus der Wartezeit vor dem nächsten Versuch.