Spring ‐ 스프링 트랜잭션 전파 - dnwls16071/Backend_Study_TIL GitHub Wiki

📚 스프링 트랜잭션 전파

[ 트랜잭션 두 번 사용 ]

스크린샷 2025-02-02 오후 11 17 33

스크린샷 2025-02-02 오후 11 18 22

  • 트랜잭션이 각각 수행되면서 사용되는 DB 커넥션은 다르다.

[ 전파 기본 - REQUIRED 옵션 디폴트 ]

스크린샷 2025-02-03 오전 12 30 28

  • 모든 논리 트랜잭션이 커밋되어야 물리 트랜잭션이 커밋된다.
  • 하나의 논리 트랜잭션이라도 롤백이 된다면 물리 트랜잭션은 롤백된다.

스크린샷 2025-02-02 오후 11 42 36

  1. 외부 트랜잭션이 시작된다.
  2. 트랜잭션 매니저는 생성자를 통해 주입받는 데이터 소스를 통해 커넥션을 생성한다.
  3. 생성한 커넥션을 수동 커밋 모드로 전환하면서 실제 트랜잭션을 시작한다. → 물리 트랜잭션 시작
  4. 트랜잭션 매니저는 트랜잭션 동기화 매니저에 커넥션을 보관한다.
  5. 트랜잭션 매니저는 트랜잭션 생성 결과를 TransactionStatus에 담아서 반환한다. 이 때, 새로운 트랜잭션이므로 신규 트랜잭션으로 분류가 된다.
  6. 로직1이 수행될 때 커넥션이 필요한 경우 트랜잭션 동기화 매니저로부터 커넥션을 확보해 사용한다.
  7. 내부 트랜잭션을 시작한다.
  8. 이 때, 내부 트랜잭션의 경우 생성자를 통해 주입받는 데이터 소스를 통해 커넥션을 생성하지 않고 트랜잭션 동기화 매니저를 통해 기존 트랜잭션이 존재하는지 확인한다.
  9. 기존 트랜잭션(외부 트랜잭션)이 있기 때문에 기존에 시작된 트랜잭션을 자연스럽게 사용하게 된다.
  10. 트랜잭션 매니저에서 트랜잭션 생성 결과를 TransactionStatus에 담아서 반환하는데 이 때, 기존 트랜잭션을 이어서 사용하는 것이기 때문에 신규 트랜잭션으로 분류되지 않는다.
  11. 로직2가 수행될 때 커넥션이 필요한 경우 트랜잭션 동기화 매니저로부터 커넥션을 확보해 사용한다.

스크린샷 2025-02-02 오후 11 49 15

  1. 로직2 실행이 끝나고 트랜잭션 매니저를 통해 내부 트랜잭션을 커밋한다.
  2. 이 때, 로직2를 실행한 트랜잭션은 외부에서 이어받은 내부 트랜잭션이다. 이 경우 실제 트랜잭션이 아니기 때문에 커밋을 호출하지 않는다.
  3. 로직1 실행이 끝나고 트랜잭션 매니저를 통해 외부 트랜잭션을 커밋한다.
  4. 이 때, 로직1을 실행한 트랜잭션은 외부에서 처음 생성된 신규 트랜잭션이기 때문에 DB 커넥션에 실제 커밋을 호출한다.
  5. 실제 데이터베이스 커밋이 반영되고 물리 트랜잭션도 끝난다.

❗트랜잭션 매니저에 커밋을 호출한다고 해서 실제 데이터베이스 커넥션에 물리 커밋이 발생하는 것이 아니다. ❗신규 트랜잭션인 경우 실제 커넥션을 사용해서 물리 커밋과 롤백을 수행한다. ❗내부 트랜잭션이 외부의 트랜잭션을 이어 받아 사용하게 되면 내부 트랜잭션에서 트랜잭션 매니저에 커밋하는 것이 항상 물리 커밋으로 이어지는 것이 아니다. ❗결과적으로 외부 트랜잭션이 물리 커밋을 담당하게 되고 이 외부 트랜잭션을 내부에서 이어서 추가로 사용하는 내부 트랜잭션의 경우 논리 커밋을 담당한다고 볼 수 있는 것이다.

[ 외부 롤백 ]

스크린샷 2025-02-03 오전 12 03 02

  • 외부 트랜잭션이든 내부 트랜잭션이든 하나의 물리 트랜잭션 안에 여러 논리 트랜잭션이 포함된 상태에서 외부 트랜잭션에서 롤벡이 발생하게 되면 전체 물리 트랜잭션 역시 롤백이 된다.

스크린샷 2025-02-03 오전 12 06 10

  1. 로직2 실행이 끝나고 트랜잭션 매니저를 통해서 내부 트랜잭션을 커밋한다.
  2. 이 때, 로직2를 실행하는 트랜잭션은 외부 트랜잭션에서 생성된 신규 트랜잭션을 그대로 이어 받아서 사용한 것이므로 실제 커밋이 호출되지 않는다.
  3. 로직1 실행이 끝나고 트랜잭션 매니저를 통해 외부 트랜잭션을 롤백한다.
  4. 이 때, 로직1을 수행하는 트랜잭션은 외부 트랜잭션에서 생성된 신규 트랜잭션이다. 따라서 DB 커넥션에 실제 롤백을 호출한다.
  5. 트랜잭션 매니저의 롤백 호출이 일어났으므로 이는 논리 롤백에 해당하고 실제 DB 커넥션에 롤백을 호출하는 것은 물리 롤백에 해당한다.

[ 내부 롤백 ]

스크린샷 2025-02-03 오전 12 11 54

