도도한 개발자
[DB] 트랜잭션 전파 - REQUIRES_NEW 본문
인프런 김영한님의 스프링 DB 2편 - 데이터 접근 활용 기술 강의 중 스프링 트랜잭션 전파 섹션을 기반으로 작성한 글입니다.
이전 글에서 트랜잭션 전파의 기본 옵션인 REQUIRED 옵션으로 내부 트랜잭션이 외부 트랜잭션에 참여하는 과정을 여러 경우로 나눠 정리해봤습니다.
위 글의 마지막으로 떠오른 궁금증을 이어 마저 정리해보겠습니다.
💭 내부에서 롤백되어도 외부에서 커밋되면 외부 커밋만이라도 반영할 순 없나? 그 반대는 안되나?
트랜잭션의 완전한 분리란 별도의 물리 트랜잭션을 사용하여 각각의 커밋 및 롤백이 서로에게 영향을 주지 않는다는 것을 말합니다.
위 그림은 두 트랜잭션이 하나의 물리 트랜잭션을 공유하는 것이 아닌, 각각의 물리 트랜잭션을 사용하고 있는 것을 보여줍니다. 그 방법으로 트랜잭션을 시작할 때 REQUIRES_NEW 옵션을 지정해주는 것인데요, 자세히 알아볼까요?
🐈⬛내부 롤백 & 외부 커밋 & REQUIRES_NEW
위와 같이 REQUIRES_NEW 옵션을 사용하면 되는데요, 그러면 외부 트랜잭션과 내부 트랜잭션이 별도의 물리 트랜잭션을 가지게 되어 DB 커넥션을 따로 사용할 수 있습니다. 이 경우 내부 트랜잭션이 롤백되면서 로직 B가 롤백되어도 로직 A에서 저장한 데이터엔 영향을 주지 않습니다.
최종적으로 로직 B는 롤백되고, 로직 A는 커밋됩니다.
로그를 천천히 살펴보자면,
외부 트랜잭션 시작 - 외부 트랜잭션을 시작하면서 conn0을 획득하고 물리 트랜잭션을 시작한다.
내부 트랜잭션 시작 - 내부 트랜잭션을 시작하면서 conn1 를 획득하고 물리 트랜잭션을 시작한다.
내부 트랜잭션 롤백 - 내부 트랜잭션을 롤백한다. 내부 트랜잭션은 신규 트랜잭션이기 때문에 실제 물리 트랜잭션을 롤백할 수 있고, 커넥션 conn1에 물리 롤백을 수행한다.
외부 트랜잭션 커밋 - 외부 트랜잭션을 커밋한다. 외부 트랜잭션은 신규 트랜잭션이기 때문에 실제 물리 트랜잭션을 커밋할 수 있고, 커넥션 conn0 에 물리 커밋을 수행한다.
이번엔 그림으로 볼까요?
🐾 내부 롤백 & 외부 커밋 & REQUIRES_NEW - 요청
[요청] 외부 트랜잭션
1. txManager.getTransaction() 메서드를 호출해서 외부 트랜잭션을 시작한다.
2. 트랜잭션 매니저는 DataSource를 통해 커넥션을 생성한다
3. 생성한 커넥션을 수동 커밋 모드(setAutoCommit(false)로 설정한다 ⇒ 물리 트랜잭션 시작
4. 트랜잭션 매니저는 생성한 커넥션을 트랜잭션 동기화 매니저에 보관한다.
5. 트랜잭션 매니저는 신규 트랜잭션 생경 결과를 TransactionStatus에 담아 반환한다.
6. 로직 A가 사용되고, 커넥션이 필요하면 트랜잭션 동기화 매니저의 con0 커넥션을 가져와 사용한다.
[요청] 내부 트랜잭션
7. REQUIRES_NEW 옵션을 지정해 txManager.getTransaction()을 통해 내부 트랜잭션을 시작한다.
8. 트랜잭션 매니저는 REQUIRES_NEW 옵션을 확인하고 세로운 트랜잭션을 시작한다
9. 생성한 커넥션을 수동 커밋 모드(setAutoCommit(false)로 설정한다 ⇒ 물리 트랜잭션 시작
10. 트랜잭션 매니저는 생성한 커넥션을 트랜잭션 동기화 매니저에 보관한다. 이때 con0은 잠시 보류되며, 내부 트랜잭션이 끝날 때까지 con1이 사용된다.
11. 트랜잭션 매니저는 신규 트랜잭션 생경 결과를 TransactionStatus에 담아 반환한다.
12. 로직 B가 사용되고, 커넥션이 필요하면 트랜잭션 동기화 매니저의 con1 커넥션을 가져와 사용한다.
🐾 내부 롤백 & 외부 커밋 & REQUIRES_NEW - 응답
[응답] 내부 트랜잭션
13. 로직 B가 끝나고 트랜잭션 매니저를 통해 내부 트랜잭션을 롤백한다.
14. 트랜잭션 매니저는 내부 트랜잭션을 신규 트랜잭션으로 판단해서 DB 커넥션에 실제 롤백을 호출한다.
15. 내부 트랜잭션이 con1 물리 트랜잭션을 롤백한다. 트랜잭션이 종료되고 con1을 커넥션 풀에 반납한다. 이제 con0 의 보류가 끝나고 다시 사용한다.
[응답] 외부 트랜잭션
16. 로직 A가 끝나고 트랜잭션 매니저를 통해 외부 트랜잭션을 커밋한다.
17. 트랜잭션 매니저는 외부 트랜잭션을 신규 트랜잭션으로 판단해서 DB 커넥션에 실제 커밋을 호출한다.
18. 외부 트랜잭션이 con0 물리 트랜잭션을 롤백한다. 트랜잭션이 종료되고 con0을 커넥션 풀에 반납한다.
정리하자면,
REQUIRES_NEW 옵션을 사용하면 물리 트랜잭션이 분리됩니다. 한편 트랜잭션이 분리되면 JPA의 영속성 컨텍스트가 달라기지 때문에 이 부분을 조심해야 합니다.
또한 트랜잭션이 2개가 생성되면 데이터베이스의 커넥션도 2개가 동시에 사용되기 때문에 커넥션 확보에 지장이 가지 않도록 조심해야 합니다.
🐈⬛ 스프링 트랜잭션의 다양한 전파 옵션
마지막으로 트랜잭션의 다양한 옵션들을 소개하고 마치겠습니다.
스프링은 다양한 전파 옵션을 제공합니다. 별도의 설정이 없으면 Default로 REQUIRED가 사용됩니다.
REQUIRED
기본 옵션이다. 기존 트랜잭션이 없으면 생성하고, 있으면 참여합니다.
REQUIRES_NEW
항상 새로운 트랜잭션을 생성합니다.
SUPPORT
트랜잭션을 지원한다는 뜻입니다. 기존 트랜잭션이 없으면, 없는대로 진행하고, 있으면 참여합니다.
NOT_SUPPORT
어떤 조건에서도 트랜잭션을 지원하지 않는다는 의미입니다.(단, 기존 트랜잭션이 수행되고 있는 경우 이를 보류합니다)
MANDATORY
트랜잭션이 반드시 있어야 합니다. 기존 트랜잭션이 있으면 참여하고, 없으면 IllegalTransactionStateException 예외가 발생합니다.
NEVER
트랜잭션을 절대 사용하지 않는다는 의미이다. 기존 트랜잭션이 있으면 IllegalTransactionStateException 예외가 발생한다. 기존 트랜잭션이 있어도 없이 진행합니다.
NESTED
기존 트랜잭션이 없으면 새로운 트랜잭션을 생성하고, 없으면 중첩 트랜잭션을 생성합니다. 중첩 트랜잭션이 롤백 되어도 외부 트랜잭션은 커밋할 수 있으나 외부 트랜잭션이 롤백 되면 중첩 트랜잭션도 함께 롤백됩니다.
실무에선 기본 옵션인 REQUIRED를 가장 자주 사용한다고 하는데 상황에 따라 필요한 옵션들을 사용하면 된다고 하네요.(당연한 말)
REQUIRES_NEW 옵션을 보면 항상 새로운 트랜잭션을 생성할 때마다 다른 커넥션을 사용하니 커넥션 부족 문제가 생길 수 있을 것 같습니다. 이처럼 각 옵션들마다 주의해야 할 점이 있기 마련인데 이 부분은 나중에 실무에서 경험하면서 배우면 좋을 것 같네요.
감사합니다. 끗!
'Backend' 카테고리의 다른 글
[DB] 트랜잭션 전파 - 외부 트랜잭션과 내부 트랜잭션은 서로 어떻게 영향을 미칠까? (2) | 2023.08.27 |
---|---|
Swagger - API 문서 자동화 사용기 (0) | 2023.06.07 |