프론트에서 500에러가 터진다고 해서 로그를 확인해보니 Transactional Rollback문제가 발생하고 있음을 확인했다.
Transaction rolled back because it has been marked as rollback-only
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
확인 결과 getAccessibility(해당 팀빌딩에 접근가능한지 여부)를 확인하는 API를 호출할 때 해당 문제가 발생했다.
하지만, 로직상의 문제는 없었다.
@Override
@Transcational(readyOnly=true)
public Accessibility getAccessibility(Long memberId) {
try {
TeamBuildingModel model = findProgressTeamBuilding();
queryTeamBuildingTargetService.getTarget(memberId, model.getId());
return Accessibility.JOINABLE;
} catch (EndTeamBuildingException | NotFoundTargetTeamBuildingException ex) {
return Accessibility.NONJOINABLE;
}
}
@Transcational(readyOnly=true)
public TeamBuildingTargetModel getTarget(Long memberId, Long teamBuildingId) {
return teamBuildingTargetRepository
.findByTeamBuildingIdAndMemberId(teamBuildingId, memberId)
.map(entityConverter::from)
.orElseThrow(NotFoundTargetTeamBuildingException::new);
}
내가 의도한 바는 자식 메서드 안에서 RuntimeException이 발생하여 트랜젝션을 롤백시키겠지만 부모 메서드에서 해당 에러를 잡기 때문에 롤백이 없이 커밋이 될 것이라고 생각했다.
@Transactional의 기본 전파(propagation)속성은 PROPAGATION_REQUIRED이다.
즉, 기존에 존재하는 트랜젝션에 참여한다는 의미이다.
나는 여기까지로만 생각을 했다.
즉 트랜젝션이 걸려있는 최상단 부모 메서드가 트랜젝션을 시작하고 해당 메서드가 종료되면 트랜젝션 또한 종료될 것이라고 생각했다.(단 한번의 시작과 단 한번의 종료)
기존 트랜젝션에 참여한다는 의미는
각 트랜젝션을 시작하고 완료하지만, commit, rollback과 같은 최종완료 처리만 최초 트랜젝션이 일어날 때 일어난다는 것이였다.
별도의 트랜젝션으로 분리하여 rollback여부가 true가 되어도 부모 트랜젝션이 영향을 받지 않도록 하는 방식으로 해결했다.
[BE/FIX] 별도의 트랜젝션으로 실행하도록 트랜젝션 분리 #282
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
public TeamBuildingTargetModel getTarget(Long memberId, Long teamBuildingId) {
return teamBuildingTargetRepository
.findByTeamBuildingIdAndMemberId(teamBuildingId, memberId)
.map(entityConverter::from)
.orElseThrow(NotFoundTargetTeamBuildingException::new);
}