스크린샷 2025-02-03 오전 12 18 15

  1. 로직2 실행이 끝나고 트랜잭션 매니저를 통해 내부 트랜잭션을 롤백한다.
  2. 이 때, 로직2를 수행하는 트랜잭션은 내부 트랜잭션으로 외부 트랜잭션을 이어 받은 기존의 트랜잭션이다.
  3. 내부 트랜잭션에서 실제 DB 커넥션에 커밋이나 롤백을 호출하면 안 되기 때문에 트랜잭션 동기화 매니저에 rollbackOnly=true표시를 해준다.
  4. 로직1 실행이 끝나고 트랜잭션 매니저를 통해 외부 트랜잭션을 커밋한다.
  5. 이 때, 로직1을 수행하는 트랜잭션은 외부 트랜잭션으로 신규 트랜잭션에 해당한다. DB 커넥션에 실제 커밋을 호출해야 하지만 이 때, 먼저 트랜잭션 동기화 매니저에 rollbackOnly=true표시가 있는지 확인한다. 롤백 전용 표시가 있으면 물리 트랜잭션을 커밋하는 것이 아니라 롤백한다.
  6. 결국 논리 트랜잭션의 롤백 호출이 일어나게 되고 물리 트랜잭션의 롤백으로 이어져 실제 DB 커넥션에 롤백이 반영되고 물리 트랜잭션이 끝나게 된다.

[ REQUIRES_NEW ]

  • REQUIRES_NEW 옵션은 외부 트랜잭션과 내부 트랜잭션을 완전히 분리해서 별도의 물리 트랜잭션을 사용하는 방법이다. 그래서 이전 방법들과 다르게 커밋과 롤백이 각각 별도로 이루어지게 된다.
TransactionDefinition.PROPAGATION_REQUIRES_NEW

스크린샷 2025-02-03 오전 12 24 22

  • 물리 트랜잭션을 분리하려면 내부 트랜잭션 시작시 REQUIRES_NEW 옵션을 사용하면 된다.
  • 외부 트랜잭션과 내부 트랜잭션이 별도의 물리 트랜잭션을 가진다.
  • 위와 달리 DB 커넥션을 따로 사용한다는 뜻이다.
  • 최종적으로 로직2의 트랜잭션 롤백이 발생하더라도 로직1의 트랜잭션 커밋은 이루어진다.

스크린샷 2025-02-03 오전 12 34 32

  1. 외부 트랜잭션을 시작한다.
  2. 생성자를 통해 주입받는 데이터 소스를 통해 커넥션을 생성한다.
  3. 자동 커밋 모드를 수동 커밋 모드로 변경함으로써 실제 물리 트랜잭션을 시작한다.
  4. 트랜잭션 매니저는 트랜잭션 동기화 매니저에 커넥션을 보관한다.
  5. 신규 트랜잭션이기 때문에 TransactionStatus에 신규 트랜잭션 결과를 담게 된다.
  6. 로직1이 수행되는데 이 때, 커넥션이 필요한 경우 트랜잭션 동기화 매니저에 보관된 커넥션을 획득해서 사용한다.
  7. REQUIRES_NEW 옵션이기 때문에 외부 트랜잭션을 이어서 사용하지 않고 새롭게 내부 트랜잭션을 시작한다.
  8. 생성자를 통해 주입받는 데이터 소스를 통해 커넥션을 생성한다.
  9. 자동 커밋 모드를 수동 커밋 모드로 변경함으로써 실제 물리 트랜잭션을 시작한다.
  10. 트랜잭션 매니저는 트랜잭션 동기화 매니저에 커넥션을 보관한다.
  11. 외부 트랜잭션을 이어 받아 사용하는 것이 아니라 새로운 트랜잭션이기 때문에 신규 트랜잭션으로 분류가 된다.
  12. 로직2가 수행되는데 이 때, 커넥션이 필요한 경우 트랜잭션 동기화 매니저에 보관된 커넥션을 획득해서 사용한다.

스크린샷 2025-02-03 오전 12 44 42

  1. 로직2가 끝나고 트랜잭션 매니저를 통해 내부 트랜잭션을 롤백한다.
  2. 이 때, REQUIRES_NEW 옵션이기 때문에 외부 트랜잭션을 이어서 사용하지 않는다. 따라서 신규 트랜잭션으로 분류가 되기 때문에 실제 DB 커넥션에 롤백을 호출한다.
  3. 내부 트랜잭션이 물리 트랜잭션을 롤백한다.
  4. 외부 트랜잭션에 커밋을 요청한다.
  5. 이 때, 해당 트랜잭션은 신규 트랜잭션이기 때문에 실제 DB 커넥션에 커밋을 호출한다.
  6. rollbackOnly 설정이 없기 때문에 커밋이 된다.
  7. 실제 DB 커넥션에 커밋이 발생한다.

❗하나의 요청에 여러 커넥션을 사용하기 때문에 데드락이 발생할 위험이 높다. ❗데이터 정합성 문제가 발생할 수 있다.

[ 다양한 전파 옵션 ]

  • 실제는 대부분 REQUIRED 옵션을 사용하고 아주 가끔 REQUIRES_NEW 옵션을 사용하며 나머지는 거의 사용하지 않는다.
  • REQUIRED
    • 기존 트랜잭션이 있으면 이어 받고 없으면 새로 생성한다.
    • 가장 많이 사용하는 기본 설정
  • REQUIRES_NEW
    • 항상 새로운 트랜잭션을 생성한다.
    • 데드락 발생 위험이 높고 데이터 정합성 불일치가 발생할 수 있다.
  • SUPPORT
    • 트랜잭션을 지원한다.
    • 기존 트랜잭션이 있으면 이어 받고 없으면 없는대로 진행한다.
  • 기타 : NOT_SUPPORT, MANDATORY, NEVER, NESTED ...