Home TransactionManager의 종류는 어떤 것들이 있을까?
Post
Cancel

TransactionManager의 종류는 어떤 것들이 있을까?

1. 글을 작성하게 된 계기


사람들과 스프링 서버를 만들어보는 프로젝트를 진행하며 Transactional의 동작원리에 대해 질문받았습니다. 이를 잘 이해하기 위해서는 TransactionManager에 대해 한 번 정리해야겠다는 생각이 들어 글을 작성하게 되었습니다.

image







2. TransactionManager


TransactionManager는 트랜잭션 관리를 추상화하는 인터페이스입니다.

Marker interface for Spring transaction manager implementations, either traditional or reactive.





이는 마커 인터페이스로, 그 자체로서는 아무런 역할을 하지 않습니다.

1
2
public interface TransactionManager {
}





마커 인터페이스는 디자인 패턴 중 하나로, 빈 인터페이스를 말합니다. 자체로서는 아무 역할도 하지 않지만, 이를 사용해 상속 계층도를 만들고, 해당 객체에 특정한 성질이 있다는 것을 알릴 수 있습니다. 스프링에서는 마커 인터페이스를 나타내기 위해 어노테이션을 사용하기도 하는데, 이를 통해 바이트 코드에서도 메타데이터를 제공할 수 있기 때문입니다.

The marker interface pattern is a design pattern in computer science, used with languages that provide run-time type information about objects. It provides a means to associate metadata with a class where the language does not have explicit support for such metadata.







TransactionManager의 상속 계층도는 다음과 같습니다. 이에 대해 조금 더 상세하게 살펴보겠습니다.

image

해당 글은 JPA는 배제하고 Jdbc를 기준으로 설명하겠습니다.







2-1. PlatformTransactionManager

PlatformTransactionManager는 트랜잭션 관리를 위한 공통 인터페이스 입니다.

This is the central interface in Spring’s imperative transaction infrastructure. Applications can use this directly, but it is not primarily meant as an API: Typically, applications will work with either TransactionTemplate or declarative transaction demarcation through AOP.





이를 통해 JDBC, JPA, JTA 등과 같은 특정 기술이나 구현체에 종속되지 않고 기술/구현체를 자유롭게 변경 할 수 있습니다.

1
2
3
4
5
6
7
8
public interface PlatformTransactionManager extends TransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
        throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}

정의된 메서드를 보면, 모든 트랜잭션에서 공통으로 사용되는 메서드만 정의된 것을 볼 수 있습니다.





구현체들을 간략히 살펴보면, Jdbc 기반 라이브러리를 사용하는 경우, 구현체로 DataSourceTransactionManager를, Hibernate를 사용하는 경우 HibernateTransactionManager를, JPA는 JpaTransactionManager를 사용합니다.

  1. Jdbc: DataSourceTransactionManager
  2. Hibernate: HibernateTransactionManager
  3. JPA: JpaTransactionManager







스프링에서는 이는 직접 사용하기보다는 TransactionTemplate 또는 AOP를 통해 사용할 것을 권장하고 있습니다.

Applications can use this directly, but it is not primarily meant as an API: Typically, applications will work with either TransactionTemplate or declarative transaction demarcation through AOP.





다음과 같이요. 스프링에는 프로그래밍적 트랜잭션(Programmatic transaction)선언적 트랜잭션(Declarative transaction) 이 존재하는데, 이에 대해서는 별도로 학습해 보실 것을 권장해 드립니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
public class ExampleService {

    private final PlatformTransactionManager transactionTemplate;

    public ExampleService(PlatformTransactionManager transactionManager) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }

    public Object invoke() {
        return transactionTemplate.execute(status -> {
            // 비즈니스 처리
            return result;
        });
    }
}
1
2
3
4
5
6
7
8
9
@Service
public class ExampleService {

    @Transactional
    public void invoke() {
        // 비즈니스 처리
    }
}







2-2. AbstractPlatformTransactionManager

AbstractPlatformTransactionManager는 스프링에서 명령형 트랜잭션 인프라의 핵심적 인터페이스입니다.

Abstract base class that implements Spring’s standard transaction workflow, serving as basis for concrete platform transaction managers like JtaTransactionManager.





