Skip to content

Commit

Permalink
fix: add graceful shutdown and hikari connection pool (#274)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ricardo Campos authored Apr 5, 2024
1 parent 0c3b777 commit 36601b5
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 32 deletions.
6 changes: 3 additions & 3 deletions backend/openshift.deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ parameters:
value: "90000"
- name: DB_POOL_IDLE_TIMEOUT
description: Maximum amount of milliseconds that a connection is allowed to sit idle in the pool.
value: "0"
value: "60000"
- name: DB_POOL_MAX_LIFETIME
description: Maximum lifetime of a connection in the pool.
value: "1800000"
value: "120000"
- name: DB_POOL_MAX_SIZE
description: Maximum number of connections per pod
value: "3"
value: "1"
- name: DB_POOL_MIN_IDLE
description: Minimum number of connections per pod
value: "1"
Expand Down
6 changes: 0 additions & 6 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,6 @@
<version>1.18.30</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>

<!-- DevOps -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ca.bc.gov.restapi.results.oracle.config;

import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

/** This class adds a listener for closing connection gracefully. */
@Component
public class OracleGracefulShutdownConfig implements ApplicationListener<ContextClosedEvent> {

@Autowired private EntityManager oracleEntityManager;

@Override
public void onApplicationEvent(@NonNull ContextClosedEvent event) {
oracleEntityManager.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ca.bc.gov.restapi.results.oracle.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/** This class holds configurations for the Oracle Hikari connection pool. */
@Getter
@Setter
@Configuration
@ConfigurationProperties("spring.datasource.oracle")
public class OracleHikariConfig {

private String driverClassName;
private String url;
private String username;
private String password;
private long connectionTimeout;
private long idleTimeout;
private long maxLifetime;
private long keepaliveTime;
private String poolName;
private int minimumIdle;
private int maximumPoolSize;
private long leakDetectionThreshold;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package ca.bc.gov.restapi.results.oracle.config;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
Expand All @@ -11,15 +14,32 @@
@Configuration
public class OraclePersistenceConfig {

@Autowired private OracleHikariConfig oracleHikariConfig;

@Bean
@ConfigurationProperties("spring.datasource.oracle")
public DataSourceProperties oracleDataSourceProperties() {
return new DataSourceProperties();
}

/** Creates a Postgres Datasource with all Hikari connection pool configuration. */
@Bean
@Primary
public DataSource oracleDataSource() {
return oracleDataSourceProperties().initializeDataSourceBuilder().build();
HikariConfig config = new HikariConfig();
config.setJdbcUrl(oracleHikariConfig.getUrl());
config.setUsername(oracleHikariConfig.getUsername());
config.setPassword(oracleHikariConfig.getPassword());
config.setDriverClassName(oracleHikariConfig.getDriverClassName());
config.setConnectionTimeout(oracleHikariConfig.getConnectionTimeout());
config.setIdleTimeout(oracleHikariConfig.getIdleTimeout());
config.setMaxLifetime(oracleHikariConfig.getMaxLifetime());
config.setKeepaliveTime(oracleHikariConfig.getKeepaliveTime());
config.setPoolName(oracleHikariConfig.getPoolName());
config.setMinimumIdle(oracleHikariConfig.getMinimumIdle());
config.setMaximumPoolSize(oracleHikariConfig.getMaximumPoolSize());
config.setLeakDetectionThreshold(oracleHikariConfig.getLeakDetectionThreshold());

return new HikariDataSource(config);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ca.bc.gov.restapi.results.postgres.config;

import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

/** This class adds a listener for closing connection gracefully. */
@Component
public class PostgresGracefulShutdownConfig implements ApplicationListener<ContextClosedEvent> {

@Autowired private EntityManager postgresEntityManager;

@Override
public void onApplicationEvent(@NonNull ContextClosedEvent event) {
postgresEntityManager.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ca.bc.gov.restapi.results.postgres.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/** This class holds configurations for the Postgres Hikari connection pool. */
@Getter
@Setter
@Configuration
@ConfigurationProperties("spring.datasource.postgres")
public class PostgresHikariConfig {

private String driverClassName;
private String url;
private String username;
private String password;
private long connectionTimeout;
private long idleTimeout;
private long maxLifetime;
private long keepaliveTime;
private String poolName;
private int minimumIdle;
private int maximumPoolSize;
private long leakDetectionThreshold;
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,14 @@ public class PostgresJpaConfig {
@Bean
public LocalContainerEntityManagerFactoryBean postgresEntityManagerFactory(
@Qualifier("postgresDataSource") DataSource dataSource, EntityManagerFactoryBuilder builder) {
LocalContainerEntityManagerFactoryBean build =
builder.dataSource(dataSource).packages(getPostgresEntities()).build();
Properties jpaProps = new Properties();
jpaProps.setProperty("hibernate.default_schema", "silva");
jpaProps.setProperty("hibernate.ddl-auto", "update");
jpaProps.setProperty("defer-datasource-initialization", "true");
jpaProps.setProperty("sql.init.mode", "always");

LocalContainerEntityManagerFactoryBean build =
builder.dataSource(dataSource).packages(getPostgresEntities()).build();
build.setJpaProperties(jpaProps);
return build;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package ca.bc.gov.restapi.results.postgres.config;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
Expand All @@ -10,14 +13,31 @@
@Configuration
public class PostgresPersistenceConfig {

@Autowired private PostgresHikariConfig postgresHikariConfig;

@Bean
@ConfigurationProperties("spring.datasource.postgres")
public DataSourceProperties postgresDataSourceProperties() {
return new DataSourceProperties();
}

/** Creates a Postgres Datasource with all Hikari connection pool configuration. */
@Bean
public DataSource postgresDataSource() {
return postgresDataSourceProperties().initializeDataSourceBuilder().build();
HikariConfig config = new HikariConfig();
config.setJdbcUrl(postgresHikariConfig.getUrl());
config.setUsername(postgresHikariConfig.getUsername());
config.setPassword(postgresHikariConfig.getPassword());
config.setDriverClassName(postgresHikariConfig.getDriverClassName());
config.setConnectionTimeout(postgresHikariConfig.getConnectionTimeout());
config.setIdleTimeout(postgresHikariConfig.getIdleTimeout());
config.setMaxLifetime(postgresHikariConfig.getMaxLifetime());
config.setKeepaliveTime(postgresHikariConfig.getKeepaliveTime());
config.setPoolName(postgresHikariConfig.getPoolName());
config.setMinimumIdle(postgresHikariConfig.getMinimumIdle());
config.setMaximumPoolSize(postgresHikariConfig.getMaximumPoolSize());
config.setLeakDetectionThreshold(postgresHikariConfig.getLeakDetectionThreshold());

return new HikariDataSource(config);
}
}
36 changes: 17 additions & 19 deletions backend/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ logging.level.ca.bc.gov.restapi.results = ${LOGGING_LEVEL:INFO}
spring.application.name = results-api
server.error.include-message=always
server.port = ${SERVER_PORT:8080}
server.shutdown = graceful

# Actuator and ops
management.endpoint.health.show-details = always
Expand All @@ -23,31 +24,28 @@ spring.datasource.oracle.driver-class-name = oracle.jdbc.OracleDriver
spring.datasource.oracle.url = jdbc:oracle:thin:@tcps://${DATABASE_HOST:nrcdb03.bcgov}:${DATABASE_PORT:1543}/${SERVICE_NAME:dbq01.nrs.bcgov}?javax.net.ssl.trustStore=${ca.bc.gov.nrs.oracle.keystore}&javax.net.ssl.trustStorePassword=${ca.bc.gov.nrs.oracle.secret}&javax.net.ssl.keyStore=${ca.bc.gov.nrs.oracle.keystore}&javax.net.ssl.keyStorePassword=${ca.bc.gov.nrs.oracle.secret}&oracle.net.ssl_certificate_alias=${ca.bc.gov.nrs.oracle.host}&oracle.net.ssl_server_dn_match=false
spring.datasource.oracle.username = ${DATABASE_USER}
spring.datasource.oracle.password = ${DATABASE_PASSWORD}
spring.datasource.oracle.hikari.connectionTimeout = ${DB_POOL_CONN_TIMEOUT:90000}
spring.datasource.oracle.hikari.idleTimeout = ${DB_POOL_IDLE_TIMEOUT:45000}
spring.datasource.oracle.hikari.maxLifetime = ${DB_POOL_MAX_LIFETIME:30000}
spring.datasource.oracle.hikari.keepaliveTime = 30000
spring.datasource.oracle.hikari.poolName = SilvaOracleConnPool
spring.datasource.oracle.hikari.minimumIdle = ${DB_POOL_MIN_IDLE:1}
spring.datasource.oracle.hikari.maximumPoolSize = ${DB_POOL_MAX_SIZE:3}
#spring.jpa.database-platform = org.hibernate.dialect.OracleDialect
spring.datasource.oracle.connectionTimeout = ${DB_POOL_CONN_TIMEOUT:90000}
spring.datasource.oracle.idleTimeout = ${DB_POOL_IDLE_TIMEOUT:60000}
spring.datasource.oracle.maxLifetime = ${DB_POOL_MAX_LIFETIME:120000}
spring.datasource.oracle.keepaliveTime = 30000
spring.datasource.oracle.poolName = SilvaOracleConnPool
spring.datasource.oracle.minimumIdle = ${DB_POOL_MIN_IDLE:1}
spring.datasource.oracle.maximumPoolSize = ${DB_POOL_MAX_SIZE:1}
spring.datasource.oracle.leakDetectionThreshold = 20000

# Database, and JPA - Postgres
spring.datasource.postgres.driver-class-name = org.postgresql.Driver
spring.datasource.postgres.url = jdbc:postgresql://${POSTGRES_HOST:localhost}:5432/${POSTGRES_DB:postgres}
spring.datasource.postgres.username = ${POSTGRES_USER:postgres}
spring.datasource.postgres.password = ${POSTGRES_PASSWORD:default}
spring.datasource.postgres.hikari.connectionTimeout = ${DB_POOL_CONN_TIMEOUT:90000}
spring.datasource.postgres.hikari.idleTimeout = ${DB_POOL_IDLE_TIMEOUT:45000}
spring.datasource.postgres.hikari.maxLifetime = ${DB_POOL_MAX_LIFETIME:30000}
spring.datasource.postgres.hikari.keepaliveTime = 30000
spring.datasource.postgres.hikari.poolName = SilvaPostgresConnPool
spring.datasource.postgres.hikari.minimumIdle = ${DB_POOL_MIN_IDLE:1}
spring.datasource.postgres.hikari.maximumPoolSize = ${DB_POOL_MAX_SIZE:3}
#spring.jpa.database-platform = org.hibernate.dialect.PostgreSQLDialect
#spring.jpa.hibernate.ddl-auto = update
#spring.jpa.defer-datasource-initialization=true
#spring.sql.init.mode=always
spring.datasource.postgres.connectionTimeout = ${DB_POOL_CONN_TIMEOUT:90000}
spring.datasource.postgres.idleTimeout = ${DB_POOL_IDLE_TIMEOUT:60000}
spring.datasource.postgres.maxLifetime = ${DB_POOL_MAX_LIFETIME:120000}
spring.datasource.postgres.keepaliveTime = 30000
spring.datasource.postgres.poolName = SilvaPostgresConnPool
spring.datasource.postgres.minimumIdle = ${DB_POOL_MIN_IDLE:1}
spring.datasource.postgres.maximumPoolSize = ${DB_POOL_MAX_SIZE:1}
spring.datasource.postgres.leakDetectionThreshold = 20000

# Common database settings
spring.jpa.show-sql = true
Expand Down

0 comments on commit 36601b5

Please sign in to comment.