Skip to content
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

fix: add graceful shutdown and hikari connection pool #274

Merged
merged 7 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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