feat: 이벤트 유실 방지와 재처리 Outbox 기반 구현#50
Conversation
krestar
left a comment
There was a problem hiding this comment.
-
기본 lease 30초는 현재 단계의 초기값으로 적절해 보입니다.
환경변수로 조정할 수 있고, 아직 장시간 실행되는 실제 handler가 없기 때문에 이번 PR의 차단 사유는 아니라고 생각합니다.
다만 현재 heartbeat가 없으므로 실제 handler와 외부 API timeout은 lease보다 짧게 제한해야 합니다.
향후 30초를 넘는 handler가 추가된다면 lease 연장 또는 heartbeat 도입을 함께 검토해야 합니다. -
REVIEW_REQUIRED 수동 재처리 API는 별도 운영 Issue로 분리하는 것이 적절합니다.
재처리에는 운영자 권한, 재처리 사유, AuditLog, expected version 또는 상태 검증이 필요하므로 Outbox 기반 구현과 분리하는 편이 안전합니다.
별도 Issue를 생성·연결하고, 그전에는 DB를 직접 수정하지 않는 현재 runbook을 유지하면 될 것 같습니다. -
Outbox claim bootstrap은 #34에서 RLS 활성화 전 필수 선행조건으로 반영하겠습니다.
claim 단계에서는 여러 사업장의 payload를 조회하지 않고 제한된 방식으로 event_id, company_id와 lease에 필요한 최소 정보만 반환해야 합니다.
claim 이후 payload 조회, handler 실행, 완료·실패 처리는 해당 이벤트의 company_id를 SET LOCAL app.company_id로 설정한 transaction 안에서 일반 RLS 정책을 따르도록 하겠습니다.
Runtime role에 BYPASSRLS, table owner 또는 전체 Outbox 조회 권한은 부여하지 않겠습니다.
위 조건들은 후속 운영 Issue와 #34에서 이어서 처리할 수 있으므로 제 리뷰 범위에서는 이번 PR을 막는 사항은 없습니다.
로컬 PostgreSQL 17.10 환경에서 PR #50 전체 테스트를 실행했습니다.
PostgreSQL 관련 검증은 정상 통과했습니다.
PostgreSqlMigrationTests: 통과
Flyway V1~V7 validate: 통과
AuthRefreshPostgreSqlConcurrencyTest: 통과
PostgreSqlTenantDatabaseContextTest: 6개 전부 통과
다만 전체 clean test에서는 4건이 실패했습니다.
Outbox 테스트 2건은 makeImmediatelyClaimable()에서 next_attempt_at = CURRENT_TIMESTAMP로 설정한 직후 JVM Clock으로 claim 여부를 판단하면서 시간 경계 차이로 processAvailable()이 0을 반환한 것으로 보입니다. 테스트에서 next_attempt_at을 확실한 과거로 설정하거나 동일한 Clock을 사용하는 방향을 확인 부탁드립니다.
Worker 테스트 2건은 로그상 updatedAt must not be before createdAt 예외로 500이 발생했습니다.
DB TIMESTAMP(6)과 Java Instant의 정밀도 차이 또는 현재 시각의 비단조성 문제로 보여, timestamp 정밀도 정규화나 updatedAt >= createdAt 보정이 필요해 보입니다.
이 부분은 PR #50의 Outbox 변경과 직접적인 관련은 없어 보입니다.
따라서 V7 PostgreSQL migration은 정상으로 확인됐지만, 전체 테스트의 로컬 재현 안정성은 한 번 확인 부탁드립니다.
이거 고려해보겠습니다 아니면 먼 이야기가 될 것 같지만 그거까지 고려해서 구현해보고 이 부분은 사용법이나 내용에 대해서 문서정리 하겠습니다 블로그쓰듯이 |
왜 필요한가요?
업무 DB 변경은 commit됐지만 Server가 직후 종료되거나 handler가 실패하면 후속 처리가 사라질 수 있습니다. 같은 이벤트가 다시 전달될 때 업무 결과가 중복 생성되는 문제도 막아야 합니다.
Closes #25
무엇이 바뀌나요?
event_publication,event_consumption과 tenant-aware FK·상태 CHECK·handler unique 제약을 추가했습니다.DomainEventPublisher와 최소 allow-list payload를 구현했습니다.REVIEW_REQUIRED, handler별 멱등 완료 기록을 구현했습니다.TaskCreated,TaskCancelled를 업무·감사로그와 같은 transaction에서 발행합니다.안전성
event_id,company_id,request_id, version과 기능별 허용 업무값만 저장합니다.검증
./gradlew clean test통과./gradlew build -x test통과로컬 환경에는 Docker/PostgreSQL 실행기가 없어 PostgreSQL 전용 테스트는 실행하지 못했습니다. 기존 GitHub Actions의 PostgreSQL 17 service가 PR CI에서 이를 검증합니다. 별도 Testcontainers 의존성을 중복 추가하지 않고 저장소의 기존 CI PostgreSQL 방식을 유지했습니다.
리뷰 포인트
REVIEW_REQUIRED수동 재처리 API는 별도 운영 Issue로 둘지롤백
V7이 적용된 뒤에는 migration 파일을 삭제하거나 수정하지 않습니다. 문제가 있으면
OUTBOX_ENABLED=false로 worker만 중지하고 publication을 보존한 뒤 forward migration 또는 후속 코드로 교정합니다.