Back-end/Spring Boot, JPA

[땅굴 조사] JPA 트랜잭션이 수행되기까지

philo0407 2024. 2. 1. 14:03

사진: Unsplash 의 Iva Rajović

조사 계기, 트랜잭션 발원지에 대한 궁금증

@EventListener / @TransactionalEventListener (+ @Async 유무)에서 트랜잭션의 동작에 대해 알아보고 있었다

그러가다 아래 트랜잭션 로그의 발원지가 궁금해졌다

 

로그를 따라가다 보니 친숙한 얼굴들이 보였다

 

 

그만 조금만 보고 나온다는게, 끝을 봐보자는 마음으로 살펴보게 되었다

 

 

사전 지식

나는 이미 어느 정도 코드의 내막을 알았지만 처음 본 사람은 어려울테니, 알게된 내용을 미리 정리했다
이걸 보고 들어가면 코드를 읽어나가기 좀 더 수월할 것이다!

 

 

TransactionAspectSupport  (org.springframework.transaction.interceptor)

스프링의 AOP 기능으로 트랜잭션을 처리해주는 역할

실제 트랜잭션 매니징 처리는 

Spring Web(Sync, Tomcat)의 경우는 PlatformTransactionManager에게

Reactive (Async, Netty)의 경우는 ReactiveTransactionManager에게

전략패턴으로 위임한다

 

 

AbstractPlatformTransactionManager  (org.springframework.transaction.support)

인터페이스인 PlatformTransactionManager보다 더 구체적인 역할을 하고 실제로 트랜잭션 매니징을 하는 역할.

각 구현체(Hibernate등)는 이것을 일부 커스텀하게 구현할 수 있다

 

 

JpaTransactionManager  (org.springframework.orm.jpa)

AbstractPlatformTransactionManager의 구현체. JPA와 관련된 트랜잭션 처리 로직을 담당한다

 

 

