Ponawianie i idempotentność
Co jest ponawiane, czego celowo się nie ponawia i dlaczego ponowiona wysyłka nie może zdublować wiadomości.
Wysyłki
Klient dołącza Idempotency-Key do każdej wysyłki (emails.send, emails.sendBatch i templates.send), generowany raz na **wywołanie** i używany ponownie przez ponowienia tego wywołania. API rezerwuje ten klucz, zanim cokolwiek wyśle, więc ponowienie odtwarza pierwotną wiadomość, zamiast wysyłać drugą, podczas gdy dwa świadome wywołania send() nadal wysyłają dwa razy. To różne intencje i takie pozostają.
Przekaż własny idempotencyKey, aby rozciągnąć tę gwarancję na wiele procesów — dzięki temu zadanie, które padło i uruchomiło się ponownie, odtwarza swoje wysyłki, zamiast je powtarzać.
await openemail.emails.send(message, { idempotencyKey: `invoice:${invoice.id}` })Wyprowadź go z tego, co wymusiło wysyłkę. Nigdy z zegara. Ponowne użycie klucza z inną treścią jest odrzucane z idempotency_key_reuse, a nie po cichu odtwarzane.
Cała reszta
Każdy odczyt jest ponawiany. Zapis jest ponawiany tylko tam, gdzie drugie identyczne żądanie nie może znaczyć nic innego niż pierwsze — a wysyłka się kwalifikuje, bo jej klucz idempotencji zamienia powtórzenie w odtworzenie.
| Wywołanie | Ponawiane | Dlaczego |
|---|---|---|
| Każdy odczyt | Tak | Nic się nie zmienia. |
| `emails.send`, `emails.sendBatch`, `templates.send` | Tak | Klucz idempotencji zamienia powtórzenie w odtworzenie. |
| `emails.cancel`, `emails.reschedule` | Tak | Czyste ustawienie nazwanego stanu. |
| `threads.update`, `threads.trash` | Tak | Ustawienie etykiet. Zastosowanie go dwa razy to to samo, co raz. |
| `threads.snooze`, `threads.unsnooze` | Tak | Moment przebudzenia jest w treści żądania, a nie wyliczany z czasu jego dotarcia. |
| `labels.update`, `webhooks.update`, `settings.update`, `roles.update`, `members.update` | Tak | Czyste ustawienie nazwanych pól. |
| `members.grantAddress`, `rules.reorder` | Tak | Przyznanie jest upsertem, a kolejność podawana jest w całości. |
| `templates.publish` | Tak | Opublikowanie czołowej wersji, która już jest opublikowana, zwraca ją bez zmian. |
| `templates.preview`, `rules.test` | Tak | Renderują albo oceniają i niczego nie zapisują. |
| `drafts.create`, `labels.create`, `webhooks.create`, `templates.create`, `rules.create`, `roles.create`, `tempMail.create` | Nie | Ponowienie zostawia dwa obiekty. |
| `drafts.update` | Nie | Odczytuj id z wyniku każdego zapisu, zamiast używać ponownie tego, które wysłałeś. |
| `drafts.delete`, `labels.delete`, `webhooks.delete`, `templates.delete`, `rules.delete`, `roles.delete`, `members.remove`, `members.revokeAddress`, `tempMail.delete`, `tempMail.deleteMessage` | Nie | Ponowienie po zgubionej odpowiedzi zgłasza niepowodzenie pracy, która się powiodła. |
| `webhooks.rotateSecret` | Nie | Druga rotacja unieważnia sekret zwrócony przez pierwszą próbę. |
| `webhooks.test` | Nie | Wysłałoby to drugie syntetyczne dostarczenie. |
| `emails.translate` | Nie | Zużywa wywołania modelu, więc ponowienie po żądaniu bez odpowiedzi kupuje tę samą odpowiedź dwa razy. |
| Każdy inny zapis | Nie | Wysyłany raz, a niepowodzenie jest zgłaszane, a nie powtarzane. |
Odczekiwanie między próbami
- Ograniczone przez
maxRetriesna kliencie, domyślnie dwie dodatkowe próby. - Tylko po błędzie sieci albo po
408,500,502,503lub504.429jest ponawiane wyłącznie wtedy, gdy niesieRetry-After, a to API go nie wysyła, więc przekroczenie limitu rzuca wyjątek od razu. Każdy inny status rzuca natychmiast. - Wykładniczy, od pół sekundy do ośmiu, z jitterem, żeby flota nie zsynchronizowała się ponownie w momencie powrotu usługi.
- Tempo wyznacza
Retry-Afterw obu swoich postaciach: delay-seconds i HTTP-date. Gdy serwer poda czas oczekiwania, klient czeka dokładnie tyle, zamiast stosować własne odstępy. - Serwer proszący o więcej niż minutę jest traktowany tak, jakby kazał klientowi przestać, a nie zasnąć, więc błąd zostaje zgłoszony z polem
retryAfterSeconds. Powrót wcześniej, niż prosił, nie jest uszanowaniem tej prośby. AbortSignalprzekazany przez wywołującego nigdy nie jest ponawiany. Przerwanie natychmiast rzucaOpenEmailNetworkError— z żądania albo z oczekiwania przed kolejną próbą.