인프런 커뮤니티 질문&답변

gusdn85554님의 프로필 이미지

작성한 질문수

스프링 DB 2편 - 데이터 접근 활용 기술

스프링 트랜잭션 전파 - REQUIRES_NEW 에 궁금한 점

22.11.15 16:22 작성

·

698

0

안녕하세요 영한님, 서포터즈님들

REQUIRES_NEW 옵션을 공부하면서 궁금한 점이 있습니다.

여기서 로직2의 트랜잭션 매니저에서는 내부 트랜잭션이므로 rollbackOnly 옵션을 확인하지 않는 것이 당연한 것으로 알고 있습니다.

그런데 외부, 내부 트랜잭션 구별 방법이 이전까지는 신규 트랜잭션인지 아닌지로 구별하였는데, REQUIRES_NEW 옵션에서는 외부,내부 트랜잭션을 구별하는 또 다른 옵션이 존재할까요,,?

제 생각에는 있을 것 같아서 내부 트랜잭션에서는 어떤 옵션이 있어서 rollbackonly 옵션을 확인하지 않을 것 같아서 질문 드립니다!!

키워드 알려주시면 찾아보겠습니다.

 

감사합니다.

답변 2

0

y2gcoder님의 프로필 이미지

2022. 11. 17. 13:50

예제 프로젝트 (spring-tx)에서 추가해 실행해봤던 허접한 테스트입니다.

@Slf4j
@SpringBootTest
public class BasicTxTest {

	//...


	@Test
	void inner_inner_rollback() {
		log.info("외부 트랜잭션 시작");
		TransactionStatus outer = txManager.getTransaction(new DefaultTransactionAttribute());
		log.info("outer.isNewTransaction()={}", outer.isNewTransaction());  //true

		log.info("내부 트랜잭션 시작");
		TransactionStatus inner = txManager.getTransaction(new DefaultTransactionAttribute());
		log.info("inner.isNewTransaction()={}", inner.isNewTransaction());  //true



		log.info("내부-내부 트랜잭션 시작");
		TransactionStatus innerInner = txManager.getTransaction(new DefaultTransactionAttribute());
		log.info("innerInner.isNewTransaction()={}", innerInner.isNewTransaction());  //true
		log.info("내부-내부 트랜잭션 롤백");
		txManager.rollback(innerInner);  //rollback-only 표시

		log.info("내부 트랜잭션 커밋");
		txManager.commit(inner);

		log.info("외부 트랜잭션 커밋");
		assertThatThrownBy(() -> txManager.commit(outer))
				.isInstanceOf(UnexpectedRollbackException.class);
	}

	@Test
	void inner_inner_rollback_requires_new() {
		log.info("외부 트랜잭션 시작");
		TransactionStatus outer = txManager.getTransaction(new DefaultTransactionAttribute());
		log.info("outer.isNewTransaction()={}", outer.isNewTransaction());  //true

		log.info("내부 트랜잭션 시작");
		DefaultTransactionAttribute definition = new DefaultTransactionAttribute();
		definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
		TransactionStatus inner = txManager.getTransaction(definition);
		log.info("inner.isNewTransaction()={}", inner.isNewTransaction());  //true

		log.info("내부-내부 트랜잭션 시작");
		TransactionStatus innerInner = txManager.getTransaction(new DefaultTransactionAttribute());
		log.info("innerInner.isNewTransaction()={}", innerInner.isNewTransaction());  //true
		log.info("내부-내부 트랜잭션 롤백");
		txManager.rollback(innerInner);  //rollback-only 표시

		log.info("내부 트랜잭션 커밋");
		assertThatThrownBy(() -> txManager.commit(inner))
				.isInstanceOf(UnexpectedRollbackException.class);

		log.info("외부 트랜잭션 커밋");
		txManager.commit(outer);  //커밋
	}
}

 

먼저 inner_inner_rollback()의 로그입니다.

image

inner_inner_rollback_requires_new()의 로그입니다.

image 

 

0

y2gcoder님의 프로필 이미지

2022. 11. 15. 17:47

안녕하세요. gusdn85554님, 공식 서포터즈 y2gcoder입니다.

말씀해주신 부분을 듣고 생각해봤을 때 이제까지 외부 트랜잭션, 내부 트랜잭션을 나누고 외부와 내부 트랜잭션을 구분할 필요가 있었던 것은 단일 물리 트랜잭션 안에 새로운 논리 트랜잭션을 생성하여 기존 트랜잭션에 참여하는 방식이었기 때문이라고 생각합니다.

