Spring 의 Transaction 을 사용하다 보면 이런 생각이 든적이 있을 것이다. 어떻게 Propagation 정책이 동작하는거지? Code 가 Generate 될때 Inline 함수처럼 해당 함수로 들어가는건 아닐 것 같은데? 어떻게 같은 Session 을 공유하는 걸까? 라는 생각이 들 것이다. 아래의 코드를 한번 보자. 김영한님의 스프링 DB 1편 강좌의 예시 코드 중 하나이다.
public void updateMoney(Connection conn, String memberId, int money) throws SQLException {
String sql = "update member set money = ? where member_id = ?";
PreparedStatement pstmt = null;
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, money);
pstmt.setString(2, memberId);
int resultSize = pstmt.executeUpdate();
log.info("Update Result Size = {}", resultSize);
} catch (SQLException e) {
log.error("DB Error ", e);
throw e;
} finally {
closeWithOutConnection(pstmt, null);
}
}
Connection 을 받아오는 이유는 다른 곳에서 열린 트랜잭션에 참가하기 위해서이다. 보통 자신이 트랜잭션을 여는 경우에는 Connection.getConnection() 메소드를 통해서 트랜잭션을 열게 된다. 이렇게 되면 메소드에서 계속해서 Connection 을 받아야 하는 문제들이 발생한다. 또한 만약 (Service A - aMethod) 의 내부에서 (Service B - bMethod) 를 호출하고 있는 상황에서 bMethod 가 aMethod 에서 열린 Transaction 과 같이 묶여야 한다면 어떻게 해야 할까? Propagation.REQUIRED 로 처리하면 될 것이다. 그런데 어떻게 bMethod 는 aMethod 와 같은 실행 메소드로 동작하는 걸까?
public void aMethod() {
tx.start();
//aMethod logic
bService.bMethod()
tx.commit();
}
위 처럼 inline 하게 작동하는 것일까? 사실 이건 불가능할 것이다. Spring 코드를 작성하다보면 DI 를 많이 쓰는데 이는 Compile 시점에 결정되는 것이 아닌 Runtime 시점에 Dependency Graph 가 완성되기 때문이다. 그렇다면 어떻게 작동하는 걸까? 토비의 스프링을 읽어봤다면 알겠지만 Spring 내부에서는 Transaction 을 Multi Platform Level 로 추상화 하여 이용하고 있다. 그래서 내부에서 TransactionSynchronizationManager 를 이용하여 Transaction 의 동기화를 관리한다.
그렇다면 어떻게 Transaction 을 전파하고 있을까? 보면 ThreadLocal 을 이용해서 해당 Thread 에서 동작하는 작업들은 같은 Transaction 사용을 보장함을 알 수 있다.
위의 메소드를 보면 현재 Transaction 에서 다른 Resource 를 실행시킬 수 있도록 도와주고 있음을 알 수 있다.
잡담
근데 여기서 궁금한게 하나 생겼는데 R2DB? 여튼 Reactive 한 쪽에서는 하나의 Thread 를 사용할 것 같지 않은데 이를 어떻게 보장하고 있을지 궁금했다. 코드를 까보기엔 귀찮았지만, 저번에 Decorator 를 통해서 Thread 의 Context 를 복사하는 방법을 알긴 알아서 그렇게 하겠구나... 얼핏 생각했다. 근데 문득 든 고민은 그렇게 Context 복사했을때 OverHead 가 적을까? 이런생각도 많이 들었다. 만약 코루틴이라면..? 어떻게 또 복사해줄까? Sleuth 코드를 까봤을땐 이전 Thread 정보를 받아왔던거 같은데.. 나중에 코드를 한번 까봐야겠다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard
'Spring' 카테고리의 다른 글
WebFlux 에서 ResponseBody 에 Mono, Flux 를 사용하는 이유 (0) | 2022.08.11 |
---|---|
Spring-Cloud-Sleuth 비동기 요청시 서버간 TraceId 보존하는 방법 (0) | 2022.04.27 |
오늘자 삽질 - Spring Kafka (1) | 2022.04.13 |
기존에 있던 Object 를 Bean 으로 등록하는 방법 (0) | 2022.04.05 |
[JPA] Transactional read only 일때 성능상 이점 (0) | 2022.03.21 |