[Spring] 트랜잭션 전파 propagation

hi·2023년 1월 2일
0
  • 히카리 커넥션 풀에서 커넥션을 획득하면 실제 커넥션을 그대로 반환하는 것이 아니라
    내부 관리를 위해 히카리 프록시 커넥션이라는 객체를 생성해서 반환
  • 객체 내부에 실제 커넥션이 포함되어 있음
  • 객체의 주소를 확인하여 커넥션 구분

트랜잭션이 두 개 이상이면 어떻게 동작하는가?

외부 트랜잭션, 내부 트랜잭션

외부 트랜잭션

  • 상대적으로 밖에 있기 때문에 외부 트랜잭션
  • 처음 시작된 트랜잭션

내부 트랜잭션

  • 외부에 트랜잭션이 수행되고 있는 도중에 호출되기 때문에 마치 내부에 있는 것 처럼 보임

물리 트랜잭션, 논리 트랜잭션

  • 논리 트랜잭션들은 하나의 물리 트랜잭션으로 묶인다
  • 물리 트랜잭션은 우리가 이해하는 실제 데이터베이스에 적용되는 트랜잭션,
    논리 트랜잭션은 트랜잭션 매니저를 통해 트랜잭션을 사용하는 단위
  • 이러한 논리 트랜잭션 개념은 트랜잭션이 진행되는 중에 내부에 추가로 트랜잭션을 사용하는 경우에 나타난다. (REQUIRED 전파 옵션을 사용하는 경우)
    단순히 트랜잭션이 하나인 경우 둘을 구분하지는 않음

원칙

  • 모든 논리 트랜잭션이 커밋되어야 물리 트랜잭션이 커밋된다
  • 하나의 논리 트랜잭션이라도 롤백되면 물리 트랜잭션은 롤백된다
  • 여러 트랜잭션이 함께 사용되는 경우, 처음 트랜잭션을 시작한 외부 트랜잭션이
    실제 물리 트랜잭션을 관리
  • 트랜잭션 매니저에 커밋을 호출한다고해서 항상 실제 커넥션에 물리 커밋이 발생하지는 않음
  • 신규 트랜잭션인 경우에만 실제 커넥션을 사용해서 물리 커밋과 롤백을 수행
  • 내부 논리 트랜잭션이 롤백되면 롤백 전용 마크를 표시
    rollbackOnly=true

isNewTransaction

  • 신규 트랜잭션 여부 확인
TransactionStatus outer = txManager.getTransaction(new DefaultTransactionAttribute());
log.info("outer.isNewTransaction()={}", outer.isNewTransaction());

REQUIRES_NEW

  • 외부 트랜잭션과 내부 트랜잭션을 완전히 분리해서 사용하는 방법
  • 커밋과 롤백이 각각 별도로 이루어짐

setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);


DefaultTransactionAttribute definition = new DefaultTransactionAttribute();
 
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

TransactionStatus inner = txManager.getTransaction(definition);
  • 전파 옵션인 propagationBehavior 에 PROPAGATION_REQUIRES_NEW 을 설정
    👉 내부 트랜잭션을 시작할 때 기존 트랜잭션에 참여하는 것이 아니라 새로운 물리
    트랜잭션을 만들어서
    시작하게 됨

  • 데이터베이스 커넥션이 동시에 2개 사용된다는 점을 주의


전파 옵션

REQUIRED

  • 기본 설정으로 실무에서 대부분 사용
  • 기존 트랜잭션이 없으면 생성하고, 있으면 참여
  • 트랜잭션이 필수

기존 트랜잭션 X : 새로운 트랜잭션을 생성
기존 트랜잭션 O : 기존 트랜잭션에 참여

REQUIRES_NEW

  • 항상 새로운 트랜잭션을 생성

기존 트랜잭션 X : 새로운 트랜잭션을 생성
기존 트랜잭션 O : 새로운 트랜잭션을 생성

SUPPORT

  • 트랜잭션을 지원
  • 기존 트랜잭션이 없으면 없는대로 진행
    있으면 참여

기존 트랜잭션 X : 트랜잭션 없이 진행
기존 트랜잭션 O : 기존 트랜잭션에 참여

NOT_SUPPORT

  • 트랜잭션을 지원하지 않음

기존 트랜잭션 X : 트랜잭션 없이 진행
기존 트랜잭션 O : 트랜잭션 없이 진행 (기존 트랜잭션은 보류)

MANDATORY

  • 의무사항
  • 트랜잭션이 반드시 있어야 함
  • 기존 트랜잭션이 없으면 예외 발생

기존 트랜잭션 X : IllegalTransactionStateException 예외 발생
기존 트랜잭션 O : 기존 트랜잭션에 참여

NEVER

  • 트랜잭션을 사용하지 않음
  • 기존 트랜잭션이 있으면 예외 발생

기존 트랜잭션 X : 트랜잭션 없이 진행
기존 트랜잭션 O : IllegalTransactionStateException 예외 발생

NESTED

기존 트랜잭션 X : 새로운 트랜잭션 생성
기존 트랜잭션 O : 중첩 트랜잭션 생성

  • 중첩 트랜잭션은 외부 트랜잭션의 영향을 받지만, 중첩 트랜잭션은 외부에 영향을 주지 않음
  • 중첩 트랜잭션이 롤백 되어도 외부 트랜잭션은 커밋 가능
    외부 트랜잭션이 롤백 되면 중첩 트랜잭션도 함께 롤백
  • JDBC savepoint 기능을 사용
    DB 드라이버에서 해당 기능을 지원하는지 확인 필요
  • 중첩 트랜잭션은 JPA에서는 사용 불가

💡 isolation timeout readOnly 는 트랜잭션이 처음 시작될 때만 적용된다
트랜잭션에 참여하는 경우에는 적용 X

예를 들어 REQUIRED , REQUIRES_NEW 를 통한 트랜잭션 시작 시점에만 적용


REQUIRES_NEW 대체 방안

REQUIRES_NEW를 사용하지 않고 구조를 분리하는 방법도 있다.
이렇게 하면 동시에 커넥션 2개를 사용하지 않으나,
구조상 REQUIRES_NEW를 사용하는 것이 더 깔끔한 경우도 있음.

0개의 댓글