이는 PlatformTransactionManager가 제공하는 기능 외에도, 트랜잭션 관리를 위한 공통 로직과 트랜잭션 동기화, 전파 정책을 처리하는 추가/구체적인 기능 을 제공합니다.

  1. determines if there is an existing transaction.
  2. applies the appropriate propagation behavior.
  3. suspends and resumes transactions if necessary.
  4. checks the rollback-only flag on commit.
  5. applies the appropriate modification on rollback (actual rollback or setting rollback-only).
  6. triggers registered synchronization callbacks (if transaction synchronization is active).





이를 통해 트랜잭션 매니저를 보다 쉽게 구현/확장 할 수 있도록 합니다. 즉, PlatformTransactionManager가 제공하는 메서드 뿐 아니라, 트랜잭션 전파, 동기화, 타임아웃 과 같은 추가적인 기능을 제공하며, AbstractPlatformTransactionManager의 하위 구현체는 이런 기능을 공통으로 사용할 수 있어, 트랜잭션 관리 기능의 확장성과 재사용성을 높일 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
    
    ......

    // PlatformTransactionManager를 구현 상속해 조금 더 구체적인 구현을 제공
    @Override
    public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
        throws TransactionException {

        // Use defaults if no transaction definition given.
        TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());

        Object transaction = doGetTransaction();
        boolean debugEnabled = logger.isDebugEnabled();

        ......
        
        else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
            ......
    }
        else

    {
            ......
        return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
    }

    // 구체적인 추가 구현 메서드 제공
    protected void prepareSynchronization(
        DefaultTransactionStatus status,
        TransactionDefinition definition
    ) {
        if (status.isNewSynchronization()) {
            TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
            TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
                definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
                    definition.getIsolationLevel() : null);
            TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
            TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
            TransactionSynchronizationManager.initSynchronization();
        }
    }

    ......

    protected int determineTimeout(TransactionDefinition definition) {
        if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
            return definition.getTimeout();
        }
        return getDefaultTimeout();
    }
        
    ......

}







2-3. ResourceTransactionManager

ResourceTransactionManager는 PlatformTransactionManager 인터페이스의 확장으로, 단일 대상 리소스에서 동작하는 네이티브 리소스 트랜잭션 관리자 입니다.

1
2
3
public interface ResourceTransactionManager extends PlatformTransactionManager {
    Object getResourceFactory();
}

Extension of the PlatformTransactionManager interface, indicating a native resource transaction manager, operating on a single target resource.







단일 대상 리소스란, 하나의 데이터베이스, 또는 하나의 메시지 큐등 단일 리소스 대상 의 트랜잭션을 말합니다.

1
2
3
4
5
6
7
8
9
10
11
12
@Service
@RequiredArgsConstructor
class MemberService {

    private final MySQL mySQL;

    @Transactional
    public void signUp(Member member) {
        mySQL.save(member);
    }
}







반대로 JTA(Java Transaction API) 는 여러 다른 타입의 리소스에 걸친 트랜잭션을 관리합니다. 다양한 리소스에 걸친 트랜잭션이란, 한 트랜잭션 내에서 데이터베이스, 메시지 큐 등 다양한 리소스에 대한 트랜잭션을 하나의 트랜잭션으로 관리하는 분산 트랜잭션을 말합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
@RequiredArgsConstructor
class MemberService {

    private final MySQL mySQL;
    private final MongoDb mongoDb;
    private final MessageQueue messageQueue;

    @Transactional
    public void signUp(Member member) {
        Member newMember = mySQL.save(member);
        mongoDb.save(new MemberDocument(newMember));
        messageQueue.sendMessage(new Message(newMember));
    }
}







이는 다음과 같은 구현체를 가집니다.

image







2-4. DataSourceTransactionManager

DataSourceTransactionManager는 Jdbc DataSource를 사용하는 데이터 액세스의 트랜잭션을 관리합니다. 이는 Jdbc 커넥션을 가지고 DataSource를 통해 작업합니다.

PlatformTransactionManager implementation for a single JDBC DataSource. This class is capable of working in any environment with any JDBC driver, as long as the setup uses a javax.sql.DataSource as its Connection factory mechanism. Binds a JDBC Connection from the specified DataSource to the current thread, potentially allowing for one thread-bound Connection per DataSource.







