728x90
problem
배치서버(On Prem)와 운영서버(AWS)가 있다.
배치서버에서는 RDS 에서 데
이터의 중복을 확인하는 구문 들이 포함 되어있다.
RDS 는 데이터 삽입에는 돈이 들지 않지만, 조회시 요금이 청구되는 구조를 가지고 있다.
배치서버는 하루에 XX만건의 데이터를 처리 하다 보니, 데이터 전송량이 늘어만 갔다.
solution
배치서버에 리소스가 어느정도 여유가 있어서, 매일 배치작업 전 RDS 의 데이터를 덤프해 온 후,
배치서버의 DB 를 초기화 후 덮어씌우는 작업을 선행하여 데이터의 정합성을 맞춰보자는 시나리오를 세우게 되었다.
장점으로는
DB 백업 / RDS 데이터 조회 비용 감소 / 데이터 정합성
가져갈 수 있다는 생각을 하게 되었다.

1. Setting
//application.properties
# Writer DataSource(RDS)
spring.datasource.writer.url=jdbc:postgresql://writeDB:port/dbdb
spring.datasource.writer.username=writedb
spring.datasource.writer.password=writedb1
# Reader DataSource
spring.datasource.reader.url=jdbc:postgresql://readDB:port/dbdb
spring.datasource.reader.username=readdb
spring.datasource.reader.password=readdb1
2. Code
@Configuration
@Profile("!test")
class DataSourceConfig(
private val initializer: SshCommandConfig,
private val properties: Environment,
) {
@Bean
fun writerDataSource(): DataSource {
val forwardedPort = initializer.sshSession()
val url = properties.getRequiredProperty("spring.datasource.writer.url")
.replace("[forwardedPort]", forwardedPort.toString())
return DataSourceBuilder.create()
.driverClassName(properties.getRequiredProperty("spring.datasource.driver-class-name"))
.url(url)
.username(properties.getRequiredProperty("spring.datasource.writer.username"))
.password(properties.getRequiredProperty("spring.datasource.writer.password"))
.build()
}
@Bean
fun readerDataSource(): DataSource {
val url = properties.getRequiredProperty("spring.datasource.reader.url")
return DataSourceBuilder.create()
.driverClassName(properties.getRequiredProperty("spring.datasource.driver-class-name"))
.url(properties.getRequiredProperty("spring.datasource.reader.url"))
.username(properties.getRequiredProperty("spring.datasource.reader.username"))
.password(properties.getRequiredProperty("spring.datasource.reader.password"))
.build()
}
@Bean
@DependsOn("writerDataSource", "readerDataSource")
fun routingDataSource(
@Qualifier("writerDataSource") writerDataSource: DataSource,
@Qualifier("readerDataSource") readerDataSource: DataSource
): DataSource {
val routingDataSource = ReplicationRoutingDataSource()
val dataSources = mapOf(
DataSourceType.WRITE to writerDataSource,
DataSourceType.READ to readerDataSource
)
routingDataSource.setTargetDataSources(dataSources as MutableMap<Any, Any>)
routingDataSource.setDefaultTargetDataSource(writerDataSource)
return routingDataSource
}
@Bean
@Primary
@DependsOn("routingDataSource")
fun dataSource(routingDataSource: DataSource): DataSource {
return LazyConnectionDataSourceProxy(routingDataSource)
}
}
@DependsOn을 통해 차례대로 @Bean 을 세팅해 준 후,
실제 사용할 dataSource 빈에 LazyConnectionDataSourceProxy 를 래핑 하여,
실제 트랜잭션 전 까지 db커넥션을 지연 시키도록 한다.
enum class DataSourceType {
WRITE, READ
}
사용 할 Enum 클래스를 정의 해준다.
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
import org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly
class ReplicationRoutingDataSource : AbstractRoutingDataSource() {
override fun determineCurrentLookupKey(): Any {
val data = isCurrentTransactionReadOnly()
return if (data) {
DataSourceType.READ
} else {
DataSourceType.WRITE
}
}
}
isCurrentTransactionReadOnly() 함수는
@Transactional(readOnly = true)
현재 트랜잭션이 readOnly 상태인지 확인한다.
그 후
determineCurrentLookupKey() 함수는
AbstractRoutingDataSource 을 통해 위에서 정의했던
val routingDataSource = ReplicationRoutingDataSource() 에 보내어
routingDataSource 에 맞는 DB 를 찾아 커넥션을 하여 데이터를 요청한다.
728x90
'개-발 > Java + Spring + Kotlin' 카테고리의 다른 글
| [Spring] storekit2 / expo 인 앱 아이템 구매 확인 구현 2 (IOS) (2) | 2025.08.07 |
|---|---|
| [kotlin] 슬랙 봇 연동하기 (0) | 2025.06.20 |
| [Kotlin] 웹 크롤링 지연 후 가져오기 (0) | 2025.05.19 |
| [spring] valkey ssh 터널링 접근 (f.JSch) (0) | 2025.04.21 |
| [Spring] SSH 터널링으로 DB접속 ( EC2 RDS ) (0) | 2025.04.01 |