문서로 건너뛰기
SDK

재시도와 멱등성

무엇이 재시도되고, 무엇이 의도적으로 재시도되지 않으며, 재시도된 발송이 왜 중복될 수 없는지.

발송

클라이언트는 모든 발송(emails.send, emails.sendBatch, templates.send)에 Idempotency-Key를 붙이며, 이 키는 **호출**마다 한 번 생성되어 그 호출의 재시도에서 재사용됩니다. API는 무엇이든 발송하기 전에 그 키를 선점하므로, 재시도는 두 번째 메시지를 보내는 대신 원래 메시지를 그대로 재생합니다. 반면 의도적으로 send()를 두 번 호출하면 두 번 발송됩니다. 둘은 서로 다른 의도이며, 앞으로도 다르게 남습니다.

직접 만든 idempotencyKey를 전달하면 그 보장을 프로세스 경계 너머까지 확장할 수 있습니다. 그러면 중간에 죽었다가 다시 실행된 작업이 발송을 반복하지 않고 재생합니다.

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

발송이 필요해진 원인에서 키를 만드십시오. 절대 시계에서 만들지 마십시오. 같은 키를 다른 본문과 함께 재사용하면 조용히 재생되지 않고 idempotency_key_reuse로 거부됩니다.

그 밖의 모든 것

모든 읽기는 재시도됩니다. 쓰기는 두 번째 동일 요청이 첫 번째와 다른 의미를 가질 수 없는 경우에만 재시도되며, 발송은 멱등성 키가 반복을 재생으로 바꾸기 때문에 여기에 해당합니다.

호출재시도 여부이유
모든 읽기바뀌는 것이 없습니다.
`emails.send`, `emails.sendBatch`, `templates.send`멱등성 키가 반복을 재생으로 바꿉니다.
`emails.cancel`, `emails.reschedule`명시된 상태를 그대로 설정하는 순수한 작업입니다.
`threads.update`, `threads.trash`레이블 설정입니다. 두 번 적용해도 한 번 적용한 것과 같습니다.
`threads.snooze`, `threads.unsnooze`깨어날 시각이 본문에 담겨 있으며, 도착 시각에서 계산되지 않습니다.
`labels.update`, `webhooks.update`, `settings.update`, `roles.update`, `members.update`명시된 필드를 그대로 설정하는 순수한 작업입니다.
`members.grantAddress`, `rules.reorder`권한 부여는 upsert이며, 순서는 전체가 명시됩니다.
`templates.publish`이미 게시된 head를 다시 게시하면 그대로 반환됩니다.
`templates.preview`, `rules.test`렌더링하거나 평가할 뿐, 아무것도 기록하지 않습니다.
`drafts.create`, `labels.create`, `webhooks.create`, `templates.create`, `rules.create`, `roles.create`, `tempMail.create`아니요재시도하면 객체가 두 개 남습니다.
`drafts.update`아니요보낸 id를 재사용하지 말고, 매 쓰기의 결과에서 id를 읽으십시오.
`drafts.delete`, `labels.delete`, `webhooks.delete`, `templates.delete`, `rules.delete`, `roles.delete`, `members.remove`, `members.revokeAddress`, `tempMail.delete`, `tempMail.deleteMessage`아니요응답을 잃어버린 뒤 재시도하면 이미 성공한 작업을 실패로 보고하게 됩니다.
`webhooks.rotateSecret`아니요두 번째 회전은 첫 번째 시도가 돌려준 시크릿을 무효화합니다.
`webhooks.test`아니요두 번째 테스트 전달이 발송됩니다.
`emails.translate`아니요모델 호출 비용이 들므로, 응답 없는 요청을 재시도하면 같은 답을 두 번 사는 셈이 됩니다.
그 밖의 모든 쓰기아니요한 번만 전송되며, 실패는 반복되지 않고 보고됩니다.

백오프

  • 클라이언트의 maxRetries로 제한되며, 기본값은 두 번의 추가 시도입니다.
  • 네트워크 실패나 408, 500, 502, 503, 504에서만 재시도합니다. 429Retry-After가 함께 올 때만 재시도하는데 이 API는 그 헤더를 보내지 않으므로, 요청 제한은 곧바로 예외를 던집니다. 그 밖의 상태 코드도 즉시 예외를 던집니다.
  • 0.5초에서 8초까지 지수적으로 늘어나며 지터가 적용되므로, 여러 대가 복구 시점에 다시 동기화되지 않습니다.
  • Retry-After가 delay-seconds든 HTTP-date든 그 값에 맞춰 대기합니다. 서버가 대기 시간을 지정하면 클라이언트는 백오프 대신 정확히 그만큼 기다립니다.
  • 서버가 1분보다 긴 대기를 요구하면 잠시 쉬라는 뜻이 아니라 멈추라는 뜻으로 해석하여, retryAfterSeconds를 담은 오류를 던집니다. 요청받은 것보다 일찍 돌아오는 것은 그 지시를 따르는 것이 아니기 때문입니다.
  • 호출자의 AbortSignal은 절대 재시도하지 않습니다. 중단되면 요청 중이든 다음 시도 전 대기 중이든 즉시 OpenEmailNetworkError를 던집니다.