반면에 REQUIRES_NEW는 기존 트랜잭션에 참여하는 방식이 아니라, 또 다른 물리 트랜잭션을 만드는 것입니다. 별도의 두 물리 트랜잭션이 작동하는 방식이기 때문에 내부 트랜잭션에 문제가 생겨서 롤백해도, 외부 트랜잭션은 다른 물리 트랜잭션이기 때문에 사실 내부 트랜잭션의 결과가 어떻든 외부 트랜잭션에 영향을 주지 않습니다.

그런 맥락에서 위의 상황에서는 REQUIRED_NEW rollbackonly 옵션을 확인할 필요가 없습니다. 왜냐하면 외부 트랜잭션은 내부 트랜잭션의 결과에 영향을 받지 않는 별개의 물리 트랜잭션이기 때문입니다. rollbackonly라는 옵션은 하나의 물리 트랜잭션 안에 참여한 내부의 논리 트랜잭션이 있을 때, 체크할 필요가 있는 옵션이라고 생각합니다.


감사합니다.

gusdn85554님의 프로필 이미지
gusdn85554
질문자

2022. 11. 17. 12:24

안녕하세요 y2gcoder님

REQUIRES_NEW로 생성된 내부 트랜잭션은 또 다른 물리 트랜잭션을 만드는 것이고, 해당 내부 트랜잭션에서는 또 다른 논리 트랜잭션이 존재하지 않기 때문에 rollbackOnly 옵션이 필요없다는 말씀으로 이해했습니다 !

여기서 궁금했던 점이 그림의 내부 트랜잭션 입장에서 '나는 내 안에 또 다른 트랜잭션을 가지고 있어' 라는 것을 알 수 있는 옵션이 따로 존재하는지 궁금해서요,,

 

감사합니다.

y2gcoder님의 프로필 이미지

2022. 11. 17. 13:30

REQUIRES_NEW로 생성된 내부 트랜잭션은 또 다른 물리 트랜잭션을 만드는 것이고, 해당 내부 트랜잭션에서는 또 다른 논리 트랜잭션이 존재하지 않기 때문에 rollbackOnly 옵션이 필요없다는 말씀으로 이해했습니다 ! 

rollbackOnly는 내부 논리 트랜잭션 에서 롤백이 발생해서 해당 물리 트랜잭션은 무조건 롤백해야 한다는 표시로 받아들이는 게 맞다고 생각합니다 :)

그리고 REQUIRES_NEW 옵션으로 생성된 트랜잭션은 새로운 연결을 사용한 물리 트랜잭션이니 내부에 논리 트랜잭션을 가질 수 있지 않을까 합니다.

저도 테스트와 디버깅을 찾아보면서 사실 질문자님께서 말씀해주셨던 외부 트랜잭션의 입장에서 자신이 내부 트랜잭션을 가지고 있는지에 대한 정보를 알 수 있는 방법은 찾지 못했습니다. 굳이 찾은 거라면 다른 물리 트랜잭션에 참여하게 된 논리 트랜잭션의 TransactionStatus의 속성 값 중 newTransaction의 속성값은 false라는 사실 뿐이었습니다. 실제로 newTransaction 속성은 문서에 따르면 해당 트랜잭션이 새로운지 여부를 반환하고, false라면 기존 트랜잭션에 참여하거나 실제 트랜잭션에서 실행되지 않았을 가능성이 있다고 합니다.

개인적으로 생각해본 것은 사실 외부 트랜잭션(=물리 트랜잭션)의 입장에서는 논리 트랜잭션이 실제로 커밋을 하는 것도 아니고, 그냥 자신의 트랜잭션을 갖다 쓰는 것이기 때문에 굳이 내부의 논리 트랜잭션을 알아야 하는가에 대한 생각도 들었습니다. 내부 트랜잭션에 대해 알아야 할 때는 내부 트랜잭션 로직에서 문제가 발생해 롤백이 필요할 때 뿐입니다. 그래서 롤백이 필요한 경우인지만 알기 위해 rollbackOnly 속성이 있는게 아닌가 하고 생각해봤습니다.

도움이 되지 못해 죄송합니다.

gusdn85554님의 프로필 이미지
gusdn85554
질문자

2022. 11. 17. 13:37

아,, isNewTransaction() 이 있었네요,,감사합니다 y2gcoder님 !!