ebson

[Spring boot, Mybatis, ChainedTransactionManager ] 분산 데이터베이스 트랜잭션 처리하기 본문

HANDS-ON

[Spring boot, Mybatis, ChainedTransactionManager ] 분산 데이터베이스 트랜잭션 처리하기

ebson 2023. 2. 23. 16:33

대형 서비스에서 데이터베이스를 여러대로 분산해 효용성을 높이고 규모를 조절하는 것은 일반적인 방법이다. 그리고 데이터베이스당 트랜잭션 처리가 이루어지는 특성상 한 기능단위에서 2개 이상 데이터 소스에 접근하고자 할 때 스프링에서 별도의 작업을 취하지 않으면, 한개의 데이터 소스로만 commit 되고 다른 데이터 소스에 대한 commit이 누락될 수 있다. 즉 A 데이터 소스와 B 데이터 소스에 대한 스프링의 트랜잭션 매니저들 중 하나만 동작한다는 것이다.

 

이때는 스프링에서 제공하는 ChainedTransactionManager를 적용해 A, B 데이터 소스에 대한 트랜잭션이 모두 동작하도록 할 수 있다.

위 클래스의 동작 방식은 공식 문서에 따르면, 생성자의 인자로 전달받은 트랜잭션 매니저를 순서대로 처리하고 처리 중 문제가 생기면 역순으로 롤백하는 것이다. 그러므로 이 클래스를 사용할 때에는 가장 문제가 생길 가능성이 큰 트랜잭션에 대한 트랜잭션매니저를 마지막 순서에 위치시켜야 한다. 특정 트랜잭션이 롤백되면 기본적으로 이전에 수행되었던 다른 트랜잭션들을 같이 롤백시키기 때문이다.

 

ChainedTxConfig.java

@Configuration
public class ChainedTxConfig {
	
	/**
	 * By Using the ChainedTransactionManager, you can manage a task using multiple transactions which SEPARATELY access dataSources(which are DIFFERENT dataSources).
	 * It processes transaction managers in order and commit or rollback in reverse. the PlatformTransactionManager rollbacked other transactions if it encounter an Exception(failed).
	 * !! So, you should examine which transaction manager is most likely to fail and place it last order. !!
	 * ChainedTransactionManager does not guarantee that all transactions are rollbacked when one is failed.
	 * */
	
    @SuppressWarnings("deprecation")
	@Bean(name = "chainedTransactionManager_RollbackAll_IfAccTxMgFailed")
    public PlatformTransactionManager chainedTransactionManager1(@Qualifier("mgrTransactionManager") PlatformTransactionManager mgrTransactionManager
    														, @Qualifier("accTransactionManager") PlatformTransactionManager accTransactionManager) {
        return new ChainedTransactionManager(mgrTransactionManager, accTransactionManager);
    }
    
    @SuppressWarnings("deprecation")
	@Bean(name = "chainedTransactionManager_RollbackAll_IfMgrTxMgFailed")
    public PlatformTransactionManager chainedTransactionManager2(@Qualifier("mgrTransactionManager") PlatformTransactionManager mgrTransactionManager
    														, @Qualifier("accTransactionManager") PlatformTransactionManager accTransactionManager) {
        return new ChainedTransactionManager(accTransactionManager, mgrTransactionManager);
    }
}

 

@Transactional(value="...")

	@Transactional(value = "chainedTransactionManager_RollbackAll_IfAccTxMgFailed")
	public void executeTask(StepContribution contribution, ChunkContext chunkContext) throws Exception {
    	/***/
    }

 

ChainedTransactionManager에 대해서 한가지 유념할 만한 것은 만약 첫번째 인자로 주어진 트랜잭션 매니저에서만 트랜잭션 오류가 발생한다면 두번째 인자로 주어진 트랜잭션 매니저는 commit을 한 채로 첫번째 인자로 주어진 트랜잭션 매니저만 rollback을 하게 될 수 있다는 것이다. 그래서 데이터 정합성이 정말 중요한 작업인 경우에는 위 클래스를 사용하기보다 아예 애초에 트랜잭션을 분리해 작성하는 것을 추천한다. 그러나 조금만 주의를 기울이면 이와 같은 경우는 예방할 수 있다고 보인다. 

 

한편, 개발자가 의도적으로 주의를 기울일 필요 없이 위와 같은 단점을 보완하면서도 같은 용도로 사용할 수 있는 것으로 JtaTransactionManager 라는 것이 있다. ChainedTransactionManager 과 비교했을 때 공부해야할 양이 많은 것으로 보이지만 분산 데이터베이스 환경에서 트랜잭션 처리에 대해 관심이 있다면 위 클래스에 대해서 알아보는 것도 추천한다.

Comments