Skip to content
Permalink
Browse files

Set up database connection pool (#234)

Set up database connection pool
  • Loading branch information...
hstonec committed Aug 29, 2019
1 parent b5ef99a commit 487b695a1006e15187af08073befe0fe4ffcf630
@@ -218,7 +218,6 @@ dependencies {
compile deps['org.bouncycastle:bcpg-jdk15on']
testCompile deps['org.bouncycastle:bcpkix-jdk15on']
compile deps['org.bouncycastle:bcprov-jdk15on']
compile deps['org.hibernate:hibernate-core']
compile deps['org.joda:joda-money']
compile deps['org.json:json']
testCompile deps['org.mortbay.jetty:jetty']
@@ -254,7 +253,7 @@ dependencies {
testCompile deps['org.hamcrest:hamcrest-all']
testCompile deps['org.hamcrest:hamcrest-core']
testCompile deps['org.hamcrest:hamcrest-library']
compile deps['org.hibernate:hibernate-core']
compile deps['org.hibernate:hibernate-hikaricp']
testCompile deps['junit:junit']
testCompile deps['org.mockito:mockito-core']
runtime deps['org.postgresql:postgresql']
@@ -112,7 +112,7 @@ public static String provideLogoFilename(RegistryConfigSettings config) {
}

/**
* The product name of this specific registry. Used throughout the registrar console.
* The product name of this specific registry. Used throughout the registrar console.
*
* @see google.registry.ui.server.registrar.ConsoleUiAction
*/
@@ -123,11 +123,11 @@ public static String provideProductName(RegistryConfigSettings config) {
}

/**
* Returns the roid suffix to be used for the roids of all contacts and hosts. E.g. a value of
* Returns the roid suffix to be used for the roids of all contacts and hosts. E.g. a value of
* "ROID" would end up creating roids that look like "ABC123-ROID".
*
* @see <a href="http://www.iana.org/assignments/epp-repository-ids/epp-repository-ids.xhtml">
* Extensible Provisioning Protocol (EPP) Repository Identifiers</a>
* Extensible Provisioning Protocol (EPP) Repository Identifiers</a>
*/
@Provides
@Config("contactAndHostRoidSuffix")
@@ -136,7 +136,7 @@ public static String provideContactAndHostRoidSuffix(RegistryConfigSettings conf
}

/**
* The e-mail address for questions about integrating with the registry. Used in the
* The e-mail address for questions about integrating with the registry. Used in the
* "contact-us" section of the registrar console.
*
* @see google.registry.ui.server.registrar.ConsoleUiAction
@@ -148,7 +148,7 @@ public static String provideIntegrationEmail(RegistryConfigSettings config) {
}

/**
* The e-mail address for general support. Used in the "contact-us" section of the registrar
* The e-mail address for general support. Used in the "contact-us" section of the registrar
* console.
*
* @see google.registry.ui.server.registrar.ConsoleUiAction
@@ -160,7 +160,7 @@ public static String provideSupportEmail(RegistryConfigSettings config) {
}

/**
* The "From" e-mail address for announcements. Used in the "contact-us" section of the
* The "From" e-mail address for announcements. Used in the "contact-us" section of the
* registrar console.
*
* @see google.registry.ui.server.registrar.ConsoleUiAction
@@ -172,7 +172,7 @@ public static String provideAnnouncementsEmail(RegistryConfigSettings config) {
}

/**
* The contact phone number. Used in the "contact-us" section of the registrar console.
* The contact phone number. Used in the "contact-us" section of the registrar console.
*
* @see google.registry.ui.server.registrar.ConsoleUiAction
*/
@@ -1040,8 +1040,8 @@ public static Duration provideMetricsWriteInterval(RegistryConfigSettings config
}

/**
* The global automatic transfer length for contacts. After this amount of time has
* elapsed, the transfer is automatically approved.
* The global automatic transfer length for contacts. After this amount of time has elapsed, the
* transfer is automatically approved.
*
* @see google.registry.flows.contact.ContactTransferRequestFlow
*/
@@ -1196,7 +1196,7 @@ static RegistryConfigSettings provideRegistryConfigSettings() {
/**
* Provides the OAuth scopes that authentication logic should detect on access tokens.
*
* <p>This list should be a superset of the required OAuth scope set provided below. Note that
* <p>This list should be a superset of the required OAuth scope set provided below. Note that
* ideally, this setting would not be required and all scopes on an access token would be
* detected automatically, but that is not the case due to the way {@code OAuthService} works.
*
@@ -1297,9 +1297,7 @@ public static String provideRdapTosStaticUrl(RegistryConfigSettings config) {
}
}

/**
* Returns the App Engine project ID, which is based off the environment name.
*/
/** Returns the App Engine project ID, which is based off the environment name. */
public static String getProjectId() {
return CONFIG_SETTINGS.get().appEngine.projectId;
}
@@ -1451,20 +1449,51 @@ public static String getDefaultRegistrarWhoisServer() {
return CONFIG_SETTINGS.get().registryPolicy.defaultRegistrarWhoisServer;
}

/**
* Returns the number of {@code EppResourceIndex} buckets to be used.
*/
/** Returns the number of {@code EppResourceIndex} buckets to be used. */
public static int getEppResourceIndexBucketCount() {
return CONFIG_SETTINGS.get().datastore.eppResourceIndexBucketsNum;
}

/**
* Returns the base retry duration that gets doubled after each failure within {@code Ofy}.
*/
/** Returns the base retry duration that gets doubled after each failure within {@code Ofy}. */
public static Duration getBaseOfyRetryDuration() {
return Duration.millis(CONFIG_SETTINGS.get().datastore.baseOfyRetryMillis);
}

/** 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 getHibernateLogSqlQueries() {
return CONFIG_SETTINGS.get().hibernate.logSqlQueries;
}

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

/** 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;
@@ -25,6 +25,7 @@
public CredentialOAuth credentialOAuth;
public RegistryPolicy registryPolicy;
public Datastore datastore;
public Hibernate hibernate;
public CloudDns cloudDns;
public Caching caching;
public IcannReporting icannReporting;
@@ -105,6 +106,17 @@
public int baseOfyRetryMillis;
}

/** Configuration for Hibernate. */
public static class Hibernate {
public String connectionIsolation;
public String logSqlQueries;
public String hbm2ddlAuto;
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;
@@ -191,6 +191,26 @@ 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 to App Engine logs. Overridable at runtime.
logSqlQueries: false

# Connection pool configurations.
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
@@ -0,0 +1,67 @@
// 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.persistence;

import static google.registry.config.RegistryConfig.getHibernateConnectionIsolation;
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.getHibernateLogSqlQueries;

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 the same as 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, "org.postgresql.Driver");
properties.put(
Environment.CONNECTION_PROVIDER,
"org.hibernate.hikaricp.internal.HikariCPConnectionProvider");
// Whether to automatically validate and export schema DDL to the database when the
// SessionFactory is created. Setting it to 'none' to turn off the feature.
properties.put(Environment.HBM2DDL_AUTO, "none");

properties.put(Environment.ISOLATION, getHibernateConnectionIsolation());
properties.put(Environment.SHOW_SQL, getHibernateLogSqlQueries());
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());
}
}
@@ -0,0 +1,28 @@
<?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.model.domain.DomainBase</class>
<class>google.registry.schema.tmch.ClaimsList</class>

<!-- TODO(weiminyu): check out application-layer validation. -->
<validation-mode>NONE</validation-mode>
</persistence-unit>
</persistence>
@@ -0,0 +1,55 @@
// 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.persistence;

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();
}
}
@@ -115,7 +115,7 @@ ext {
'org.hamcrest:hamcrest-all:1.3',
'org.hamcrest:hamcrest-core:1.3',
'org.hamcrest:hamcrest-library:1.3',
'org.hibernate:hibernate-core:5.4.4.Final',
'org.hibernate:hibernate-hikaricp:5.4.4.Final',
'org.joda:joda-money:0.10.0',
'org.json:json:20160810',
'org.mockito:mockito-core:2.25.0',
@@ -125,8 +125,8 @@ 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.testcontainers:postgresql:1.8.3',
'org.yaml:snakeyaml:1.17',
'xerces:xmlParserAPIs:2.6.2',
'xpp3:xpp3:1.1.4c'

0 comments on commit 487b695

Please sign in to comment.
You can’t perform that action at this time.