인프런 커뮤니티 질문&답변
@Transactional과 MULTI_TENANT_CONNECTION_PROVIDER, MULTI_TENANT_IDENTIFIER_RESOLVER 동작
작성
·
25
0
Map<String, Object> properties = hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
properties.put(MultiTenancySettings.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
properties.put(MultiTenancySettings.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver);
// MultiTenantConnectionProvider 중 커넥션 획득 메서드
@Override
public Connection getAnyConnection() throws SQLException {
return routingDataSource.getConnection();
}
이번 JPA, Hibernate 사용 중 다중 스키마 지원을 위해 멀티태넌시를 도입했습니다. 그런데 커넥션 프로바이더의 커넥션 획득 타이밍과 트랜잭션의 타이밍이 다른 것인지, @Transactional 의 readOnly 값이 나중에 적용되어, 커넥션 자체는 readWrite로 지정되고 계속 rw DB에 I/O가 가더라구요
혹시 이 문제 해결 방법이 있을까요?
ThreadLocal에 라우팅 키를 지정해서 그것을 기반으로 하는 것도 진행해봤으나, 그 부분에서도 propagaion = REQUIRED_NEW인 경우 새로운 커넥션을 열어 readWrite로 향해야 하는 것이 readOnly로 향해서 데이터 수정에 실패하는 케이스도 확인했습니다
Hibernate 설계 의도에 맞춰 해결할 수 있는 방법이 있는지 궁금해요
알고 계신 방법 있다면 조언 부탁드립니다.
답변 1
0
안녕하세요, 인프런 AI 인턴입니다.
정서현님께서 문의하신 Hibernate와 JPA의 Multi-tenancy 및 @Transactional을 사용한 트랜잭션 관리 문제와 관련된 해결 방법을 함께 살펴보겠습니다.
@Transactional(readOnly = true)사용시 문제:@Transactional에서readOnly옵션을 사용하면 Hibernate가 특정 데이터베이스 최적화를 시도합니다. 이로 인해 커넥션이 읽기 전용 모드로 열릴 수 있습니다. 하지만 Multi-tenancy 구성에서 모든 트랜잭션의 기본 설정은 데이터베이스 연결 전에 결정되어야 합니다.
트랜잭션의 타이밍과 연결 타이밍:
- Multi-tenant 환경에서 동일한 데이터소스가 다양한 테넌트(스키마)를 지원하고, Hibernate가 각각의 트랜잭션 앞에 언제 어떤 연결을 해야하는지는 중요한 문제입니다.
getAnyConnection()메서드에서 단순히 연결을 반환하는 방식이 아닌, 테넌트 구분의 역할을 명확히 하고 해당 테넌트를 기반으로 적절한 연결을 반환하는 방식으로 변경해야 합니다.
- Multi-tenant 환경에서 동일한 데이터소스가 다양한 테넌트(스키마)를 지원하고, Hibernate가 각각의 트랜잭션 앞에 언제 어떤 연결을 해야하는지는 중요한 문제입니다.
ThreadLocal을 사용한 해결책:ThreadLocal을 사용하여 현재 트랜잭션에서의 테넌트 정보를 유지하고 이를 통해 현재 테넌트의 데이터소스를 명확히 선택하는 방법이 일반적입니다.
- 그러나
propagation = REQUIRED_NEW인 경우 새로운 트랜잭션이 생성되며, 이 때 이전의ThreadLocal설정이 반영되지 않을 수 있습니다. 이 경우 새로운 연결이 그때의 설정을 따르도록 별도의TransactionSynchronization을 이용하거나, 트랜잭션의 종료 시점에ThreadLocal을 초기화하여 새롭게 적용할 수 있도록 해야 합니다.
Hibernate의 Custom Connection Provider 사용:
- 커스터마이즈된
MultiTenantConnectionProvider를 통해 각 트랜잭션 컨텍스트에 따라 적절한 데이터베이스 연결을 명확히 선택할 수 있도록 설정을 재구성하는 것도 방법입니다.
- 커스터마이즈된
정리하자면, Multi-tenancy 환경에서의 @Transactional의 선정적 타이밍 문제를 해결하기 위해서는 트랜잭션 라이프사이클에 맞춰 ThreadLocal이나 커스텀 Connection Provider를 적절히 활용하는 것이 중요합니다. 이를 통해 모든 트랜잭션이 올바른 모드로 작동할 수 있도록 설정해줘야 합니다.
도움이 되었기를 바랍니다. 추가적으로, Spring Data JPA의 트랜잭션 관련 문서도 함께 참고하시면 보다 구체적인 이해에 도움이 될 것입니다.





MultiTenantConnectionProvider 를 커스텀해서 적용하는 것도 방법이라면 어떤 방식이 있을까요?
제가 커넥션 프로바이더에 행했던 방식은 아래와 같습니다
@Override public Connection getAnyConnection() throws SQLException { return (TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? slaveDataSource : masterDataSource).getConnection(); }그런데 커넥션 프로바이더 내에서 TransactionSynchronizationManager.isCurrentTransactionReadOnly() 메서드가 호출되었을때는 readOnly 플래그가 false인 경우가 대부분인 것 같아요
아마 커넥션 프로바이더의 커넥션 획득 시점과 트랜잭션 인터셉터가 readOnly 플래그를 업데이트하는 시점에 차이가 발생한 것 같은데, 맞을까요? 커넥션 프로바이더가 들어가며 생긴 문제라 둘의 커넥션 획득 시점 차이가 생긴 이유가 궁금해요