비동기 동기 및 트랜잭션 전파에 대해 알아봅니다...
옵션설명
옵션 | 설명 |
REQUIRED | 기본 옵션 부모 트랜잭션이 존재한다면 부모 트랜잭션에 합류, 그렇지 않다면 새로운 트랜잭션을 만든다. 중간에 자식/부모에서 rollback이 발생된다면 자식과 부모 모두 rollback 한다. |
REQUIRES_NEW | 새로운 트랜잭션을 만든다. nested한 방식으로 메소드 호출이 이루어지더라도 rollback은 각각 이루어 진다. |
MANDATORY | 무조건 부모 트랜잭션에 합류시킨다. 부모 트랜잭션이 존재하지 않는다면 예외를 발생시킨다. |
SUPPORTS | 메소드가 트랜잭션을 필요로 하지는 않지만, 진행 중인 트랜잭션이 존재하면 트랜잭션을 사용한다는 것을 의미한다. 진행 중인 트랜잭션이 존재하지 않더라도 메소드는 정상적으로 동작한다. |
NESTED | 부모 트랜잭션이 존재하면 부모 트랜잭션에 중첩시키고, 부모 트랜잭션이 존재하지 않는다면 새로운 트랜잭션을 생성한다. 부모 트랜잭션에 예외가 발생하면 자식 트랜잭션도 rollback한다. 자식 트랜잭션에 예외가 발생하더라도 부모 트랜잭션은 rollback하지 않는다. 이때 롤백은 부모 트랜잭션에서 자식 트랜잭션을 호출하는 지점까지만 롤백된다. 이후 부모 트랜잭션에서 문제가 없으면 부모 트랜잭션은 끝까지 commit 된다. 현재 트랜잭션이 있으면 중첩 트랜잭션 내에서 실행하고, 그렇지 않으면 REQUIRED 처럼 동작합니다. |
NEVER | 메소드가 트랜잭션을 필요로 하지 않는다. 만약 진행 중인 트랜잭션이 존재하면 익셉션이 발생한다. |
위에.. 이렇게 정리를 하는데 사실 위에 두개 빼고는 써본 적이 없다... 필요할때 찾아서 쓰면 될 듯..?
REQUIRED -> 부모트랜잭션과 자식트랜잭션이 같이 진행 중에 자식이 예외가 발생 시에 부모가 성공해도 전체 트랜잭션 실패.
REQUIRES_NEW -> 제일 이상한 넘... 무조건 새로운 트랜잭션을 열면 자식이든 부모이든 영향이 없어야하는데 영향이 있긴 하다 왜냐..?
대부분 부모에서 트랜잭션을 부르고 그 안에 자식트랜잭션이 새로운 트랜잭션을 만들어도 자식에서 예외가 발생하면 부모까지 예외가 전파가 된다...
예를 간단하게 들어보면
class 자식클래스{
private final TestRepository testRepository;
public void 자식클래스트랜잭션(){
testRepository.test();
throw new RunTimeException("자식클래스트랜잭션예외");
}
}
class 부모클래스{
private final 자식클래스 자식클래스;
private final TestRepository testRepository;
public void 부모클래스트랜잭션(){
testRepository.test();
자식클래스.자식클래스트랜잭션();
}
}
이런 경우에 예외가 전파가 된다고 볼 수 가 있다.....
해결 법은 부모트랜잭션에서 try catch로 예외를 잡던지 해결을 할 수 있다.
반대로 부모에서 예외가 터진다면 그때의 경우에는 자식은 트랜잭션이 잘 된다.
MANDATORY-> 부모의 트랜잭션이 있으면 합류를 시킨다 근데 부모의 트랜잭션이 없을 경우에는 예외가 터진다
개발 중에 자주 서비스레이어 영역에 메소드에 트랜잭션이 합쳐지거나 겹치는 경우가 생길 수가 있다.
예를 들면 댓글을 작성하면 + 알림을 전송을 한다고 하자.
댓글에 대해 성공을 하였지만 알림전송이 실패를 하여... 다 롤백이 일어난다.... 이런 경우에는 어떻게 하면 좋을까?
처음에는 아예 비동기를 적용을 하여서 따로 스레드를 돌리면 스레드 자체가 다르기 때문에 서로에 대해 영향을 받지 않게 해본다. (근데 이것을 굳이 비동기를 사용할 필요가 있을까? 알림 전송이 오래걸리는 것도 아닌데 비동기가 아닌 그냥 동기방법이 있나보자.)
1. 처음에 일단 @async를 써서 부모스레드와 자식스레드를 다르게 하여서 영향을 안받게 한다.
-> 그럼 부모 트랜잭션이 에러나도 실행되나? 확인해보자.
@transactionalListener 기본적으로 phase가 AFTER_COMMIT이라서 부모 트랜잭션이 커밋이 된 다음에 실행이 되는 경우이다.
@async + @transactionalListener
1. 부모 실패 -> 애초에 자식트랜잭션이 실행실패.
2. 자식 실패 -> 부모트랜잭션 실행 성공, 자식트랜잭션 실행실패
근데 이게 트랜잭션을 분리할려고하는게 비동기를 쓰는게 맞나..?
로그를 보면 비동기이기 때문에 스레드가 달라서 session이 다르게 나온다.
@transactionalListener 지워보자
@async + @EventListener ->
부모 트랜잭션이 실패해도 자식은 실행된다. -> 이상한...? ㅋㅋ..
그냥 다 따로 따로 돈다.
@async 빼보자
@Transactional+@TransactionalEventListener
1.부모실패 -> 부모 실패 자식 실패
2.자식실패 ->근데 애초에 부모가 커밋이 된 상태에서 실행이 하니까 자식에서 실패해도 부모에게 영향이 가지않는다.
위에 방법이 내가 원하는 방법...?이다.
'Spring' 카테고리의 다른 글
스프링 (0) | 2023.03.08 |
---|---|
Spring 올바른(?)빈주입? (0) | 2023.02.06 |
스프링-시큐리티 UsernamePasswordAuthenticationFilter (0) | 2022.12.18 |
스프링 aop (0) | 2022.12.13 |
Interceptor 예외처리 (0) | 2022.12.11 |