-
Notifications
You must be signed in to change notification settings - Fork 6
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
feat-be: DataSource Routing을 통한 DB Read/Write 요청 분산 #704
Changes from all commits
98adb0e
83b41b7
bdd2623
ee575fd
0f19d86
21c99f6
f29b33c
d0bbf10
a8825d7
ae1bce5
7dfc65e
a7bf63f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package com.cruru.config; | ||
|
||
import com.zaxxer.hikari.HikariDataSource; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import javax.sql.DataSource; | ||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
import org.springframework.boot.jdbc.DataSourceBuilder; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.DependsOn; | ||
import org.springframework.context.annotation.Primary; | ||
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; | ||
|
||
@Configuration | ||
public class DataSourceConfig { | ||
|
||
private static final String READ_DATASOURCE = "readDataSource"; | ||
private static final String WRITE_DATASOURCE = "writeDataSource"; | ||
private static final String ROUTE_DATASOURCE = "routeDataSource"; | ||
|
||
@Bean(name = READ_DATASOURCE) | ||
@ConfigurationProperties(prefix = "spring.datasource.read") | ||
public DataSource readDataSource() { | ||
return DataSourceBuilder.create() | ||
.type(HikariDataSource.class) | ||
.build(); | ||
} | ||
|
||
@Bean(name = WRITE_DATASOURCE) | ||
@ConfigurationProperties(prefix = "spring.datasource.write") | ||
public DataSource writeDataSource() { | ||
return DataSourceBuilder.create() | ||
.type(HikariDataSource.class) | ||
.build(); | ||
} | ||
|
||
@Bean(name = ROUTE_DATASOURCE) | ||
@DependsOn({READ_DATASOURCE, WRITE_DATASOURCE}) | ||
public DataSourceRouter routeDataSource() { | ||
DataSourceRouter dataSourceRouter = new DataSourceRouter(); | ||
DataSource writeDataSource = writeDataSource(); | ||
DataSource readDataSource = readDataSource(); | ||
|
||
Map<Object, Object> dataSourceMap = new HashMap<>(); | ||
dataSourceMap.put(DataSourceRouter.READ_DATASOURCE_KEY, readDataSource); | ||
dataSourceMap.put(DataSourceRouter.WRITE_DATASOURCE_KEY, writeDataSource); | ||
dataSourceRouter.setTargetDataSources(dataSourceMap); | ||
dataSourceRouter.setDefaultTargetDataSource(writeDataSource()); | ||
return dataSourceRouter; | ||
} | ||
|
||
@Bean | ||
@Primary | ||
@DependsOn(ROUTE_DATASOURCE) | ||
public DataSource defaultDataSource() { | ||
return new LazyConnectionDataSourceProxy(routeDataSource()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.cruru.config; | ||
|
||
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; | ||
import org.springframework.transaction.support.TransactionSynchronizationManager; | ||
|
||
public class DataSourceRouter extends AbstractRoutingDataSource { | ||
|
||
public static final String READ_DATASOURCE_KEY = "read"; | ||
public static final String WRITE_DATASOURCE_KEY = "write"; | ||
|
||
@Override | ||
protected Object determineCurrentLookupKey() { | ||
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { | ||
return READ_DATASOURCE_KEY; | ||
} | ||
Comment on lines
+13
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 복제 구성은 주로 writer:read의 관계가 1:N입니다. 현재 구조에서 read db가 늘어나도 코드를 그대로 재사용할 수 있나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 구조에서는 read data가 늘어났을 때 코드를 재사용할 수 없죠. |
||
return WRITE_DATASOURCE_KEY; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.cruru.config; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.transaction.support.TransactionSynchronizationManager; | ||
|
||
@DisplayName("Datasource Routing 테스트") | ||
class DataSourceRouterTest { | ||
|
||
private final DataSourceRouter dataSourceRouter = new DataSourceRouter(); | ||
|
||
@Test | ||
void determineCurrentLookupKeyForReadOnlyTransaction() { | ||
// given | ||
TransactionSynchronizationManager.setCurrentTransactionReadOnly(true); | ||
|
||
// when | ||
Object lookupKey = dataSourceRouter.determineCurrentLookupKey(); | ||
|
||
// then | ||
assertThat(lookupKey).isEqualTo(DataSourceRouter.READ_DATASOURCE_KEY); | ||
} | ||
|
||
@Test | ||
void determineCurrentLookupKeyForReadWriteTransaction() { | ||
// give | ||
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false); | ||
|
||
// when | ||
Object lookupKey = dataSourceRouter.determineCurrentLookupKey(); | ||
|
||
// then | ||
assertThat(lookupKey).isEqualTo(DataSourceRouter.WRITE_DATASOURCE_KEY); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LazyConnectionDataSourceProxy
은 실제로 커넥션이 필요한경우가 아니라면 데이터베이스 풀에서 커넥션을 점유하지 않고 실제로 필요한 시점에만 커넥션을 점유하게 할 수 있게 합니다.