무엇이
LeftMemberPurgeService.purgeExpired()(크론 진입점)가 같은 클래스의 purge()를 직접 부른다. 프록시를 거치지 않아 purge()의 @Transactional이 적용되지 않는다.
근거
LeftMemberPurgeService.kt:40-43: @Scheduled fun purgeExpired() { purge() }
LeftMemberPurgeService.kt:45: @Transactional fun purge(). 자기 호출이라 트랜잭션이 열리지 않는다.
MemberDeviceRepository.deleteAllByMemberId는 @Modifying @Query이고 @Transactional이 없다. 트랜잭션 없이 부르면 TransactionRequiredException이 난다.
refreshTokenRepository.deleteAllByMemberId, notificationRepository.deleteAllByMemberId는 querydsl Impl의 @Transactional로 각각 따로 커밋된다.
무엇이 문제인가
ditto.member.purge.dry-run=false로 켜면 04:00 크론에서 토큰·알림만 지워지고 기기 삭제에서 예외가 난다. 회원은 지워지지 않고 루프도 멈춘다. 다음 날에도 같은 회원에서 같은 식으로 실패해서 "30일 뒤 완전 삭제"가 끝나지 않는다.
지금은 기본값이 dry-run=true라 대상 조회만 하고 끝나서 드러나지 않는다. dry-run을 끄기 전에 고쳐야 한다.
테스트가 이 문제를 잡지 못한다. MemberLeaveTest는 purge()를 테스트 쪽 트랜잭션 안에서 부르고, 크론 진입점 테스트는 삭제 대상이 없는 경우만 본다.
수정 방향
@Scheduled를 purge()에 직접 붙이거나, 크론 진입점을 다른 빈으로 나눈다.
- dry-run을 끈 상태로 크론 진입점을 불러 회원까지 지워지는지 보는 테스트를 추가한다.
배경
#138 PR의 버그 리뷰에서 나왔다. 스케줄러 스레드 변경과는 상관없이 원래 있던 문제다.
무엇이
LeftMemberPurgeService.purgeExpired()(크론 진입점)가 같은 클래스의purge()를 직접 부른다. 프록시를 거치지 않아purge()의@Transactional이 적용되지 않는다.근거
LeftMemberPurgeService.kt:40-43:@Scheduled fun purgeExpired() { purge() }LeftMemberPurgeService.kt:45:@Transactional fun purge(). 자기 호출이라 트랜잭션이 열리지 않는다.MemberDeviceRepository.deleteAllByMemberId는@Modifying @Query이고@Transactional이 없다. 트랜잭션 없이 부르면TransactionRequiredException이 난다.refreshTokenRepository.deleteAllByMemberId,notificationRepository.deleteAllByMemberId는 querydsl Impl의@Transactional로 각각 따로 커밋된다.무엇이 문제인가
ditto.member.purge.dry-run=false로 켜면 04:00 크론에서 토큰·알림만 지워지고 기기 삭제에서 예외가 난다. 회원은 지워지지 않고 루프도 멈춘다. 다음 날에도 같은 회원에서 같은 식으로 실패해서 "30일 뒤 완전 삭제"가 끝나지 않는다.지금은 기본값이
dry-run=true라 대상 조회만 하고 끝나서 드러나지 않는다. dry-run을 끄기 전에 고쳐야 한다.테스트가 이 문제를 잡지 못한다.
MemberLeaveTest는purge()를 테스트 쪽 트랜잭션 안에서 부르고, 크론 진입점 테스트는 삭제 대상이 없는 경우만 본다.수정 방향
@Scheduled를purge()에 직접 붙이거나, 크론 진입점을 다른 빈으로 나눈다.배경
#138 PR의 버그 리뷰에서 나왔다. 스케줄러 스레드 변경과는 상관없이 원래 있던 문제다.