Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE] multiDataSource 설정 수정 및 테스트 검증 강화 #696

Merged
merged 7 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,21 @@ public class DataSourceConfig {
private static final String ROUTING_DATA_SOURCE = "routingDataSource";

@Primary
@Bean(name = "dataSource")
public DataSource dataSource(@Qualifier(ROUTING_DATA_SOURCE) DataSource routingDataSourceType) {
return new LazyConnectionDataSourceProxy(routingDataSourceType);
}

@Bean(name = WRITER_DATA_SOURCE_BEAN_NAME)
@ConfigurationProperties(prefix= WRITER_DATA_SOURCE_PREFIX)
@ConfigurationProperties(prefix = WRITER_DATA_SOURCE_PREFIX)
public DataSource writerDataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
}

@Bean(name = READER_DATA_SOURCE_BEAN_NAME)
@ConfigurationProperties(prefix= READER_DATA_SOURCE_PREFIX)
@ConfigurationProperties(prefix = READER_DATA_SOURCE_PREFIX)
public DataSource readerDataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
Expand All @@ -58,9 +63,4 @@ public DataSource routingDataSource(

return routingDataSource;
}

@Bean(name = "dataSource")
public DataSource dataSource(@Qualifier(ROUTING_DATA_SOURCE) DataSource routingDataSourceType) {
return new LazyConnectionDataSourceProxy(routingDataSourceType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {

@Override
protected Object determineCurrentLookupKey() {
if(TransactionSynchronizationManager.isCurrentTransactionReadOnly()){
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
return DataSourceType.READER;
}
return DataSourceType.WRITER;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,49 @@

import static org.assertj.core.api.Assertions.assertThat;

import com.happy.friendogly.config.datasource.DataSourceType;
import com.happy.friendogly.config.datasource.ReplicationRoutingDataSource;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.transaction.annotation.Transactional;

@SpringBootTest
@DataJpaTest
@Import(TestDataSourceConfig.class)
@AutoConfigureTestDatabase(replace = Replace.NONE)
@ActiveProfiles("testMultiDataSource")
public class DataSourceRoutingTest {

private static final String TEST_METHOD_NAME = "determineCurrentLookupKey";

@Test
@DisplayName("쓰기전용 트랜잭션이면 Writer 데이터소스가 바운딩된다.")
@Transactional
void writeOnlyTransactionTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ReplicationRoutingDataSource replicationRoutingDataSource = new ReplicationRoutingDataSource();

Method determineCurrentLookupKey = ReplicationRoutingDataSource.class.getDeclaredMethod(TEST_METHOD_NAME);
determineCurrentLookupKey.setAccessible(true);

DataSourceType dataSourceType = (DataSourceType) determineCurrentLookupKey
.invoke(replicationRoutingDataSource);
@Autowired
private ApplicationContext applicationContext;

assertThat(dataSourceType).isEqualTo(DataSourceType.WRITER);
@DisplayName("읽기 전용 트랜잭션이 아니면, Writer DB 데이터소스가 바운딩된다.")
@Test
@Transactional(readOnly = false)
void writeOnlyTransactionTest() throws SQLException {
DataSource dataSource = applicationContext.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection()) {
String actual = connection.getMetaData().getURL();
assertThat(actual).contains("writer");
}
}

@Test
@DisplayName("readOnly 트랜잭션이면 redaer 데이터소스가 바운딩된다.")
@DisplayName("읽기전용 트랜잭션이면 reader DB 데이터소스가 바운딩된다.")
@Transactional(readOnly = true)
void readOnlyTransactionTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ReplicationRoutingDataSource replicationRoutingDataSource = new ReplicationRoutingDataSource();

Method determineCurrentLookupKey = ReplicationRoutingDataSource.class.getDeclaredMethod(TEST_METHOD_NAME);
determineCurrentLookupKey.setAccessible(true);

DataSourceType dataSourceType = (DataSourceType) determineCurrentLookupKey
.invoke(replicationRoutingDataSource);

assertThat(dataSourceType).isEqualTo(DataSourceType.READER);
void readOnlyTransactionTest() throws SQLException {
DataSource dataSource = applicationContext.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection()) {
String actual = connection.getMetaData().getURL();
assertThat(actual).contains("reader");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.happy.friendogly;

import com.happy.friendogly.config.datasource.DataSourceType;
import com.happy.friendogly.config.datasource.ReplicationRoutingDataSource;
import com.zaxxer.hikari.HikariDataSource;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;

@Profile({"testMultiDataSource"})
@TestConfiguration
public class TestDataSourceConfig {

private static final String WRITER_DATA_SOURCE_BEAN_NAME = "writerDataSource";
private static final String READER_DATA_SOURCE_BEAN_NAME = "readerDataSource";
private static final String WRITER_DATA_SOURCE_PREFIX = "spring.datasource.writer.hikari";
private static final String READER_DATA_SOURCE_PREFIX = "spring.datasource.reader.hikari";
private static final String ROUTING_DATA_SOURCE = "routingDataSource";

@Primary
@Bean(name = "dataSource")
public DataSource dataSource(@Qualifier(ROUTING_DATA_SOURCE) DataSource routingDataSourceType) {
return new LazyConnectionDataSourceProxy(routingDataSourceType);
}

@Bean(name = WRITER_DATA_SOURCE_BEAN_NAME)
@ConfigurationProperties(prefix = WRITER_DATA_SOURCE_PREFIX)
public DataSource writerDataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
}

@Bean(name = READER_DATA_SOURCE_BEAN_NAME)
@ConfigurationProperties(prefix = READER_DATA_SOURCE_PREFIX)
public DataSource readerDataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
}

@Bean(name = ROUTING_DATA_SOURCE)
public DataSource routingDataSource(
@Qualifier(WRITER_DATA_SOURCE_BEAN_NAME) DataSource writerDataSourceType,
@Qualifier(READER_DATA_SOURCE_BEAN_NAME) DataSource readerDataSourceType
) {

ReplicationRoutingDataSource routingDataSource = new ReplicationRoutingDataSource();

Map<Object, Object> dataSources = new HashMap<>();

dataSources.put(DataSourceType.WRITER, writerDataSourceType);
dataSources.put(DataSourceType.READER, readerDataSourceType);

routingDataSource.setTargetDataSources(dataSources);
routingDataSource.setDefaultTargetDataSource(writerDataSourceType);

return routingDataSource;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;


class ClubQueryServiceTest extends ClubServiceTest {

@Autowired
Expand Down Expand Up @@ -383,7 +382,8 @@ void findById() {
() -> assertThat(response.memberDetails().get(0).id()).isEqualTo(savedMember.getId()),
() -> assertThat(response.petDetails().size()).isEqualTo(1),
() -> assertThat(response.petDetails().get(0).id()).isEqualTo(savedPet.getId()),
() -> assertThat(response.allowedGender()).containsExactlyInAnyOrder(Gender.FEMALE, Gender.FEMALE_NEUTERED),
() -> assertThat(response.allowedGender()).containsExactlyInAnyOrder(Gender.FEMALE,
Gender.FEMALE_NEUTERED),
() -> assertThat(response.allowedSize()).containsExactlyInAnyOrder(SizeType.SMALL)
);
}
Expand Down
25 changes: 4 additions & 21 deletions backend/src/test/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,9 @@ jwt:
kakao:
admin-key: sdfsdfsdfdsfsdfsdf


# DataSource 라우팅 테스트
spring:
datasource:
writer:
hikari:
driver-class-name: org.h2.Driver
jdbc-url: jdbc:h2:mem:database
read-only: false
username: sa
password:
reader:
hikari:
driver-class-name: org.h2.Driver
jdbc-url: jdbc:h2:mem:database
read-only: true
username: sa
password:
jpa:
properties:
hibernate:
default_batch_fetch_size: 100
jpa:
properties:
hibernate:
default_batch_fetch_size: 100

18 changes: 18 additions & 0 deletions backend/src/test/resources/application-testMultiDataSource.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# DataSource 라우팅 테스트
spring:
datasource:
writer:
hikari:
driver-class-name: org.h2.Driver
jdbc-url: jdbc:h2:mem:writer;
read-only: false
username: sa
password:
reader:
hikari:
driver-class-name: org.h2.Driver
jdbc-url: jdbc:h2:mem:reader;
read-only: true
username: sa
password:

Loading