Skip to content

Commit

Permalink
feat : use better way to delegate database (#1491)
Browse files Browse the repository at this point in the history
* feat : use better way to delegate database

* fix : Issues with Junit

* adds more documentation
  • Loading branch information
rajadilipkolli authored Nov 2, 2024
1 parent 480364d commit 3fac8d2
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 97 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The following table list all sample codes related to the spring boot integration
| [Hibernate Envers Implementation using spring data JPA](./jpa/boot-data-envers) | The application, demonstrates how to apply hibernate envers to the spring boot project, monitor the system and alert when CPU usage is high or when system is down | Completed |
| [Graph QL implementation using webflux](./graphql/boot-graphql-webflux) | The application, demonstrates the way to connect to database using graph ql using webflux | Completed |
| [Hibernate 2nd Level Cache Using Redis](./jpa/boot-hibernate2ndlevelcache-sample) | The application, demonstrates how to apply Hibernate 2nd level cache using redis in a spring boot project , testing using QueryCounting, implemented hypersistence Repository instead of default JPARepository | Completed |
| [Read Replica Postgres](./jpa/boot-read-replica-postgresql) | The application, demonstrates saving the data in Postgresql and then read from replica instance | Completed |
| [Read Replica Postgres with connection optimization](./jpa/boot-read-replica-postgresql) | The application, demonstrates saving the data in Postgresql and then read from replica instance with optimized connection handling via LazyConnectionDataSourceProxy | Completed |
| [BackgroundJobs and Scheduling using Jobrunr](./scheduler/boot-scheduler-jobrunr) | The application, demonstrates running background jobs and scheduling the tasks using [Jobrunr](https://www.jobrunr.io/en/) | Completed |
| [MultiTenancy DB Based](./jpa/multitenancy/multitenancy-db) | The application, demonstrates running multi tenancy in JPA using different databases but same DDLs and DMLs | Completed |
| [MultiTenancy Partition Based](./jpa/multitenancy/partition) | The application, demonstrates running multi tenancy in JPA using partition based i.e Shared Database with Shared table | Completed |
Expand Down
1 change: 1 addition & 0 deletions jpa/boot-read-replica-postgresql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This project is an example to show how we can separate read and write operations
A read replica in Postgres is a database instance that receives data from a primary database instance and serves it to clients. Read replicas are useful for scaling database workloads, as they can offload read operations from the primary instance, allowing it to focus on more resource-intensive tasks such as writing data. This can improve the performance of the overall database system. Read replicas can also be useful for providing high availability, as they can take over read operations if the primary instance becomes unavailable for any reason.

- All reads will go to reader instance and writes will go to writer instance
- Swithching between master and replica can be observed at docker compose logs or in application/logs when datasourceproxy is enabled.

![](../../images/replica.png)

Expand Down
2 changes: 0 additions & 2 deletions jpa/boot-read-replica-postgresql/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '2'

services:
postgresql-master:
image: 'bitnami/postgresql:latest'
Expand Down
10 changes: 7 additions & 3 deletions jpa/boot-read-replica-postgresql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version>
<version>3.4.0-RC1</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.demo</groupId>
Expand Down Expand Up @@ -50,12 +50,16 @@
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.ttddyy.observation</groupId>
<artifactId>datasource-micrometer-spring-boot</artifactId>
<version>1.0.5</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down Expand Up @@ -105,7 +109,7 @@
<configuration>
<java>
<googleJavaFormat>
<version>1.22.0</version>
<version>1.24.0</version>
<style>AOSP</style>
</googleJavaFormat>
</java>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package com.example.demo.readreplica.config;

import com.example.demo.readreplica.config.routing.RoutingDataSource;
import com.zaxxer.hikari.HikariDataSource;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;

@Configuration(proxyBeanMethods = false)
class DatabaseConfig {
Expand All @@ -24,7 +22,7 @@ DataSourceProperties primaryDataSourceProperties() {
}

@Bean
@ConfigurationProperties(PRIMARY_DATABASE_PROPERTY_KEY_PREFIX + ".configuration")
@ConfigurationProperties(PRIMARY_DATABASE_PROPERTY_KEY_PREFIX + ".hikari")
DataSource primaryDataSource(final DataSourceProperties primaryDataSourceProperties) {
return primaryDataSourceProperties
.initializeDataSourceBuilder()
Expand All @@ -39,7 +37,7 @@ DataSourceProperties replicaDataSourceProperties() {
}

@Bean
@ConfigurationProperties(REPLICA_DATABASE_PROPERTY_KEY_PREFIX + ".configuration")
@ConfigurationProperties(REPLICA_DATABASE_PROPERTY_KEY_PREFIX + ".hikari")
DataSource replicaDataSource(final DataSourceProperties replicaDataSourceProperties) {
return replicaDataSourceProperties
.initializeDataSourceBuilder()
Expand All @@ -50,15 +48,9 @@ DataSource replicaDataSource(final DataSourceProperties replicaDataSourcePropert
@Bean
@Primary
DataSource dataSource(final DataSource primaryDataSource, final DataSource replicaDataSource) {
final RoutingDataSource routingDataSource = new RoutingDataSource();

final Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(RoutingDataSource.Route.PRIMARY, primaryDataSource);
targetDataSources.put(RoutingDataSource.Route.REPLICA, replicaDataSource);

routingDataSource.setTargetDataSources(targetDataSources);
routingDataSource.setDefaultTargetDataSource(primaryDataSource);

return routingDataSource;
LazyConnectionDataSourceProxy lazyConnectionDataSourceProxy =
new LazyConnectionDataSourceProxy(primaryDataSource);
lazyConnectionDataSourceProxy.setReadOnlyDataSource(replicaDataSource);
return lazyConnectionDataSourceProxy;
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,62 @@ spring:
datasource:
password: postgres_write
username: postgres_write
driver: org.postgresql.Driver
driverClassName: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/my_database
configuration:
poolName: primaryHikariPool
hikari:
auto-commit: false
pool-name: primaryHikariPool
data-source-properties:
ApplicationName: ${spring.application.name}
replica:
datasource:
password: repl_password
username: repl_user
driver: org.postgresql.Driver
driverClassName: org.postgresql.Driver
url: jdbc:postgresql://localhost:15432/my_database
configuration:
hikari:
auto-commit: false
poolName: replicaHikariPool

################ Database #####################
data:
jpa:
repositories:
bootstrap-mode: deferred
jpa:
open-in-view: false
show-sql: false
hibernate:
ddl-auto: validate
properties:
hibernate:
connection:
provider_disables_autocommit: true
jdbc:
time_zone: UTC
batch_size: 25
lob.non_contextual_creation : true
generate_statistics: false
order_inserts: true
order_updates: true
query:
fail_on_pagination_over_collection_fetch: true
in_clause_parameter_padding: true
plan_cache_max_size: 4096
threads:
virtual:
enabled: true
enabled: true

# spring boot log level property
logging:
level:
read-replica-logger: DEBUG
jdbc:
datasource-proxy:
enabled: true
multiline: false
logging: slf4j
query:
logger-name: read-replica-logger
log-level: DEBUG
enable-logging: true
excluded-data-source-bean-names: dataSource
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.awaitility.Awaitility.await;

import com.example.demo.readreplica.config.routing.RoutingDataSource;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
Expand All @@ -19,13 +18,17 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.util.ReflectionTestUtils;

@ActiveProfiles("test")
@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ReadReplicaApplicationTests {

@Autowired private RoutingDataSource routingDataSource;
@Autowired private DataSource dataSource;

private JdbcTemplate primaryJdbcTemplate;

Expand All @@ -38,13 +41,36 @@ class ReadReplicaApplicationTests {

@BeforeAll
void setUp() {
assertThat(routingDataSource).isNotNull();
Map<Object, DataSource> resolvedDataSources = routingDataSource.getResolvedDataSources();
assertThat(resolvedDataSources).isNotEmpty().hasSize(2);
setupJdbcTemplates(dataSource);
}

private void setupJdbcTemplates(DataSource dataSource) {
assertThat(dataSource)
.isNotNull()
.withFailMessage("DataSource must not be null")
.isInstanceOf(LazyConnectionDataSourceProxy.class);

LazyConnectionDataSourceProxy lazyProxy = (LazyConnectionDataSourceProxy) dataSource;

// Setup primary template
DataSource targetDataSource = lazyProxy.getTargetDataSource();
assertThat(targetDataSource)
.isNotNull()
.withFailMessage("Target DataSource must not be null")
.isInstanceOf(ProxyDataSource.class);

primaryJdbcTemplate =
new JdbcTemplate(resolvedDataSources.get(RoutingDataSource.Route.PRIMARY));
new JdbcTemplate(((ProxyDataSource) targetDataSource).getDataSource());

// Setup replica template
Object readOnlyDataSource = ReflectionTestUtils.getField(lazyProxy, "readOnlyDataSource");
assertThat(readOnlyDataSource)
.isNotNull()
.withFailMessage("Read-only DataSource must not be null")
.isInstanceOf(ProxyDataSource.class);

replicaJdbcTemplate =
new JdbcTemplate(resolvedDataSources.get(RoutingDataSource.Route.REPLICA));
new JdbcTemplate(((ProxyDataSource) readOnlyDataSource).getDataSource());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#since tests uses only jdbc template, connection optimizations handled by jps is gone, to enabling autocommit
spring.primary.datasource.hikari.auto-commit=true

0 comments on commit 3fac8d2

Please sign in to comment.