공식문서는 트랜잭션 전파에 대해서 뭐라고 할까요?
이번글은 공식 문서에서 소개하는 트랜잭션 전파에 대해 정리했습니다.
Transaction Propagation
This section describes some semantics of transaction propagation in Spring. Note that this section is not a proper introduction to transaction propagation. Rather, it details some of the semantics regarding transaction propagation in Spring.
이 섹션에서는 Spring에서의 트랜잭션 전파(transaction propagation)에 대한 몇 가지 의미론(semantics)을 설명합니다.
그러나 이 섹션은 트랜잭션 전파에 대한 올바른(본격적인) 소개가 아니라, Spring에서의 트랜잭션 전파와 관련된 몇 가지 의미론적인 내용을 상세히 설명하는 것입니다.
In Spring-managed transactions, be aware of the difference between physical and logical transactions, and how the propagation setting applies to this difference.
Spring이 관리하는 트랜잭션에서는 물리적 트랜잭션과 논리적 트랜잭션의 차이를 인식하고, 전파 설정이 이 차이에 어떻게 적용되는지 주의해야 합니다.
Understanding PROPAGATION_REQUIRED
PROPAGATION_REQUIRED enforces a physical transaction, either locally for the current scope if no transaction exists yet or participating in an existing 'outer' transaction defined for a larger scope. This is a fine default in common call stack arrangements within the same thread (for example, a service facade that delegates to several repository methods where all the underlying resources have to participate in the service-level transaction).
PROPAGATION_REQUIRED는 물리적 트랜잭션을 강제하며, 현재 범위 내에서 트랜잭션이 존재하지 않으면 로컬에서 새로 생성하고, 더 넓은 범위에서 정의된 기존의 '외부' 트랜잭션이 있으면 해당 트랜잭션에 참여합니다.
이는 동일한 스레드 내에서 일반적인 호출 스택 구성에서 적절한 기본값이며(예: 여러 개의 리포지토리 메서드에 위임하는 서비스 퍼사드에서, 모든 하위 리소스가 서비스 수준의 트랜잭션에 참여해야 하는 경우).
서비스 퍼사드는 무엇인가요?
서비스 퍼사드(Service Facade)는 여러 개의 리포지토리(repository) 메서드를 호출하는 서비스 계층을 의미합니다.
즉, 하나의 서비스 메서드가 여러 개의 데이터베이스 작업을 묶어서 실행하는 구조입니다.
따라서, 서비스 계층에서 트랜잭션을 시작했으면, 하위의 모든 리포지토리 메서드들이 같은 트랜잭션을 공유해야 한다는 의미입니다.
By default, a participating transaction joins the characteristics of the outer scope, silently ignoring the local isolation level, timeout value, or read-only flag (if any). Consider switching the validateExistingTransactions flag to true on your transaction manager if you want isolation level declarations to be rejected when participating in an existing transaction with a different isolation level. This non-lenient mode also rejects read-only mismatches (that is, an inner read-write transaction that tries to participate in a read-only outer scope).
기본적으로, 참여하는 트랜잭션은 외부 범위의 특성을 따르며, 로컬의 격리 수준, 타임아웃 값 또는 읽기 전용 플래그(있는 경우)를 조용히 무시합니다.
만약 다른 격리 수준을 가진 기존 트랜잭션에 참여할 때 격리 수준 선언이 거부되도록 하려면, 트랜잭션 관리자에서 validateExistiing Transactions 플래그를 true로 변경하는 것을 고려해야 합니다.
이러한 비관용(non-lenient) 모드에서는 읽기 전용 플래그 불일치도 거부됩니다. (즉, 내부의 읽기-쓰기 트랜잭션이 읽기 전용 외부 범위에 참여하려고 할 경우 이를 허용하지 않습니다.)
추가 설명
Spring에서 트랜잭션이 중첩될 때, 내부 트랜잭션(참여하는 트랜잭션)은 외부 트랜잭션의 설정을 따릅니다.
즉, 내부 트랜잭션이 별도로 설정한 격리 수준(isolation level), 타임아웃(timeout), 읽기 전용(read-only) 설정은 무시됩니다.
When the propagation setting is PROPAGATION_REQUIRED, a logical transaction scope is created for each method upon which the setting is applied. Each such logical transaction scope can determine rollback-only status individually, with an outer transaction scope being logically independent from the inner transaction scope. In the case of standard PROPAGATION_REQUIRED behavior, all these scopes are mapped to the same physical transaction. So a rollback-only marker set in the inner transaction scope does affect the outer transaction’s chance to actually commit.
전파 설정이 PROPAGATION_REQUIRED일 때, 해당 설정이 적용된 각 메서드마다 논리적 트랜잭션 범위(logical transaction scope)가 생성됩니다.
이러한 각 논리적 트랜잭션 범위는 개별적으로 롤백 전용(rollback-only) 상태를 결정할 수 있으며, 외부 트랜잭션 범위는 내부 트랜잭션 범위와 논리적으로 독립적입니다. 표준 PROPAGATION_REQUIRED 동작의 경우, 이 모든 논리적 트랜잭션 범위는 동일한 물리적 트랜잭션(physical transaction)과 매핑됩니다.따라서 내부 트랜잭션 범위에서 롤백 전용 마커(rollback-only marker)가 설정되면,이는 외부 트랜잭션이 실제로 커밋될 수 있는 가능성에 영향을 미칩니다.
추가 설명
PROPAGATION_REQUIRED를 사용하면, 내부와 외부 트랜잭션이 논리적으로 독립적이어도
실제로는 같은 물리적 트랜잭션을 공유합니다. 그래서 내부 트랜잭션에서 rollback-only 가 설정되면, 결국 외부 트랜잭션도 롤백됩니다.
However, in the case where an inner transaction scope sets the rollback-only marker, the outer transaction has not decided on the rollback itself, so the rollback (silently triggered by the inner transaction scope) is unexpected. A corresponding UnexpectedRollbackException is thrown at that point. This is expected behavior so that the caller of a transaction can never be misled to assume that a commit was performed when it really was not. So, if an inner transaction (of which the outer caller is not aware) silently marks a transaction as rollback-only, the outer caller still calls commit. The outer caller needs to receive an UnexpectedRollbackException to indicate clearly that a rollback was performed instead.
그러나 내부 트랜잭션 범위에서 rollback-only 마커를 설정하는 경우, 외부 트랜잭션은 자신이 직접 롤백을 결정한 것이 아니기 때문에,
내부 트랜잭션 범위에 의해 조용히(triggered silently) 롤백이 발생하면 예상치 못한 상황이 됩니다.
이 시점에서 UnexpectedRollbackException(예기치 않는 롤백 예외)가 발생합니다.
이러한 동작은 의도된(expected) 동작이며, 그 이유는 트랜잭션을 호출한 측(외부 트랜잭션의 호출자)이 트랜잭션이 실제로 커밋된 것으로 오해하는 것을 방지하기 위해서입니다.
즉, 외부 호출자는 내부 트랜잭션이 존재하는지 인식하지 못한 상태에서, 내부 트랜잭션이 조용히 rollback-only로 설정했을 수도 있습니다. 이 경우, 외부 호출자는 여전히 commit을 호출하지만, 실제로는 롤백이 수행되었으므로 Spring은 이를 분명하게 알리기 위해 UnexpectedRollbackException을 발생시킵니다.
추가 설명
외부 트랜잭션(바깥쪽 트랜잭션)에서 내부 트랜잭션(안쪽 트랜잭션)이 별도로 rollback-only를 설정한 사실을 모릅기 때문에 Spring에서 UnexpectedRollbackException을 발생시킵니다.
Understanding PROPAGATION_REQUIRES_NEW

