[Spring] 비동기 환경에서 트랜잭션은 어떻게 동작할까?
- [ Backend ]/Spring
- 2023. 12. 8.
동기와 비동기
스프링에서는 @Async 어노테이션을 이용한 비동기 호출을 지원한다. 동기 호출은 일반적인 코드 흐름으로, 코드의 순서에 따라서 순차 처리되는 일반적인 플로우를 말한다. 반면 비동기 호출은 현재 실행중인 코드의 흐름과는 상관없이, 완료 여부를 확인하지 않고 다음 로직으로 넘어가는 것을 말한다.
(동기 시퀀스 다이어그램)
동기 호출의 예시인데, ServiceBus에서 메시지가 반환될때까지 TBJump 정보를 조회하는 플로우는 실행되지 않는다.
(비동기 시퀀스 다이어그램)
이번에는 비동기 호출의 예시이다. 처음 보낸 요청의 결과를 확인하지 않고, 두번째 로직을 실행하는 모습을 볼 수 있다.
동기 호출은 코드의 이해와 설계가 쉽지만, 많은 요청을 반복적으로 처리해야 하거나, 요청에 대한 반환이 늦어지는 등의 가능성이 있을 때 비동기를 사용하면 좋다. 대표적으로 데이터베이스나 파일시스템 처리와 같이 disk i/o로 인해서 시간이 많이 걸리는 작업은 비동기로 처리하는 것을 고려해볼만 하다.
스프링과 비동기
그렇다면 스프링에서 비동기는 어떻게 동작할까?
@Async 어노테이션을 사용하여 비동기 호출을 한 후, exception이 터져도 트랜잭션 롤백이 발생하지 않는다는 사실은 간단한 테스트코드를 통해 알 수 있었다. 또한, 비동기 호출 시 별도의 쓰레드에서 프로세스가 실행된다는 사실도 알 수 있었다.
-> 비동기 호출시 다른 쓰레드에서 프로세스가 실행된다.
위와 같이 스프링 트랜잭션은 일반적으로 스레드간 전파가 이루어지지 않으며, 따라서 비동기 호출시에는 다른 스레드로 넘어가기에 트랜잭션이 유지되지 않는다.
스프링 트랜잭션과 스레드
그렇다면 스프링은 트랜잭션을 어떻게 스레드별로 관리하고 있을까?
트랜잭션이 어떻게 관리되는지를 알아보기 위해서, 간단하게 트랜잭션을 얻어오는 코드에서 중단점을 찍고 step into로 디버깅을 해보았다.
코드를 따라들어가다보면 TransactionSynchronizationManager라는 클래스가 나오는데, 찾아보니 이 클래스는 트랜잭션 동안 데이터베이스 커넥션, 쓰레드 바인딩, 세션과 같은 트랜잭션에 필요한 리소스를 동기화하고 관리하는 데 사용된다고 나왔다. 코드를 살펴보면 해당 클래스의 resource 객체에서 다양한 리소스 정보를 가져오는 것을 볼 수 있었는데,
resource 객체는 ThreadLocal 타입으로 되어 있었다. 다시 말해 트랜잭션 유지에 필요한 리소스들이 쓰레드별로 관리된다.
즉, 트랜잭션에 필요한 리소스들은 ThreadLocal을 통해서 쓰레드 단위로 관리되고 있었던 것. 따라서 하나의 스레드에서 여러개의 트랜잭션을 할당해줄 수 있지만, 트랜잭션 자체는 하나의 스레드 내부에서만 유지된다.
(ThreadLocal에 대해서 모른다면, https://eckrin.tistory.com/173에 정리되어 있다)
'[ Backend ] > Spring' 카테고리의 다른 글
[Spring Security] 스프링 시큐리티로 익명 사용자 관리하기 (0) | 2024.01.13 |
---|---|
[Spring] 동시성 이슈를 고려한 스프링 프로젝트 설계 (0) | 2024.01.03 |
[Spring Security] Security 인증, 인가 설정 (0) | 2023.10.16 |
[Spring] 스프링과 Tomcat (0) | 2023.02.16 |
[Spring Security] @AuthenticationPrincipal, HandlerMethodArgumentResolver (0) | 2022.11.13 |