Spring Multi Datasource

Hoo-Sung.Lee·2024년 1월 13일
0

Database

목록 보기
4/18
post-thumbnail

Datasource와 Transaction을 구성하는 여러 가지 방법이 있지만, 두 가지를 소개하겠습니다.

1. 단일 Datasource + Transaction
하나의 Transaction에 Datasource를 하나 포함하는 가장 기본적인 구성입니다.

2. 복수 Datasource + 단일 Transaction

이번 포스팅에서는 2번 방식으로 설정을 해보겠습니다.

  • Master : Insert, Update, Delete
  • Slave: Select
    실제 운영 환경에서는 DB작업이 위와같이 분산되어 있는 경우가 많다고 합니다.

본 글에서는 다룰 내용들은 아래와 같습니다.

  • AbstractRoutingDataSource
  • LazyConnectionDataSourceProxy
  • ChainedTransactionManager

1. AbstractRoutingDataSource

AbstractRoutingDataSource는 복수의 DataSource를 제어하게끔 도와주는 DataSource 인터페이스입니다.

  • target으로 하는 DataSource들을 Map형태로 가지고 있습니다.
  • defaultTargetDataSource는 RO,RW 노드중에 RW 노드로 설정하였습니다.

이 인터페이스를 구현해서 SoccerRoutingDataSource를 만들어 보았습니다.

determineCurrentLookupKey를 override해줘야합니다.
이 메소드는 직접적으로 routing을 담당하는 중요한 역할을 하는 핵심입니다.

이런식으로 @Transactional(readOnly = true)같은 속성을 보고, 분기를 결정해주는 역할을 하는 핵심이라고 볼 수 있습니다.


2. LazyConnectionDataSourceProxy

AbstractRoutingDataSource를 구현한 SoccerRoutingDataSource로 DB 분기를 제대로 적용하기 위해서는

LazyConnectionDataSourceProxy를 이용하여 현재 RoutingDataSource를 감싸서 구현 가능합니다. 이렇게 해야하는 이유는 아래와 같습니다.

  1. Spring은 기본적으로 트랜잭션을 시작할 때 쿼리가 실행되기도 전에 DataSource를 정해놓습니다.
    (TransactionManager 식별 -> DataSource에서 Connection을 가져오고 -> Transaction 동기화(synchronization))
  2. Transaction이 시작되면 같은 DataSource만을 사용합니다.

따라서 쿼리 메소드에 AOP로 DataSource를 정하는 로직이 가능하게 하려면 쿼리를 실행할 때 DataSource를 정할 수 있도록
DataSource 연결을 늦춰야 합니다. 따라서 LazyConnectionDataSourceProxy를 이용해서 현재 RoutingDataSource를 감싸야 하는 것이었습니다...!

TransactionManager 식별 -> LazyConnectionDataSourceProxy에서 Connection proxy 객체 획득 -> transaction 동기화(synchronization) -> 실제 쿼리 호출 시에 RoutingDataSource.determineCurrentLookupKey() 호출이 일어나게 됩니다...!


3. ChainedTransactionManager

두 개 이상의 데이터베이스 트랜잭션을 연결해야 하는 경우에 사용한다고 합니다.(사실 정말 트랜잭션을 묶어야 하는 것인지 생각을 해봐야 합니다. 분리할 수 있다면, 어플리케이션 레벨에서 트랜잭션을 보장해주는 방법도 고려해봐야 합니다.)

ChainedTransactionManager는 commit과 rollback을 마지막 요소부터 처리합니다.

즉, 등록한 트랜잭션 매니저를 역순으로 commit하고 있습니다. 그렇기 때문에 실패할 경우 모두 rollback할 수 있도록 트랜잭션 매니저 순서를 잘 정해서 등록해야 합니다.

코드 중간에 보면

이렇게 적혀져 있습니다.

역순으로 반복문을 돌리면서 커밋을 하는데, 중간에 실패한 경우 남아 있는(아직 커밋하지 않은) Transaction에 대해서만 rollback처리를 한다는 이야기입니다.

예를 들어 T1, T2, T3순서로 등록되었다고 했을 때,
1. T3 fail-> T3, T2, T1모두 rollback
2. T3 commit, T2 fail -> T2, T1만 rollback, T3 commit

위와 같은 상황이 벌어질 수 있는데, 특히 2번의 경우, T3가 rollback되어야 함에도 불구하고, commit되었습니다. 이렇게 일부 데이터가 등록되는 상황이 벌어지게 됩니다.

그런 이유에서인지
ChainTransactionManager는 현재는 deprecated되었습니다...


생각해볼 거리

정말 분리할 수 없는 업무 단위(트랜잭션) 인가?
되도록 ChainTransaction을 적용하지 말고, DB Transaction들을 연결하지 않고, 어플리케이션 레벨에서 트랜잭션을 보장해줄수 있는 방식이 좋을거 같아요!!!😃

profile
Working towards becoming Backend-Developer

0개의 댓글