PROPAGATION_REQUIRES_NEW는 PROPAGATION_REQUIRED와 달리, 영향을 받는 각 트랜잭션 범위마다 항상 독립적인 물리적 트랜잭션을 사용하며, 외부 범위의 기존 트랜잭션에 절대 참여하지 않습니다.
이러한 방식에서는 기본적으로 사용되는 리소스 트랜잭션이 서로 다르므로, 각 트랜잭션은 독립적으로 커밋되거나 롤백될 수 있으며,
내부 트랜잭션이 롤백되더라도 외부 트랜잭션에는 영향을 미치지 않습니다. 또한, 내부 트랜잭션이 완료되면 즉시 해당 트랜잭션의 락이 해제됩니다. 이러한 독립적인 내부 트랜잭션은 자신만의 격리 수준(isolation level), 타임아웃(timeout), 읽기 전용(read-only) 설정을 선언할 수 있으며, 외부 트랜잭션의 특성을 상속받지 않습니다.
The resources attached to the outer transaction will remain bound there while the inner transaction acquires its own resources such as a new database connection. This may lead to exhaustion of the connection pool and potentially to a deadlock if several threads have an active outer transaction and wait to acquire a new connection for their inner transaction, with the pool not being able to hand out any such inner connection anymore. Do not use PROPAGATION_REQUIRES_NEW unless your connection pool is appropriately sized, exceeding the number of concurrent threads by at least 1.
외부 트랜잭션에 연결된 리소스들은 그대로 해당 트랜잭션에 묶여 있는 상태를 유지하는 반면, 내부 트랜잭션은 새로운 데이터베이스 연결과 같은 자신만의 리소스를 확보합니다.
이로 인해 커넥션 풀(connection pool)의 고갈(exhaustion)이 발생할 수 있으며, 특히 여러 스레드가 활성화된 외부 트랜잭션을 가지고 있고, 내부 트랜잭션을 위해 새로운 연결을 얻으려고 대기하는 상황에서는 데드락(deadlock)이 발생할 가능성이 있습니다.
만약 커넥션 풀이 이러한 내부 연결을 더 이상 제공할 수 없으면, 트랜잭션이 진행되지 못하고 교착 상태에 빠질 수 있습니다.
따라서 PROPAGATION_REQUIRES_NEW를 사용할 경우, 커넥션 풀이 충분히 크도록 설정해야 하며, 최소한 동시 실행되는 스레드 수보다 1개 이상 여유가 있도록 크기를 조정해야 합니다.
Understanding PROPAGATION_NESTED
PROPAGATION_NESTED uses a single physical transaction with multiple savepoints that it can roll back to. Such partial rollbacks let an inner transaction scope trigger a rollback for its scope, with the outer transaction being able to continue the physical transaction despite some operations having been rolled back. This setting is typically mapped onto JDBC savepoints, so it works only with JDBC resource transactions. See Spring’s DataSourceTransactionManager.
PROPAGATION_NESTED는 하나의 물리적 트랜잭션(physical transaction)을 사용하며, 여러 개의 세이브포인트(savepoint)를 설정하여 특정 시점으로 롤백할 수 있습니다.
이러한 부분 롤백(partial rollback)을 통해, 내부 트랜잭션 범위에서 해당 범위에 대한 롤백을 수행할 수 있으며, 일부 작업이 롤백되었더라도 외부 트랜잭션은 물리적 트랜잭션을 계속 진행할 수 있습니다.
이 설정은 일반적으로 JDBC 세이브포인트(JDBC savepoints)에 매핑되므로, JDBC 리소스 트랜잭션(JDBC resource transactions)에서만 동작합니다.
Transaction Propagation :: Spring Framework
PROPAGATION_REQUIRES_NEW, in contrast to PROPAGATION_REQUIRED, always uses an independent physical transaction for each affected transaction scope, never participating in an existing transaction for an outer scope. In such an arrangement, the underlying re
docs.spring.io
'공식문서' 카테고리의 다른 글
Garbage Collector #2 (0) | 2025.03.17 |
---|---|
[Spring Docs] DispatchServlet #1 (0) | 2025.03.15 |
Garbage Collector #1 (0) | 2025.03.09 |
[Spring Docs] Transaction Management #1 (0) | 2025.03.07 |
Servlet (0) | 2025.03.07 |