Rlog

어떻게 Service Layer 간 Transaction Session 을 공유할 수 있을까? 본문

Spring

어떻게 Service Layer 간 Transaction Session 을 공유할 수 있을까?

dev_roach 2022. 6. 6. 16:59
728x90

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

 

스프링 DB 1편 - 데이터 접근 핵심 원리 - 인프런 | 강의

백엔드 개발에 필요한 DB 데이터 접근 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., - 강의

www.inflearn.com