TransactionImpl  (org.hibernate.engine.transaction.internal

트랜잭션과 관련된 오브젝트 .commit, rollback 등을 수행

 

 

SessionImpl  (org.hibernate.internal)

엔티티 매니저의 구현체. 엔티티 매니저라고 봐도 된다.

엔티티 매니저 관련 로직을 볼때 이 클래스를 보면 된다!

 

 

ActionQueue (org.hibernate.engine.spi)

(번역) 객체 삽입, 업데이트 및 삭제는 참조 무결성을 존중하기 위해 올바른 순서로 이루어져야 하므로 목록 의미가 있습니다

JPA에서 발생한 DML 이벤트 (insert, update, delete) 등을 보관하는 큐 객체이다

내부적으로 아래 필드들을 갖고 있다

 

해당 필드는 List of EntityAction로 구성되어 있는데, EntityAction의 구현체는 Command  DML로 되어있다

 

AbstractFlushingEventListener  (org.hibernate.event.internal)

JPA의 DML Flush(쓰기 지연소에 반영)을 처리 이벤트가 발생할 때 수신하는 이벤트객체

구현체로 

DefaultFlushEventListener

DefaultDirtyCheckEventListener

DefaultAutoFlushEventListener

-가 있다

 

 

DefaultFlushEventListener  (org.hibernate.event.internal)

JPA에서 Update 발생시 수신하는 이벤트 처리 객체

 

 

EventListenerGroupImpl  (org.hibernate.event.service.internal)

JPA의 DML Action을 처리한 이벤트 리스너들을 담고 있는 그룹

 

 

JdbcCoordinator  (org.hibernate.engine.jdbc.spi)

하이버네이트에서 사용하기 위한 JDBC관련된 동작을 정의

 

 

JdbcResourceLocalTransactionCoordinatorImpl (org.hibernate.resource.transaction.backend.jdbc.internal)

단일 트랜잭션 관리를 하기 위한 역할

TransactionCoordinator 인터페이스를 구현했고,

이것의 구현체는 위 클래스와 분산 트랜잭션 관리 역할인 JtaTransactionCoordinatorImpl가 있다

 

 

 

땅굴 조사

헤헤...

 

실제로 디버거를 타고 내려가는 과정은 그렇게 쉽지 않았다

 

거시적으로 보면 아래와 같이 분기처리를 탄다

 

코드로 보면 아래와 같다

 

 

메서드 호출 스택을 적으면서 코드로 보면 아래와 같다

 

TransactionAspectSupport .invokeWithinTransaction → commitTransactionAfterReturning →

AbstractPlatformTransactionManager .commit → processCommit →

JpaTransactionManager .doCommit → 

TransactionImpl .commit

JdbcResourceLocalTransactionCoordinatorImpl .commit →

JdbcResourceLocalTransactionCoordinatorImpl .beforeCompletionCallback →

JdbcCoordinatorImpl . beforeTransactionCompletion →

SessionImpl .beforeTransactionCompletion

flushBeforeTransactionCompletion → managedFlush → doFlush

EventListenerGroupImpl .fireEventOnEachListener

DefaultFlushEventListener .onFlush →

AbstractFlushingEventListener .performExecutions →

ActionQueue .executeActions() → executeActions(ExecutableList<E>) →

AbstractEntityPersister .update →

UpdateCoordinatorStandard . performUpdate → doStaticUpdate

(이곳부터 정신을 잃기 시작했다… 의미를 알 수 없는 급하게 작성한 득한 느낌의 냄새가 풍기는 코드들이 등장하기 시작했다)

 

예외 발생의 경우

TransactionAspectSupport .invokeWithinTransaction →

completeTransactionAfterThrowing → rollback → processRollback → doRollback →

TransactionImpl .rollback → …

 

 

각 객체간 위임 흐름도

구체적으로 클래스를 나열해보면 아래 그림과 같이 끝없는 위임을 연속한다

 

그나마 간소화해서 표현하면 아래와 같다

 

더 요약하면!!

@Transactional에 의해 AOP가 걸리면 CGLib에 의해 TransactionAspectSupport가 호출되고

트랜잭션 매니저에게 위임한다.

그리고 Transaction 객체에 commit을 맡긴 후 Transaction 조정자에게 위임,

그리고 EntityManger에게 트랜잭션 처리를 위임해서 flush 단계까지 간다

이벤트 리스너 그룹에게 이벤트 처리를 맡긴다

단순 Update의 경우 DefaultFlushEventListener 가 맡아서 처리한다

그리고 ActuionQueue가 DML 이벤트 큐를 받아서 처리한다 (이 경우 update)

 

느낀점

정말 보면서 느낀거지만 Spring은 정돈 되지 않은 코드들은 있지만 문서화가 정말 잘 되어있다

Hibernate는 스프링만큼의 문서화가 잘 되어있지 않은 것 같다는 인상을 받았다

확장성도 조금 빈약하다는 생각이 들었다. 인터페이스와 구현 클래스들의 관계가 명확하지 않다는 인상을 받았다

HIbernate가 천천히 발전하는 이유가 이것 때문이 아닐까 ...

코드 베이스를 이해하는 사람들이 많아지면서 오픈 소스 컨트리뷰터들이 앞으로 적어지지 않을까 싶은 걱정이 있다

 

 

참고

같이 읽어보면 좋을 내용 ㅎ

(작성한 내용과 다른 부분을 다룬 내용들도 있음)

 

Spring Data JPA 기본 구조와 동작원리

hibernate 코드로 이해하는 Spring Data JPA | Spring Data JPA(이하 스프링 JPA)는 Spring Data의 모듈로 JPA 기반의 데이터 접근 레이어(Data Access Layer)를 편하게 구현할 수 있도록 지원하는 프레임워크이다. 관계

brunch.co.kr

 

영한님 책에서 볼 수 없는 JPA (Hibernate) 내부 코드 살펴보기

몇 달 전 JPA를 공부를 하는데 책에서 말하는 엔티티매니저, 쓰기지연저장소 등등 이런 용어들은 코드로 어떻게 되어있을까 궁금해져 JPA 관련 검색을 해보니 대부분 글들이 영한님의 PDF 강의 자

dding9code.tistory.com

 

Spring Boot JPA 1차 캐시 정리

개요 우리는 JPA를 사용하며, 어떠한 이점을 누릴 수 있는지에 대한 질문을 받는다면, 1차 캐시를 빼고 말하기 어렵습니다. "1차 캐시가 어떻게 동작하는데??"라고 질문받는다면, "영속성 콘텍스트

dingdingmin-back-end-developer.tistory.com