Spring @Transactional propagation 정리

 Spring 에서는 총 일곱가지의propagation 옵션을 제공한다.

@Transactional  로 묶인 함수나 클래스에서 excpetion 이 발생하면 rollback이 된다. 하지만 스프링에서 제공하는 noRollbackFor, noRollbackForClassName, rollbackFor, rollbackForClassName 같은 옵션을 통해 이 역시 설정 할 수 있다.


1.REQUIRED

물리적 transaction 이 없으면 만든다. 이미 존재하면 그 transaction 에 참여한다. 그리고 각각의 @Transactional REQUIRED 로 설정된 메소드 들은 논리적 transaction 을 갖는데, 각각 경계를 가지고 있고, 저마다 범위가 있다. 


모든 transaction 이 하나의 물리적 transaction  에 매핑되기 때문에 그중 하나라도 논리적 transaction 이 실패하게 되면 rollback  모든 그 물리적 transaction 에 있는 논리적 transaction 들이 rollback 된다.

바깥에서 catch 해서 처리 한다 하더라도 여전히 transaction 은 rollback 된다. 이미 실패된 논리적 transaction 에서 rollback-only marker 라는것을 세팅 해버리기 때문이다. 이렇게 되면 같은 물리적 transaction 에 있는 것들은 rollback 될수밖에 없다.


2.REQUIRES_NEW

이는 이름에서처럼, 스프링이 항상 새로운 물리적 transaction 을 만들도록 한다. 그렇기 때문에 바깥의 물리적 transaction 의 속성(timeout, isolation level, readonly)을 상속받지 않는다. 

각각의 물리적 transaction 은 각각의 database connection 을 필요로 한다는것을 염두해야 한다. 예를 들어 메소드 A 가 메소드 B 를 호출했는데, B 가 REQUIRES_NEW 로 열렸다면, B 가 끝날때까지 A 는 connection 이 오픈 된 상태로 B 가 끝날때까지대기하게 된다. 


이 경우 B 가 Runtime Exception 을 호출하면 caller 인 A 역시 rollback 되지만(because exception is propagated to A), try-catch 로 Runtime Exception 을 막으면, A 의 transaction 은 rollback 되지 않고 commit 된다.  마찬가지로 B 가 commit 되고 난 뒤 A 가 rollback 되더라도 B 의 commit 은 영향 받지 않는다. 


3.NESTED

NESTED 는 1번 REQUIRED 와 유사하게 행동한다. 다른점은,  savepoint 라는것을 사용한다는 것이다. 내부의 논리적 transaction 이 caller 의 logical transaction 과 무관하게 rollback 될 수 있다.

Hibernate JPA 는 NESTED 옵션을 지원하지 않는다.


4.MANDATORY

이 옵션은, 이 메소드 혹은 클래스까지 오기 이전에 기존에 물리적 transaction 이 반드시 존재해야한다. 그렇지 않으면 exception  을 발생시킨다. 존재 한다면, 그 물리적 transaction 에 참여하게 되며, 이 후 동작은 REQUIRED와 같이, callee 가 에러를 발생시키면, caller 에서 발생한 작업 역시 rollback 된다. 


5.NEVER

4번 옵션과 달리, 이 메소드 혹은 클래스가 실행될때, 이전에 존재하는 물리적 transaction  이 반드시 없어야 한다. 존재하면 exception  을 발생시킨다. 


6.NOT_SUPPORTED

이 메소드 혹은 클래스가 실행될 때, 기존에 물리적 transaction 이 있으면, 그것을 일시 중지 시키고,  어떠한 물리적 transaction 과 상관 없이 떨어져서 작업을 수행한다. 이때 DB 작업이 필요하다면, REQUIRED 형태로 새로운 별도의 물리적  transaction  을 생성하여 작업을 하고, 이 메소드 혹은 클래스가 종료 되면, 기존의 suspend 되었던 transaction  을 resume  시킨다. 이때, 따로 떨어져 작업했던 결과가 exception 을 발생시켰다면, 이 역시 propagate 되어 caller 역시 rollbakc 된다.


마찬가지도, suspend 된 기존 DB connection  역시 active  상태로, 쓰이지 못하며 대기하게 되므로, 잘 관리해야 한다. (오래 걸리지 않도록 해야한다.)

7.SUPPORTS

기존에 물리적 transaction 이 존재한다면, 경계선을 긋고 논리적 transaction 으로 그 물리적 transaction 에 참여한다. 기존에 존재하는게 없으면, 그냥 수행한다.


caller 가  REQUIRED 이고, callee 가 SUPPORTS 라고 생각해보자. REQUIRED 는 물리 transaction 을 만든다. callee  가 수행될땐, 이미 물리 transaction 이 있으므로, 여기에 join 한다. 이때 실패하게 되면, rollbakc 하고, caller 의 transaction 도 rollback 된다. 바깥에서 try-catch  를 해도, 이미 안에서 rollback-only marker 를 찍었고 동일한 물리 transaction 이기 때문에 어쩔수 없다.


만약 caller 가 아무런 annotation 이 없고, callee 가 SUPPORTS 라고 해보자. 이 경우 caller 는 물리적 transaction 없이 작업을 수행한다. 만약 DB 세팅하는 순간이 오면, 기본값이 REQUIRED 에 의해 스프링은 물리 transaction 을 생성하고, insert 같은 작업을 할것이고, commit 을 할 것이다. 그리고 callee 를 부르게 되면, callee 입장에서는 기존의 물리적 transaction 이 없으므로 joni 할 transaction 이 없고, 그렇다고 따로 만들지도 않는다. save 같은 함수를 호출하게 되면, 그저 스프링의 기본값인   REQUIRED 로 물리적 transaction  을 만들고, commit 하고 닫는다. 만약 callee 가 runtime exception 을 발생시킨다고 해도, 이미 뭐 처리할 물리적 transaction같은게 없으므로, 아무런 rollbackk 도 일어나지 않는다. 


참조 : https://dzone.com/articles/spring-transaction-propagation

Comments

Popular posts from this blog

삼성전자 무선사업부 퇴사 후기

개발자 커리어로 해외 취업, 독일 이직 프로세스

코드리뷰에 대하여