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.send_batch, templates.send und broadcasts.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 idempotency_key, 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.
invoice_id = 'inv_4192' client.emails.send( {'from': sender, 'to': recipient, 'subject': subject, 'text': text}, idempotency_key=f'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.
| Aufruf | Wiederholt | Warum |
|---|---|---|
| Jeder Lesevorgang | Ja | Es ändert sich nichts. |
| emails.send, emails.send_batch, templates.send und broadcasts.send | Ja | Ein Idempotency-Key macht aus einer Wiederholung ein erneutes Abspielen. |
| emails.cancel, emails.reschedule, broadcasts.cancel, forms.publish, forms.pause, forms.resume, forms.approve_submission, account.accept_invitation und account.decline_invitation | Ja | Ein reines Setzen eines benannten Zustands. |
| threads.update, threads.trash und threads.restore | Ja | Ein Setzen von Labels. Zweimal anwenden ist dasselbe wie einmal anwenden. |
| threads.snooze, threads.unsnooze | Ja | Der Weckzeitpunkt steht im Body und wird nicht aus dem Eingangszeitpunkt abgeleitet. |
| labels.update, webhooks.update, settings.update, roles.update, members.update, domains.update, contacts.update, audiences.update, keys.update, emails.update, forms.update, branding.update, chats.rename, threads.update_note, domains.update_address, domains.update_address_forward, account.set_email_notification, account.set_push_muted, app_host.set und workspaces.set_active | Ja | Ein reines Setzen benannter Felder. |
| members.grant_address, members.grant_domain, rules.reorder und threads.reorder_notes | Ja | Die Vergabe ist ein Upsert, und die Reihenfolge wird vollständig angegeben. |
| templates.publish, imports.start | Ja | Das Veröffentlichen eines bereits veröffentlichten Head oder das Starten eines bereits gestarteten Imports gibt ihn unverändert zurück. |
| templates.preview, templates.render, broadcasts.preview und rules.test | Ja | Sie rendern, zählen oder werten aus und schreiben nichts. |
| domains.verify, app_host.verify | Ja | Eine wiederholte Prüfung ändert nichts außer dem Zeitpunkt der Prüfung. |
| contacts.save, contacts.set_audiences, contacts.remove_photo, contacts.block, contacts.unblock, contacts.delete_many, keys.revoke, account.remove_photo, branding.remove_image, domains.remove_logo, domains.remove_logo_certificate, domains.remove_address_photo, app_host.delete, files.revoke_link, files.revoke_all_links und subscriptions.move | Ja | Jeder gibt das Endergebnis an, ein zweiter Aufruf hinterlässt also, was der erste hinterlassen hat. |
| audiences.add_contact, audiences.add_contacts, audiences.remove_contacts, audiences.import_contacts, suppressions.add, domains.create_address und senders.research | Ja | Eine Wiederholung findet die Arbeit des ersten Aufrufs bereits erledigt vor und meldet sie, statt sie zweimal zu tun. |
| contacts.set_photo, imports.upload_chunk, account.set_photo, branding.upload_image, domains.set_logo, domains.set_logo_certificate und domains.set_address_photo | Ja | Erneut gesendete Bytes ersetzen, was der erste Versuch gespeichert hat. |
| drafts.create, labels.create, webhooks.create, templates.create, rules.create, roles.create, temp_mail.create, files.upload, templates.design und forms.design | Nein | Ein Wiederholungsversuch hinterlässt zwei Objekte. |
| drafts.update | Nein | Lesen 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.revoke_address, temp_mail.delete und temp_mail.delete_message | Nein | Ein Wiederholungsversuch nach einer verlorenen Antwort meldet einen Fehlschlag für Arbeit, die erfolgreich war. |
| webhooks.rotate_secret | Nein | Eine zweite Rotation macht das Secret ungültig, das der erste Versuch zurückgegeben hat. |
| webhooks.test | Nein | Sie würde eine zweite synthetische Zustellung senden. |
| webhooks.replay_delivery | Nein | Es würde das Event ein zweites Mal an Ihren Empfänger senden. |
| emails.translate | Nein | Sie verbraucht Modellaufrufe, daher bezahlt ein Wiederholungsversuch nach einer unbeantworteten Anfrage dieselbe Antwort zweimal. |
| emails.compose, emails.rewrite und emails.suggest_subject | Nein | Jeder Versuch verbraucht eine weitere KI-Aktion und kommt mit einer anderen Antwort zurück. |
| Jeder andere Aufruf | Nein | Einmal gesendet; ein Fehlschlag wird gemeldet statt wiederholt. |
forms.update wird auch mit expectedUpdatedAt wiederholt, ein erneuter Versuch nach einer verlorenen Antwort kann daher mit 409 version_conflict zurückkommen, weil der erste Versuch durchgegangen ist. Lesen Sie das Formular, bevor Sie es erneut versuchen.
client.raw.request wiederholt ein GET und sendet alles andere einmal, sofern Sie nicht repeatable=True übergeben.
Der Backoff
- Begrenzt durch
max_retriesauf dem Client, standardmäßig zwei zusätzliche Versuche. - Nur nach einem Netzwerkfehler oder einem
408,500,502,503oder504. Ein429wird nur wiederholt, wenn er einRetry-Aftermitführt, und diese API sendet keines, sodass ein Rate Limit sofort eine Ausnahme auslöst. Jeder andere Status löst umgehend eine Ausnahme aus. - Exponentiell von einer halben Sekunde bis zu acht, mit Jitter, damit sich eine Flotte bei der Wiederherstellung nicht neu synchronisiert.
- Getaktet durch
Retry-Afterin 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
retry_after_secondsausgelöst. Früher zurückzukommen als gefordert heißt, die Vorgabe nicht zu beachten. timeoutbegrenzt jeden einzelnen Versuch, ein Aufruf, der beide Wiederholungen ausschöpft, kann also drei Timeouts plus die Wartezeiten dazwischen dauern.- Ein abgebrochener
AsyncOpenEmail-Aufruf wird nie wiederholt. Der Abbruch wird sofort weitergereicht, aus der Anfrage oder aus der Wartezeit vor dem nächsten Versuch.