You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
원자적 저장: 발송 작업·수신자 대상·실행 큐 레코드를 하나의 DB 트랜잭션으로 저장합니다. 외부 SES 호출은 커밋 이후 워커가 수행합니다.
대상 단위 claim: 대상 상태를 PENDING → PROCESSING → SENT/FAILED로 관리하고, attempt_count, next_attempt_at, lease_until, 마지막 안전 오류 코드를 저장합니다. lease가 만료된 PROCESSING은 재기동 시 회수합니다.
재시도 정책: 네트워크 오류·SES 5xx·throttling만 지수 backoff와 최대 횟수까지 재시도합니다. 주소 형식·수신 거부·4xx 영구 오류는 즉시 FAILED로 종료합니다.
멱등성: requestedByAdminId + Idempotency-Key를 unique로 유지하고, canonical JSON fingerprint가 다르면 충돌 오류를 반환합니다. 비교 대상은 scenarioId, recruitId, applyIds, 제목 덮어쓰기, 입력 변수입니다.
동시성: 워커 claim은 DB 잠금 또는 원자적 상태 변경으로 한 대상만 획득하게 합니다. 재시도·복구가 같은 대상을 동시에 처리하지 않는지 검증합니다.
관찰성: 작업·대상 ID, 시도 횟수, 결과 코드만 로그에 남기고 이메일 주소·SES 원문 오류·요청 본문은 남기지 않습니다. claim, retry, exhausted, recovery 지표를 추가합니다.
장애 테스트: 저장 직후 중단, SES 성공 후 결과 기록 전 중단, DB 기록 실패, lease 만료 복구, 동시 워커, 재시도 한도 초과를 테스트합니다.
별도 결정 필요
외부 SES 성공 직후 DB 기록 전에 프로세스가 중단되면, SES 자체 멱등성 키가 없어 외부 발송의 정확히 한 번 처리를 보장할 수 없습니다. 따라서 구현 PR 시작 전 at-least-once + 중복 완화를 허용할지, SES 호출 경계에 별도 중복 방지 수단을 둘지 결정해야 합니다.
이번 단계에서는 설계 기준만 정리하고 Outbox·워커·재시도 코드는 별도 PR로 구현합니다.
Activity
023-dev commentedon Sep 9, 2026
후속 구현 설계 기준
#670 구현 전 다음 경계를 기준으로 확정합니다.
PENDING → PROCESSING → SENT/FAILED로 관리하고,attempt_count,next_attempt_at,lease_until, 마지막 안전 오류 코드를 저장합니다. lease가 만료된PROCESSING은 재기동 시 회수합니다.FAILED로 종료합니다.requestedByAdminId + Idempotency-Key를 unique로 유지하고, canonical JSON fingerprint가 다르면 충돌 오류를 반환합니다. 비교 대상은scenarioId,recruitId,applyIds, 제목 덮어쓰기, 입력 변수입니다.별도 결정 필요
외부 SES 성공 직후 DB 기록 전에 프로세스가 중단되면, SES 자체 멱등성 키가 없어 외부 발송의 정확히 한 번 처리를 보장할 수 없습니다. 따라서 구현 PR 시작 전
at-least-once + 중복 완화를 허용할지, SES 호출 경계에 별도 중복 방지 수단을 둘지 결정해야 합니다.이번 단계에서는 설계 기준만 정리하고 Outbox·워커·재시도 코드는 별도 PR로 구현합니다.
feat/#670 Idempotency-Key 요청 본문 검증 (#682)
feat/#670 발송 Outbox 저장 (#683)
feat/#670 Outbox claim과 lease 복구 (#684)
test: #670 V45·V46 Flyway 마이그레이션 검증 추가
test: #670 Outbox migration 제약 검증 추가
test: #670 V45 이전 fingerprint 누락 요청 충돌 검증 추가
fix: #670 fingerprint 없는 작업 요청 충돌 처리 수정
test: #670 Outbox 통합 테스트 기반 클래스 상속 추가
test: #670 통합 테스트 기반 클래스 상속 추가
test: #670 MySQL 동시 키 충돌 검증 추가
test: #670 MySQL 통합 테스트 dialect 설정 수정
17 remaining items
test: #670 메일 결과 조회 중복 검증 통합
test: #670 메일 migration 테스트 실행 환경 정리
test: #670 메일 요청 fingerprint 필드별 변경 검증 추가
Merge pull request #696 from JECT-Study/feat/670-mail-ses-outcome-cla…
Merge pull request #693 from JECT-Study/feat/670-mail-dispatch-claim-…
Merge pull request #688 from JECT-Study/feat/670-mail-dispatch-outbox…
feat: #670 메일 발송 worker 및 미처리 대상 자동 복구 구현 (#698)
chore: #670 dev 대상 메일 CI 실행
feat: #670 메일 worker·복구 및 안전한 재시도 dev 통합 (#700)
chore: #670 dev 병합 후 장애 복구 PR 기준 정리
feat: #670 메일 발송 장애 복구 검증 및 운영 관측 보강 (#702)
chore: #670 dev 병합 후 조회 검증 PR 기준 정리
test: #670 메일 조회 검증 통합 및 요청 식별 테스트 보강 (#706)
feat: #502 메일 예약 생성·조회 및 취소 기반 구현 (#707)