Skip to content

Commit

Permalink
Merge pull request #299 from woowacourse-teams/feat/#287
Browse files Browse the repository at this point in the history
[FEAT] DB Replication 설정
  • Loading branch information
PgmJun authored Sep 26, 2024
2 parents ddde96c + f56a315 commit 2a2e35e
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/be-ci-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
jobs:
build:
timeout-minutes: 4
runs-on: [ self-hosted, linux, ARM64, prod-a ]
runs-on: ubuntu-latest

defaults:
run:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package ddangkong.config.database;

import java.util.HashMap;
import java.util.List;
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.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;

@Profile("prod")
@Configuration
public class DataSourceConfig {

@Bean
@ConfigurationProperties(prefix = "spring.datasource.source")
public DataSource sourceDataSource() {
return DataSourceBuilder.create()
.build();
}

@Bean
@ConfigurationProperties(prefix = "spring.datasource.replica1")
public DataSource replica1DataSource() {
return DataSourceBuilder.create()
.build();
}

@Bean
public DataSource routingDataSource(
DataSource sourceDataSource,
DataSource replica1DataSource
) {
Map<Object, Object> dataSources = new HashMap<>();
dataSources.put("source", sourceDataSource);
dataSources.put("replica1", replica1DataSource);

RoutingDataSource routingDataSource = new RoutingDataSource(List.of("replica1"));
routingDataSource.setDefaultTargetDataSource(dataSources.get("source"));
routingDataSource.setTargetDataSources(dataSources);

return routingDataSource;
}

@Primary
@Bean
public DataSource dataSource() {
return new LazyConnectionDataSourceProxy(routingDataSource(sourceDataSource(), replica1DataSource()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ddangkong.config.database;

import java.util.List;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.support.TransactionSynchronizationManager;

public class RoutingDataSource extends AbstractRoutingDataSource {

private final RoutingReplicas<String> routingReplicas;

public RoutingDataSource(List<String> routingReplicas) {
this.routingReplicas = new RoutingReplicas<>(routingReplicas);
}

@Override
protected Object determineCurrentLookupKey() {
boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
if (isReadOnly) {
return routingReplicas.get();
} else {
return "source";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ddangkong.config.database;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class RoutingReplicas<T> {

private final List<T> replicas;
private final AtomicInteger index;

public RoutingReplicas(List<T> replicas) {
this.replicas = replicas;
index = new AtomicInteger(0);
}

public T get() {
return replicas.get(index.getAndIncrement() % replicas.size());
}
}
17 changes: 13 additions & 4 deletions backend/src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
spring:
config:
import: prod-secret.yml

datasource:
url: jdbc:mysql://${secret.datasource.url}:${secret.datasource.port}/${secret.datasource.database}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&useSSL=false
username: ${secret.datasource.username}
password: ${secret.datasource.password}
source:
driver-class-name: com.mysql.cj.jdbc.Driver
username: ${secret.datasource.source.username}
password: ${secret.datasource.source.password}
jdbc-url: jdbc:mysql://${secret.datasource.source.host}:${secret.datasource.source.port}/${secret.datasource.database}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&useSSL=false
replica1:
driver-class-name: com.mysql.cj.jdbc.Driver
username: ${secret.datasource.replica1.username}
password: ${secret.datasource.replica1.password}
jdbc-url: jdbc:mysql://${secret.datasource.replica1.host}:${secret.datasource.replica1.port}/${secret.datasource.database}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&useSSL=false

sql:
init:
Expand Down Expand Up @@ -33,6 +39,9 @@ logging:
location: ${secret.application.log.location}

management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
Expand Down

0 comments on commit 2a2e35e

Please sign in to comment.