-
스프링 트랜잭션 전파Spring/Spring Boot 2021. 11. 9. 23:59
트랜잭션이 이미 진행중인데, 여기에 추가로 트랜잭션을 수행하면 어떻게 될까? 기존 트랜잭션과 별도의 트랜잭션을 진행해야 할까? 아니면 기존 트랜잭션을 그대로 이어 받아서 트랜잭션을 수행해야 할까?
이런 경우 어떻게 동작할지 결정하는 것을 트랜잭션 전파(propagation)라 한다. 참고로 스프링은 다양한 트랜잭션 전파 옵션을 제공한다.
1. 외부 트랜잭션이 수행중인데, 내부 트랜잭션이 추가로 수행됨
- 기본 전파 옵션인 REQUIRED 기준
스프링에서는 이 경우 외부 트랜잭션과 내부 트랜잭션을 묶어서 하나의 트랜잭션을 만들어준다. 내부 트랜잭션이 외부 트랜잭션에 참여하는 것이며 이것이 기본 동작이고 옵션을 통해 다른 동작방식도 선택할 수 있다.
스프링은 논리 트랜잭션과 물리 트랜잭션이라는 개념을 나눈다. 논리 트랜잭션들은 하나의 물리 트랜잭션으로 묶인다. 물리 트랜잭션은 실제 데이터베이스에 적용되는 트랜잭션을 뜻한다. 실제 커넥션을 통해서 트랜잭션을 시작하고, 실제 커넥션을 통해서 커밋/롤백하는 단위이다.
논리 트랜잭션은 트랜잭션 매니저를 통해 트랜잭션을 사용하는 단위이다. 이러한 논리 트랜잭션 개념은 트랜잭션이 진행되는 중에 내부에 추가로 트랜잭션을 사용하는 경우에 나타난다. 트랜잭션이 사용중일 때 또 다른 트랜잭션이 내부에 사용되면 여러가지 복잡한 상황이 발생할 수 있다. 이때 논리 트랜잭션 개념을 도입하면 다음과 같은 단순한 원칙을 만들 수 있다.
- 모든 논리 트랜잭션이 커밋되어야 물리 트랜잭션이 커밋된다.
- 하나의 논리 트랜잭션이라도 롤백되면 물리 트랜잭션은 롤백된다.**
처음 트랜잭션을 시작한 외부 트랜잭션이 실제 물리 트랜 잭션을 관리하도록 한다. 이를 통해 트랜잭션 중복 커밋 문제를 해결한다.
- 내부 논리 트랜잭션이 롤백되면 롤백 전용 마크를 표시한다. 그리고 외부 트랜잭션을 커밋할 때 롤백 전용 마크 ( rollbackOnly = true) 를 확인하게 된다. 즉 내부 트랜잭션의 롤백 요청이 곧바로 호출되는 것은 아니며 물리 트랜잭션이 외부 트랜잭션을 종료할때 까지 이어나감.
- 롤백시 UnexpectedRollbackException 예외를 던지며 기대하지 않은 롤백이 발생했다는 것을 명확하게 알 수 있음.
내부에서 시작했지만 두개를 다른 트랜잭션으로 보고 싶은 경우? REQUIRES_NEW
외부 트랜잭션과 내부 트랜잭션을 완전히 분리해서 사용하는 방법에 대해서 알아보자. 각각 별도의 물리 트랜잭션을 사용하는 방법이다. 그래서 커밋과 롤백도 각각 별도로 이루어지게 된다. 이 방법은 내부 트랜잭션에 문제가 발생해서 롤백해도, 외부 트랜잭션에는 영향을 주지 않는다. 반대로 외부 트랜잭션에 문제가 발생해도 내부 트랜잭션에 영향을 주지 않는다.
물리 트랜잭션을 분리하려면 내부 트랜잭션을 시작할 때 REQUIRES_NEW 옵션을 사용하면 된다. 외부 트랜잭션과 내부 트랜잭션이 각각 별도의 물리 트랜잭션을 가지게 되며 이는 DB 커넥션을 따로 사용한다는 뜻이다. 이 경우 내부 트랜잭션이 롤백되면서 로직 2가 롤백되어도 로직 1에서 저장한 데이터에는 영향을 주지 않는다. 최종적으로 로직2는 롤백되고, 로직1은 커밋된다.
내부 트랜잭션을 시작하면서 커넥션을 새로 획득하고 별도의 물리 트랜잭션을 시작한다. 내부 트랜잭션은 외부 트랜잭션에 참여하는 것이 아니라, PROPAGATION_REQUIRES_NEW 옵션을 사용했기 때 문에 완전히 새로운 신규 트랜잭션으로 생성되는 것이다.
논리 트랜잭션 중 하나를 예외를 복구한다면?
Required
내부 트랜잭션에서 rollbackOnly 를 설정하기 때문에 결과적으로 정상 흐름 처리를 해서 외부 트랜잭션에서 커밋을 호출해도 물리 트랜잭션은 롤백된다. 그리고 UnexpectedRollbackException 이 던져진다.
Requires New
내부 트랜잭션에서 REQUIRES_NEW 를 사용하게 되면 물리 트랜잭션 자체가 완전히 분리되어 버린다. 그리고 REQUIRES_NEW 는 신규 트랜잭션이므로 나머지 트랜잭션에서는 rollbackOnly 표시가 되지 않는다.
스프링 트랜잭션 전파 옵션들
1. REQUIRED
- 기본 설정이다. 필수이기 때문에 기존 트랜잭션이 없으면 만들고, 있으면 참여한다.
2. REQUIRES_NEW
- 항상 새로운 트랜잭션을 생성한다.
3. SUPPORT
- 트랜잭션을 지원한다는 뜻이다. 기존 트랜잭션이 없으면, 없는대로 진행하고, 있으면 참여한다.
4. NOT_SUPPORT
- 트랜잭션을 지원하지 않는다는 의미이다.
5. MANDATORY
- 의무사항이다. 트랜잭션이 반드시 있어야 한다. 기존 트랜잭션이 없으면 예외가 발생한다. IllegalTransactionStateException
6. NEVER
- 트랜잭션을 사용하지 않는다는 의미이다. 기존 트랜잭션이 있으면 예외가 발생한다. IllegalTransactionStateException. 기존 트랜잭션도 허용하지 않는 강 한 부정의 의미로 이해하면 된다.
옵션
- `isolation` , `timeout` , `readOnly` 는 트랜잭션이 처음 시작될 때만 적용된다. 트랜잭션에 참여하는 경우에는 적용되지 않는다. 예를 들어서 `REQUIRED` 를 통한 트랜잭션 시작, `REQUIRES_NEW` 를 통한 트랜잭션 시작 시점에만 적용된다.
참고
'Spring > Spring Boot' 카테고리의 다른 글
스프링 배치 반복 및 오류 제어 (0) 2022.03.30 스프링 @Transaction 이해 (0) 2021.10.29 [springboot, kotlin] 배포시 FileReader FileNotFoundException (0) 2021.09.28 @ConfigurationPropertiesScan 과 생성자 바인딩 (1) 2020.12.15 application.properties 란? (0) 2020.08.29