내부 구현을 보더라도 DataSource를 가지고 이를 통해 작업하는 것을 볼 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@SuppressWarnings("serial")
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean {

    @SuppressWarnings("serial")
    public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
        implements ResourceTransactionManager, InitializingBean {

        @Nullable
        private DataSource dataSource;

        private final boolean enforceReadOnly = false;


        ......

        public DataSourceTransactionManager(DataSource dataSource) {
            this();
            setDataSource(dataSource);
            afterPropertiesSet();
        }
        
        ......

        @Override
        public void afterPropertiesSet() {
            if (getDataSource() == null) {
                throw new IllegalArgumentException("Property 'dataSource' is required");
            }
        }


        @Override
        public Object getResourceFactory() {
            return obtainDataSource();
        }

        @Override
        protected Object doGetTransaction() {
            ......
            return txObject;
        }

        @Override
        protected boolean isExistingTransaction(Object transaction) {
            ......
        }

        @Override
        protected void doBegin(Object transaction, TransactionDefinition definition) {
            ......
        }

        ......

        @Override
        protected void doCommit(DefaultTransactionStatus status) {
            ......
        }

        @Override
        protected void doRollback(DefaultTransactionStatus status) {
            ......
        }
    }
}





추가로 스프링 5.3부터 JdbcTransactionManager가 등장했는데, 이는 DataSourceTransactionManager를 상속받아 추가적인 기능을 제공합니다. 이에 대한 설명은 해당 댓글을 참조해 주세요.

The only difference is exception translation: Like with JpaTransactionManager, there is DataAccessException translation on commit here. Where DataSourceTransactionManager will only ever throw TransactionSystemException on commit, JdbcTransactionManager is more sophisticated and translates locking failures etc to corresponding DataAccessException subclasses. Note that calling code needs to be prepared for that, not exclusively expecting TransactionSystemException in such scenarios. That’s the main reason why those are two separate transaction manager classes to choose from.







2-5. TransactionSynchronizationManager

TransactionSynchronizationManager는 현재 쓰레드에 대한 트랜잭션 동기화, 트랜잭션 상태 정보 관리리소스 바인딩, 트랜잭션과 관련된 콜백 실행 등을 담당합니다.

Central delegate that manages resources and transaction synchronizations per thread. To be used by resource management code but not by typical application code.







이는 쓰레드 로컬(ThreadLocal)을 사용해 동기화 작업을 하며, 이를 통해 동시성 문제로부터 안전할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class TransactionSynchronizationManager {

    private static final ThreadLocal<Map<Object, Object>> resources =
        new NamedThreadLocal<>("Transactional resources");

    private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
        new NamedThreadLocal<>("Transaction synchronizations");

    private static final ThreadLocal<String> currentTransactionName =
        new NamedThreadLocal<>("Current transaction name");

    private static final ThreadLocal<Boolean> currentTransactionReadOnly =
        new NamedThreadLocal<>("Current transaction read-only status");

    private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
        new NamedThreadLocal<>("Current transaction isolation level");

    private static final ThreadLocal<Boolean> actualTransactionActive =
        new NamedThreadLocal<>("Actual transaction active");

    ......

}







트랜잭션에 관한 여러 정보는 여러 쓰레드 로컬에 나누어 저장됩니다. 각 쓰레드 로컬의 역할은 다음과 같습니다.

  1. resources: 현재 쓰레드와 관련된 트랜잭션 리소스를 저장합니다.
  2. synchronizations: 트랜잭션은 beforeCommit, readOnly, afterCommit 등의 상태를 가지는데, 이에 대한 정보를 저장합니다.
  3. currentTransactionName: 각 트랜잭션은 이름을 가지며, 이름을 저장합니다.
  4. currentTransactionReadOnly: 각 트랜잭션의 읽기 속성에 대한 정보를 저장합니다.
  5. currentTransactionIsolationLevel: 각 트랜잭션의 격리수준에 대한 정보를 저장합니다.
  6. actualTransactionActive: 커밋/롤백 여부에 따른 트랜잭션 활성화에 대한 정보를 저장합니다.







3. 정리


스프링의 TransactionManager에 대해 살펴보았습니다. 스프링은 TransactionManager를 통해 상속 계층도를 만들어 트랜잭션을 추상화하고, 일관되게 적용할 수 있게 해줍니다. 따라서 어떤 트랜잭션 매니저가 존재하는지, 이를 통해 어떻게 트랜잭션을 일관되게 관리하는지에 대해 정리하고, 이를 통해 트랜잭션을 조금 더 잘 사용할 수 있도록 학습해 볼 것을 권장해 드립니다.


This post is licensed under CC BY 4.0 by the author.

Config 서버

Transaction의 동작 과정은 어떻게 될까?