Skip to content

Commit

Permalink
Set up database connection pool
Browse files Browse the repository at this point in the history
  • Loading branch information
hstonec committed Aug 19, 2019
1 parent e1b312a commit e219b11
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ dependencies {
testCompile deps['org.seleniumhq.selenium:selenium-chrome-driver']
testCompile deps['org.seleniumhq.selenium:selenium-java']
testCompile deps['org.seleniumhq.selenium:selenium-remote-driver']
testCompile deps['org.testcontainers:postgresql']
testCompile deps['org.testcontainers:selenium']
compile deps['xerces:xmlParserAPIs']
compile deps['xpp3:xpp3']
Expand Down
45 changes: 45 additions & 0 deletions core/src/main/java/google/registry/config/RegistryConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -1465,6 +1465,51 @@ public static Duration getBaseOfyRetryDuration() {
return Duration.millis(CONFIG_SETTINGS.get().datastore.baseOfyRetryMillis);
}

/** Returns the JDBC driver class name. */
public static String getDriverClassName() {
return CONFIG_SETTINGS.get().hibernate.driverClassName;
}

/** Returns the default database transaction isolation. */
public static String getHibernateConnectionIsolation() {
return CONFIG_SETTINGS.get().hibernate.connectionIsolation;
}

/** Returns true if hibernate.show_sql is enabled. */
public static String getHibernateShowSql() {
return CONFIG_SETTINGS.get().hibernate.showSql;
}

/** Returns true if schema modification is allowed. */
public static String getHibernateHbm2ddlAuto() {
return CONFIG_SETTINGS.get().hibernate.hbm2ddlAuto;
}

/** Returns the class name of the connection pool provider. */
public static String getHibernateConnectionProviderClass() {
return CONFIG_SETTINGS.get().hibernate.connectionProviderClass;
}

/** Returns the connection timeout for HikariCP. */
public static String getHibernateHikariConnectionTimeout() {
return CONFIG_SETTINGS.get().hibernate.hikariConnectionTimeout;
}

/** Returns the minimum idle connections for HikariCP. */
public static String getHibernateHikariMinimumIdle() {
return CONFIG_SETTINGS.get().hibernate.hikariMinimumIdle;
}

/** Returns the maximum pool size for HikariCP. */
public static String getHibernateHikariMaximumPoolSize() {
return CONFIG_SETTINGS.get().hibernate.hikariMaximumPoolSize;
}

/** Returns the idle timeout for HikariCP. */
public static String getHibernateHikariIdleTimeout() {
return CONFIG_SETTINGS.get().hibernate.hikariIdleTimeout;
}

/** Returns the roid suffix to be used for the roids of all contacts and hosts. */
public static String getContactAndHostRoidSuffix() {
return CONFIG_SETTINGS.get().registryPolicy.contactAndHostRoidSuffix;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class RegistryConfigSettings {
public CredentialOAuth credentialOAuth;
public RegistryPolicy registryPolicy;
public Datastore datastore;
public Hibernate hibernate;
public CloudDns cloudDns;
public Caching caching;
public IcannReporting icannReporting;
Expand Down Expand Up @@ -105,6 +106,19 @@ public static class Datastore {
public int baseOfyRetryMillis;
}

/** Configuration for Hibernate. */
public static class Hibernate {
public String connectionIsolation;
public String showSql;
public String hbm2ddlAuto;
public String driverClassName;
public String connectionProviderClass;
public String hikariConnectionTimeout;
public String hikariMinimumIdle;
public String hikariMaximumPoolSize;
public String hikariIdleTimeout;
}

/** Configuration for Apache Beam (Cloud Dataflow). */
public static class Beam {
public String defaultJobZone;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,31 @@ datastore:
# doubles after each failure).
baseOfyRetryMillis: 100

hibernate:
# Make 'SERIALIZABLE' the default isolation level to ensure correctness.
#
# Entities that are never involved in multi-table transactions may use optimistic
# locks and a less strict isolation level. We may lower individual transaction's
# isolation level using a framework-dependent method.
#
# Alternatively, if a use case calls for, we may also use a lower isolation level
# but lock tables explicitly, either using framework-dependent API, or execute
# "select table for update" statements directly.
connectionIsolation: TRANSACTION_SERIALIZABLE
# Whether to log all SQL queries. Overridable at runtime.
showSql: false
# Never modify the schema. May use 'none' or 'validate'
hbm2ddlAuto: none
# JDBC driver class name for PostgreSQL
driverClassName: org.postgresql.Driver

# Connection pool configurations.
connectionProviderClass: org.hibernate.hikaricp.internal.HikariCPConnectionProvider
hikariConnectionTimeout: 20000
hikariMinimumIdle: 0
hikariMaximumPoolSize: 20
hikariIdleTimeout: 300000

cloudDns:
# Set both properties to null in Production.
# The root url for the Cloud DNS API. Set this to a non-null value to
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package google.registry.model.transaction;

import static google.registry.config.RegistryConfig.getDriverClassName;
import static google.registry.config.RegistryConfig.getHibernateConnectionIsolation;
import static google.registry.config.RegistryConfig.getHibernateConnectionProviderClass;
import static google.registry.config.RegistryConfig.getHibernateHbm2ddlAuto;
import static google.registry.config.RegistryConfig.getHibernateHikariConnectionTimeout;
import static google.registry.config.RegistryConfig.getHibernateHikariIdleTimeout;
import static google.registry.config.RegistryConfig.getHibernateHikariMaximumPoolSize;
import static google.registry.config.RegistryConfig.getHibernateHikariMinimumIdle;
import static google.registry.config.RegistryConfig.getHibernateShowSql;

import com.google.common.collect.ImmutableMap;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.hibernate.cfg.Environment;

/** Factory class to provide {@link EntityManagerFactory} instance. */
public class EntityManagerFactoryProvider {
// This name must be same to the one defined in persistence.xml.
public static final String PERSISTENCE_UNIT_NAME = "nomulus";
public static final String HIKARI_CONNECTION_TIMEOUT = "hibernate.hikari.connectionTimeout";
public static final String HIKARI_MINIMUM_IDLE = "hibernate.hikari.minimumIdle";
public static final String HIKARI_MAXIMUM_POOL_SIZE = "hibernate.hikari.maximumPoolSize";
public static final String HIKARI_IDLE_TIMEOUT = "hibernate.hikari.idleTimeout";

private static ImmutableMap<String, String> getDefaultProperties() {
ImmutableMap.Builder<String, String> properties = ImmutableMap.builder();
properties.put(Environment.DRIVER, getDriverClassName());
properties.put(Environment.ISOLATION, getHibernateConnectionIsolation());
properties.put(Environment.SHOW_SQL, getHibernateShowSql());
properties.put(Environment.HBM2DDL_AUTO, getHibernateHbm2ddlAuto());
properties.put(Environment.CONNECTION_PROVIDER, getHibernateConnectionProviderClass());
properties.put(HIKARI_CONNECTION_TIMEOUT, getHibernateHikariConnectionTimeout());
properties.put(HIKARI_MINIMUM_IDLE, getHibernateHikariMinimumIdle());
properties.put(HIKARI_MAXIMUM_POOL_SIZE, getHibernateHikariMaximumPoolSize());
properties.put(HIKARI_IDLE_TIMEOUT, getHibernateHikariIdleTimeout());
return properties.build();
}

/** Constructs the {@link EntityManagerFactory} instance. */
public static EntityManagerFactory create(String jdbcUrl, String username, String password) {
ImmutableMap.Builder<String, String> properties = ImmutableMap.builder();
properties.putAll(getDefaultProperties());
properties.put(Environment.URL, jdbcUrl);
properties.put(Environment.USER, username);
properties.put(Environment.PASS, password);
return Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME, properties.build());
}

}
27 changes: 27 additions & 0 deletions core/src/main/resources/META-INF/persistence.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
version="2.2">
<persistence-unit name="nomulus" transaction-type="RESOURCE_LOCAL">
<description>
Persistence unit for the Nomulus Cloud SQL database.
</description>
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

<!--
All JPA entities must be enumerated here. JPA does not support auto detection.
Note that Hibernate's auto detection functionality (hibernate.archive.autodection)
does not meet our needs. It only scans archives, not the 'classes' folders. So we
are left with two options:
* Move tests to another (sub)project. This is not a big problem, but feels unnatural.
* Use Hibernate's ServiceRegistry for bootstrapping (not JPA-compliant)
-->
<!-- <class>google.registry.schema.tmch.ClaimsList</class> -->

<!-- TODO(weiminyu): check out application-layer validation. -->
<validation-mode>NONE</validation-mode>
</persistence-unit>
</persistence>
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package google.registry.model.transaction;

import static com.google.common.truth.Truth.assertThat;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.testcontainers.containers.PostgreSQLContainer;

/** Unit tests for {@link EntityManagerFactoryProvider}. */
@RunWith(JUnit4.class)
public class EntityManagerFactoryProviderTest {
@Rule
public PostgreSQLContainer database = new PostgreSQLContainer();

private EntityManagerFactory emf;

@Before
public void init() {
emf = EntityManagerFactoryProvider.create(
database.getJdbcUrl(),
database.getUsername(),
database.getPassword()
);
}

@After
public void destroy() {
emf.close();
emf = null;
}

@Test
public void testConnectToDatabase_success() {
EntityManager em = emf.createEntityManager();
assertThat(em.isOpen()).isTrue();
em.close();
}
}
1 change: 1 addition & 0 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ ext {
'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59',
'org.seleniumhq.selenium:selenium-java:3.141.59',
'org.seleniumhq.selenium:selenium-remote-driver:3.141.59',
'org.testcontainers:postgresql:1.11.3',
'org.testcontainers:selenium:1.10.7',
'org.yaml:snakeyaml:1.17',
'xerces:xmlParserAPIs:2.6.2',
Expand Down

0 comments on commit e219b11

Please sign in to comment.