제목: "LazyConnectionDataSourceProxy 알아보기"
작성자: github.io(sup2is)
작성자 수정일: 2021년7월8일
링크: https://sup2is.github.io/2021/07/08/lazy-connection-datasource-proxy.html
작성일: 2022년8월16일
Spring은 트랜잭션에 진입하는 순간 Database Connection을 가져올까?
결론부터 말하면 스프링에서는 트랜잭션에 진입하는 순간 설정된 데이터소스의 커넥션을 가져온다. 이 결론으로 도달할 수 있는 단점은 아래와 같다.
위 단점들을 해결할 수 있는 방법이 바로 LazyConnectionDataSourceProxy
이다.
LazyConnectionDataSourceProxy
을 사용하면 실제로 커넥션이 필요한 경우가 아니라면 커넥션 풀에서 커넥션을 점유하지 않고 실제로 필요한 시점에만 커넥션을 점유하게 할 수 있다.
먼저 실제 스프링 트랜잭션에 진입한 뒤 아무런 작업을 하지않아도 DB 커넥션을 점유하는지 알아보자.
package me.dragonappear.replicationdatasource.service;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariPoolMXBean;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.dragonappear.replicationdatasource.entity.Account;
import me.dragonappear.replicationdatasource.repository.AccountJpaRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.sql.DataSource;
@Slf4j
@RequiredArgsConstructor
@Transactional
@Service
public class AccountService {
private final AccountJpaRepository accountJpaRepository;
private final DataSource dataSource;
public Account get(Long id) {
logConnectionStatus();
return null;
//return accountJpaRepository.findById(id).orElseThrow(() -> new RuntimeException("조회 실패"));
}
public Long create(String name) {
return null;
//return accountJpaRepository.save(new Account(name)).getId();
}
private void logConnectionStatus() {
HikariPoolMXBean hikariPoolMXBean = ((HikariDataSource) dataSource).getHikariPoolMXBean();
log.info("################################");
log.info("현재 active인 connection의 수 : {}",hikariPoolMXBean.getActiveConnections());
log.info("현재 idle인 connection의 수 : {}",hikariPoolMXBean.getIdleConnections());
log.info("################################");
}
}
아무런 작업을 하지 않아도 트랜잭션에 진입하는 순간 커넥션을 점유하는 것을 확인할 수 있다.
아래와 같이 LazyConnectionDataSourceProxy
을 적용해보고 다시 아무런 작업도 하지 않는 AccountService 의 get()
메서드를 실행해보도록 하겠다.
@Configuration
@Profile("local")
public class DataSourceConfig {
@Bean
public DataSource lazyDataSource(DataSourceProperties properties) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(properties.getUrl());
dataSource.setUsername(properties.getUsername());
dataSource.setPassword(properties.getPassword());
dataSource.setDriverClassName(properties.getDriverClassName());
return new LazyConnectionDataSourceProxy(dataSource);
}
}
logConnectionStatus()
수정
private void logConnectionStatus() {
HikariPoolMXBean hikariPoolMXBean = ((HikariDataSource) ((LazyConnectionDataSourceProxy) lazyDataSource).getTargetDataSource()).getHikariPoolMXBean();
log.info("################################");
log.info("현재 active인 connection의 수 : {}",hikariPoolMXBean.getActiveConnections());
log.info("현재 idle인 connection의 수 : {}",hikariPoolMXBean.getIdleConnections());
log.info("################################");
}
JpaTransactionManager
의 doBegin()
메서드를 실행하는 부분에서 커넥션을 맺는 과정이 있는데 일반 HikariDataSource
인스턴스는 doBegin()
시점에 커넥션을 실제로 가져오지만 LazyConnectionDataSourceProxy
은 Proxy
객체로 한번 감싼 인스턴스를 doBegin()
시점에 리턴시켜서 실제 커넥션을 맺지는 않고 프록시 객체를 반환하도록 되어있다.JpaTransactionManager.doBegin()
HikariDataSource.getConnection()
에서 실제로 커넥션을 가져오는 모습LazyConnectionDataSourceProxy.getConnection()
에서 프록시를 리턴하는 모습