Przejdź do dokumentacji
SDK

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ć.

idempotency.ts
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łaniePonawianeDlaczego
Każdy odczytTakNic się nie zmienia.
`emails.send`, `emails.sendBatch`, `templates.send`TakKlucz idempotencji zamienia powtórzenie w odtworzenie.
`emails.cancel`, `emails.reschedule`TakCzyste ustawienie nazwanego stanu.
`threads.update`, `threads.trash`TakUstawienie etykiet. Zastosowanie go dwa razy to to samo, co raz.
`threads.snooze`, `threads.unsnooze`TakMoment przebudzenia jest w treści żądania, a nie wyliczany z czasu jego dotarcia.
`labels.update`, `webhooks.update`, `settings.update`, `roles.update`, `members.update`TakCzyste ustawienie nazwanych pól.
`members.grantAddress`, `rules.reorder`TakPrzyznanie jest upsertem, a kolejność podawana jest w całości.
`templates.publish`TakOpublikowanie czołowej wersji, która już jest opublikowana, zwraca ją bez zmian.
`templates.preview`, `rules.test`TakRenderują albo oceniają i niczego nie zapisują.
`drafts.create`, `labels.create`, `webhooks.create`, `templates.create`, `rules.create`, `roles.create`, `tempMail.create`NiePonowienie zostawia dwa obiekty.
`drafts.update`NieOdczytuj 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`NiePonowienie po zgubionej odpowiedzi zgłasza niepowodzenie pracy, która się powiodła.
`webhooks.rotateSecret`NieDruga rotacja unieważnia sekret zwrócony przez pierwszą próbę.
`webhooks.test`NieWysłałoby to drugie syntetyczne dostarczenie.
`emails.translate`NieZużywa wywołania modelu, więc ponowienie po żądaniu bez odpowiedzi kupuje tę samą odpowiedź dwa razy.
Każdy inny zapisNieWysyłany raz, a niepowodzenie jest zgłaszane, a nie powtarzane.

Odczekiwanie między próbami

  • Ograniczone przez maxRetries na kliencie, domyślnie dwie dodatkowe próby.
  • Tylko po błędzie sieci albo po 408, 500, 502, 503 lub 504. 429 jest ponawiane wyłącznie wtedy, gdy niesie Retry-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-After w 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.
  • AbortSignal przekazany przez wywołującego nigdy nie jest ponawiany. Przerwanie natychmiast rzuca OpenEmailNetworkError — z żądania albo z oczekiwania przed kolejną próbą.