mirror of
https://github.com/google/nomulus
synced 2026-09-18 22:14:23 +00:00
Alt entity model for fast JPA bulk query (#1398)
* Alt entity model for fast JPA bulk query Defined an alternative JPA entity model that allows fast bulk loading of multi-level entities, DomainBase and DomainHistory. The idea is to bulk the base table as well as the child tables separately, and assemble them into the target entity in memory in a pipeline. For DomainBase: - Defined a DomainBaseLite class that models the "Domain" table only. - Defined a DomainHost class that models the "DomainHost" table (nsHosts field). - Exposed ID fields in GracePeriod so that they can be mapped to domains after being loaded into memory. For DomainHistory: - Defined a DomainHistoryLite class that models the "DomainHistory" table only. - Defined a DomainHistoryHost class that models its namesake table. - Exposed ID fields in GracePeriodHistory and DomainDsDataHistory classes so that they can be mapped to DomainHistory after being loaded into memory. In PersistenceModule, provisioned a JpaTransactionManager that uses the alternative entity model. Also added a pipeline option that specifies which JpaTransactionManager to use in a pipeline.
This commit is contained in:
@@ -19,6 +19,7 @@ import static google.registry.beam.common.RegistryPipelineOptions.validateRegist
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.persistence.PersistenceModule.JpaTransactionManagerType;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.testing.SystemPropertyExtension;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
@@ -123,4 +124,37 @@ class RegistryPipelineOptionsTest {
|
||||
validateRegistryPipelineOptions(options);
|
||||
assertThat(options.getProject()).isEqualTo("some-project");
|
||||
}
|
||||
|
||||
@Test
|
||||
void jpaTransactionManagerType_default() {
|
||||
RegistryPipelineOptions options =
|
||||
PipelineOptionsFactory.fromArgs(
|
||||
"--registryEnvironment=" + RegistryEnvironment.UNITTEST.name())
|
||||
.withValidation()
|
||||
.as(RegistryPipelineOptions.class);
|
||||
assertThat(options.getJpaTransactionManagerType()).isEqualTo(JpaTransactionManagerType.REGULAR);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jpaTransactionManagerType_regularJpa() {
|
||||
RegistryPipelineOptions options =
|
||||
PipelineOptionsFactory.fromArgs(
|
||||
"--registryEnvironment=" + RegistryEnvironment.UNITTEST.name(),
|
||||
"--jpaTransactionManagerType=REGULAR")
|
||||
.withValidation()
|
||||
.as(RegistryPipelineOptions.class);
|
||||
assertThat(options.getJpaTransactionManagerType()).isEqualTo(JpaTransactionManagerType.REGULAR);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jpaTransactionManagerType_bulkQueryJpa() {
|
||||
RegistryPipelineOptions options =
|
||||
PipelineOptionsFactory.fromArgs(
|
||||
"--registryEnvironment=" + RegistryEnvironment.UNITTEST.name(),
|
||||
"--jpaTransactionManagerType=BULK_QUERY")
|
||||
.withValidation()
|
||||
.as(RegistryPipelineOptions.class);
|
||||
assertThat(options.getJpaTransactionManagerType())
|
||||
.isEqualTo(JpaTransactionManagerType.BULK_QUERY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,6 +412,10 @@ public final class ImmutableObjectSubject extends Subject {
|
||||
// don't use ImmutableMap or a stream->collect model since we can have nulls
|
||||
Map<Field, Object> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<Field, Object> entry : originalFields.entrySet()) {
|
||||
// TODO(b/203685960): filter by @DoNotCompare instead.
|
||||
if (entry.getKey().isAnnotationPresent(ImmutableObject.Insignificant.class)) {
|
||||
continue;
|
||||
}
|
||||
if (!ignoredFieldSet.contains(entry.getKey().getName())) {
|
||||
result.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
@@ -426,7 +430,9 @@ public final class ImmutableObjectSubject extends Subject {
|
||||
// don't use ImmutableMap or a stream->collect model since we can have nulls
|
||||
Map<Field, Object> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<Field, Object> entry : originalFields.entrySet()) {
|
||||
if (!entry.getKey().isAnnotationPresent(annotation)) {
|
||||
// TODO(b/203685960): filter by @DoNotCompare instead.
|
||||
if (!entry.getKey().isAnnotationPresent(annotation)
|
||||
&& !entry.getKey().isAnnotationPresent(ImmutableObject.Insignificant.class)) {
|
||||
|
||||
// Perform any necessary substitutions.
|
||||
if (entry.getKey().isAnnotationPresent(ImmutableObject.EmptySetToNull.class)
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright 2021 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.bulkquery;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.GracePeriod.GracePeriodHistory;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.domain.secdns.DomainDsDataHistory;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.persistence.VKey;
|
||||
|
||||
/**
|
||||
* Helpers for bulk-loading {@link google.registry.model.domain.DomainBase} and {@link
|
||||
* google.registry.model.domain.DomainHistory} entities in <em>tests</em>.
|
||||
*/
|
||||
public class BulkQueryHelper {
|
||||
|
||||
static DomainBase loadAndAssembleDomainBase(String domainRepoId) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
BulkQueryEntities.assembleDomainBase(
|
||||
jpaTm().loadByKey(DomainBaseLite.createVKey(domainRepoId)),
|
||||
jpaTm()
|
||||
.loadAllOfStream(GracePeriod.class)
|
||||
.filter(gracePeriod -> gracePeriod.getDomainRepoId().equals(domainRepoId))
|
||||
.collect(toImmutableSet()),
|
||||
jpaTm()
|
||||
.loadAllOfStream(DelegationSignerData.class)
|
||||
.filter(dsData -> dsData.getDomainRepoId().equals(domainRepoId))
|
||||
.collect(toImmutableSet()),
|
||||
jpaTm()
|
||||
.loadAllOfStream(DomainHost.class)
|
||||
.filter(domainHost -> domainHost.getDomainRepoId().equals(domainRepoId))
|
||||
.map(DomainHost::getHostVKey)
|
||||
.collect(toImmutableSet())));
|
||||
}
|
||||
|
||||
static DomainHistory loadAndAssembleDomainHistory(DomainHistoryId domainHistoryId) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
BulkQueryEntities.assembleDomainHistory(
|
||||
jpaTm().loadByKey(VKey.createSql(DomainHistoryLite.class, domainHistoryId)),
|
||||
jpaTm()
|
||||
.loadAllOfStream(DomainDsDataHistory.class)
|
||||
.filter(
|
||||
domainDsDataHistory ->
|
||||
domainDsDataHistory.getDomainHistoryId().equals(domainHistoryId))
|
||||
.collect(toImmutableSet()),
|
||||
jpaTm()
|
||||
.loadAllOfStream(DomainHistoryHost.class)
|
||||
.filter(
|
||||
domainHistoryHost ->
|
||||
domainHistoryHost.getDomainHistoryId().equals(domainHistoryId))
|
||||
.map(DomainHistoryHost::getHostVKey)
|
||||
.collect(toImmutableSet()),
|
||||
jpaTm()
|
||||
.loadAllOfStream(GracePeriodHistory.class)
|
||||
.filter(
|
||||
gracePeriodHistory ->
|
||||
gracePeriodHistory.getDomainHistoryId().equals(domainHistoryId))
|
||||
.collect(toImmutableSet()),
|
||||
jpaTm()
|
||||
.loadAllOfStream(DomainTransactionRecord.class)
|
||||
.filter(x -> true)
|
||||
.collect(toImmutableSet())));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright 2021 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.bulkquery;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Sets.SetView;
|
||||
import com.google.common.truth.Truth8;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for reading {@link DomainBaseLite}. */
|
||||
class DomainBaseLiteTest {
|
||||
|
||||
protected FakeClock fakeClock = new FakeClock(DateTime.now(UTC));
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().withClock(fakeClock).build();
|
||||
|
||||
private final TestSetupHelper setupHelper = new TestSetupHelper(fakeClock);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
setupHelper.initializeAllEntities();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
setupHelper.tearDownBulkQueryJpaTm();
|
||||
}
|
||||
|
||||
@Test
|
||||
void readDomainHost() {
|
||||
setupHelper.applyChangeToDomainAndHistory();
|
||||
setupHelper.setupBulkQueryJpaTm(appEngine);
|
||||
Truth8.assertThat(
|
||||
jpaTm().transact(() -> jpaTm().loadAllOf(DomainHost.class)).stream()
|
||||
.map(DomainHost::getHostVKey))
|
||||
.containsExactly(setupHelper.host.createVKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void domainBaseLiteAttributes_versusDomainBase() {
|
||||
Set<String> domainBaseAttributes =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.getMetamodel()
|
||||
.entity(DomainBase.class)
|
||||
.getAttributes())
|
||||
.stream()
|
||||
.map(Attribute::getName)
|
||||
.collect(Collectors.toSet());
|
||||
setupHelper.setupBulkQueryJpaTm(appEngine);
|
||||
Set<String> domainBaseLiteAttributes =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.getMetamodel()
|
||||
.entity(DomainBaseLite.class)
|
||||
.getAttributes())
|
||||
.stream()
|
||||
.map(Attribute::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
assertThat(domainBaseAttributes).containsAtLeastElementsIn(domainBaseLiteAttributes);
|
||||
|
||||
SetView<?> excludedFromDomainBase =
|
||||
Sets.difference(domainBaseAttributes, domainBaseLiteAttributes);
|
||||
assertThat(excludedFromDomainBase)
|
||||
.containsExactly("internalDelegationSignerData", "internalGracePeriods", "nsHosts");
|
||||
}
|
||||
|
||||
@Test
|
||||
void readDomainBaseLite_simple() {
|
||||
setupHelper.setupBulkQueryJpaTm(appEngine);
|
||||
assertThat(BulkQueryHelper.loadAndAssembleDomainBase(TestSetupHelper.DOMAIN_REPO_ID))
|
||||
.isEqualTo(setupHelper.domain);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readDomainBaseLite_full() {
|
||||
setupHelper.applyChangeToDomainAndHistory();
|
||||
setupHelper.setupBulkQueryJpaTm(appEngine);
|
||||
assertThat(BulkQueryHelper.loadAndAssembleDomainBase(TestSetupHelper.DOMAIN_REPO_ID))
|
||||
.isEqualTo(setupHelper.domain);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
// Copyright 2021 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.bulkquery;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Sets.SetView;
|
||||
import com.google.common.truth.Truth8;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link DomainHistoryLite}. */
|
||||
public class DomainHistoryLiteTest {
|
||||
|
||||
protected FakeClock fakeClock = new FakeClock(DateTime.now(UTC));
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().withClock(fakeClock).build();
|
||||
|
||||
private final TestSetupHelper setupHelper = new TestSetupHelper(fakeClock);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
setupHelper.initializeAllEntities();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
setupHelper.tearDownBulkQueryJpaTm();
|
||||
}
|
||||
|
||||
@Test
|
||||
void readDomainHistoryHost() {
|
||||
setupHelper.applyChangeToDomainAndHistory();
|
||||
setupHelper.setupBulkQueryJpaTm(appEngine);
|
||||
Truth8.assertThat(
|
||||
jpaTm().transact(() -> jpaTm().loadAllOf(DomainHistoryHost.class)).stream()
|
||||
.map(DomainHistoryHost::getHostVKey))
|
||||
.containsExactly(setupHelper.host.createVKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void domainHistoryLiteAttributes_versusDomainHistory() {
|
||||
Set<String> domainHistoryAttributes =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.getMetamodel()
|
||||
.entity(DomainHistory.class)
|
||||
.getAttributes())
|
||||
.stream()
|
||||
.map(Attribute::getName)
|
||||
.collect(Collectors.toSet());
|
||||
setupHelper.setupBulkQueryJpaTm(appEngine);
|
||||
Set<String> domainHistoryLiteAttributes =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.getMetamodel()
|
||||
.entity(DomainHistoryLite.class)
|
||||
.getAttributes())
|
||||
.stream()
|
||||
.map(Attribute::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
assertThat(domainHistoryAttributes).containsAtLeastElementsIn(domainHistoryLiteAttributes);
|
||||
|
||||
SetView<?> excludedFromDomainHistory =
|
||||
Sets.difference(domainHistoryAttributes, domainHistoryLiteAttributes);
|
||||
assertThat(excludedFromDomainHistory)
|
||||
.containsExactly(
|
||||
"dsDataHistories",
|
||||
"gracePeriodHistories",
|
||||
"internalDomainTransactionRecords",
|
||||
"nsHosts");
|
||||
}
|
||||
|
||||
@Test
|
||||
void readDomainHistory_noContent() {
|
||||
setupHelper.setupBulkQueryJpaTm(appEngine);
|
||||
assertThat(
|
||||
BulkQueryHelper.loadAndAssembleDomainHistory(
|
||||
setupHelper.domainHistory.getDomainHistoryId()))
|
||||
.isEqualTo(setupHelper.domainHistory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readDomainHistory_full() {
|
||||
setupHelper.applyChangeToDomainAndHistory();
|
||||
setupHelper.setupBulkQueryJpaTm(appEngine);
|
||||
assertThat(
|
||||
BulkQueryHelper.loadAndAssembleDomainHistory(
|
||||
setupHelper.domainHistory.getDomainHistoryId()))
|
||||
.isEqualTo(setupHelper.domainHistory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
// Copyright 2021 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.bulkquery;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.launch.LaunchNotice;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.transfer.ContactTransferData;
|
||||
import google.registry.persistence.BulkQueryJpaFactory;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.persistence.transaction.TransactionManagerFactory;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
|
||||
/** Entity creation utilities for domain-related tests. */
|
||||
class TestSetupHelper {
|
||||
|
||||
public static final String TLD = "tld";
|
||||
public static final String DOMAIN_REPO_ID = "4-TLD";
|
||||
public static final String DOMAIN_NAME = "example.tld";
|
||||
public static final String REGISTRAR_ID = "AnRegistrar";
|
||||
|
||||
private final FakeClock fakeClock;
|
||||
|
||||
Registry registry;
|
||||
Registrar registrar;
|
||||
ContactResource contact;
|
||||
DomainBase domain;
|
||||
DomainHistory domainHistory;
|
||||
HostResource host;
|
||||
|
||||
private JpaTransactionManager originalJpaTm;
|
||||
private JpaTransactionManager bulkQueryJpaTm;
|
||||
|
||||
TestSetupHelper(FakeClock fakeClock) {
|
||||
this.fakeClock = fakeClock;
|
||||
}
|
||||
|
||||
void initializeAllEntities() {
|
||||
registry = putInDb(DatabaseHelper.newRegistry(TLD, Ascii.toUpperCase(TLD)));
|
||||
registrar = saveRegistrar(REGISTRAR_ID);
|
||||
contact = putInDb(createContact(DOMAIN_REPO_ID, REGISTRAR_ID));
|
||||
domain = putInDb(createSimpleDomain(contact));
|
||||
domainHistory = putInDb(createHistoryWithoutContent(domain, fakeClock));
|
||||
host = putInDb(createHost());
|
||||
}
|
||||
|
||||
void applyChangeToDomainAndHistory() {
|
||||
domain = putInDb(createFullDomain(contact, host, fakeClock));
|
||||
domainHistory = putInDb(createFullHistory(domain, fakeClock));
|
||||
}
|
||||
|
||||
void setupBulkQueryJpaTm(AppEngineExtension appEngineExtension) {
|
||||
bulkQueryJpaTm =
|
||||
BulkQueryJpaFactory.createBulkQueryJpaTransactionManager(
|
||||
appEngineExtension
|
||||
.getJpaIntegrationTestExtension()
|
||||
.map(JpaIntegrationTestExtension::getJpaProperties)
|
||||
.orElseThrow(
|
||||
() -> new IllegalStateException("Expecting JpaIntegrationTestExtension.")),
|
||||
fakeClock);
|
||||
originalJpaTm = TransactionManagerFactory.jpaTm();
|
||||
TransactionManagerFactory.setJpaTm(() -> bulkQueryJpaTm);
|
||||
}
|
||||
|
||||
void tearDownBulkQueryJpaTm() {
|
||||
if (bulkQueryJpaTm != null) {
|
||||
bulkQueryJpaTm.teardown();
|
||||
TransactionManagerFactory.setJpaTm(() -> originalJpaTm);
|
||||
}
|
||||
}
|
||||
|
||||
static ContactResource createContact(String repoId, String registrarId) {
|
||||
return new ContactResource.Builder()
|
||||
.setRepoId(repoId)
|
||||
.setCreationRegistrarId(registrarId)
|
||||
.setTransferData(new ContactTransferData.Builder().build())
|
||||
.setPersistedCurrentSponsorRegistrarId(registrarId)
|
||||
.build();
|
||||
}
|
||||
|
||||
static DomainBase createSimpleDomain(ContactResource contact) {
|
||||
return DatabaseHelper.newDomainBase(DOMAIN_NAME, DOMAIN_REPO_ID, contact)
|
||||
.asBuilder()
|
||||
.setCreationRegistrarId(REGISTRAR_ID)
|
||||
.setPersistedCurrentSponsorRegistrarId(REGISTRAR_ID)
|
||||
.build();
|
||||
}
|
||||
|
||||
static DomainBase createFullDomain(
|
||||
ContactResource contact, HostResource host, FakeClock fakeClock) {
|
||||
return createSimpleDomain(contact)
|
||||
.asBuilder()
|
||||
.setDomainName(DOMAIN_NAME)
|
||||
.setRepoId(DOMAIN_REPO_ID)
|
||||
.setCreationRegistrarId(REGISTRAR_ID)
|
||||
.setLastEppUpdateTime(fakeClock.nowUtc())
|
||||
.setLastEppUpdateRegistrarId(REGISTRAR_ID)
|
||||
.setLastTransferTime(fakeClock.nowUtc())
|
||||
.setNameservers(host.createVKey())
|
||||
.setStatusValues(
|
||||
ImmutableSet.of(
|
||||
StatusValue.CLIENT_DELETE_PROHIBITED,
|
||||
StatusValue.SERVER_DELETE_PROHIBITED,
|
||||
StatusValue.SERVER_TRANSFER_PROHIBITED,
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED,
|
||||
StatusValue.SERVER_RENEW_PROHIBITED,
|
||||
StatusValue.SERVER_HOLD))
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(DesignatedContact.Type.ADMIN, contact.createVKey())))
|
||||
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
|
||||
.setPersistedCurrentSponsorRegistrarId(REGISTRAR_ID)
|
||||
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
|
||||
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
|
||||
.setLaunchNotice(LaunchNotice.create("tcnid", "validatorId", START_OF_TIME, START_OF_TIME))
|
||||
.setSmdId("smdid")
|
||||
.addGracePeriod(
|
||||
GracePeriod.create(
|
||||
GracePeriodStatus.ADD, DOMAIN_REPO_ID, END_OF_TIME, REGISTRAR_ID, null, 100L))
|
||||
.build();
|
||||
}
|
||||
|
||||
static HostResource createHost() {
|
||||
return new HostResource.Builder()
|
||||
.setRepoId("host1")
|
||||
.setHostName("ns1.example.com")
|
||||
.setCreationRegistrarId(REGISTRAR_ID)
|
||||
.setPersistedCurrentSponsorRegistrarId(REGISTRAR_ID)
|
||||
.build();
|
||||
}
|
||||
|
||||
static DomainTransactionRecord createDomainTransactionRecord(FakeClock fakeClock) {
|
||||
return new DomainTransactionRecord.Builder()
|
||||
.setTld(TLD)
|
||||
.setReportingTime(fakeClock.nowUtc())
|
||||
.setReportField(TransactionReportField.NET_ADDS_1_YR)
|
||||
.setReportAmount(1)
|
||||
.build();
|
||||
}
|
||||
|
||||
static DomainHistory createHistoryWithoutContent(DomainBase domain, FakeClock fakeClock) {
|
||||
return new DomainHistory.Builder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
|
||||
.setModificationTime(fakeClock.nowUtc())
|
||||
.setRegistrarId(REGISTRAR_ID)
|
||||
.setTrid(Trid.create("ABC-123", "server-trid"))
|
||||
.setBySuperuser(false)
|
||||
.setReason("reason")
|
||||
.setRequestedByRegistrar(true)
|
||||
.setDomainRepoId(domain.getRepoId())
|
||||
.setOtherRegistrarId("otherClient")
|
||||
.setPeriod(Period.create(1, Period.Unit.YEARS))
|
||||
.build();
|
||||
}
|
||||
|
||||
static DomainHistory createFullHistory(DomainBase domain, FakeClock fakeClock) {
|
||||
return createHistoryWithoutContent(domain, fakeClock)
|
||||
.asBuilder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE)
|
||||
.setDomain(domain)
|
||||
.setDomainTransactionRecords(ImmutableSet.of(createDomainTransactionRecord(fakeClock)))
|
||||
.build();
|
||||
}
|
||||
|
||||
static <T> T putInDb(T entity) {
|
||||
jpaTm().transact(() -> jpaTm().put(entity));
|
||||
return jpaTm().transact(() -> jpaTm().loadByEntity(entity));
|
||||
}
|
||||
}
|
||||
+27
-21
@@ -45,7 +45,6 @@ import java.sql.Driver;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -172,24 +171,39 @@ abstract class JpaTransactionManagerExtension implements BeforeEachCallback, Aft
|
||||
exporter.export(getTestEntities(), tempSqlFile);
|
||||
executeSql(new String(Files.readAllBytes(tempSqlFile.toPath()), StandardCharsets.UTF_8));
|
||||
}
|
||||
assertReasonableNumDbConnections();
|
||||
emf = createEntityManagerFactory(getJpaProperties());
|
||||
emfEntityHash = entityHash;
|
||||
}
|
||||
|
||||
ImmutableMap<String, String> properties = PersistenceModule.provideDefaultDatabaseConfigs();
|
||||
/**
|
||||
* Returns the full set of properties for setting up JPA {@link EntityManagerFactory} to the test
|
||||
* database. This allows creation of customized JPA by individual tests.
|
||||
*
|
||||
* <p>Test that create {@code EntityManagerFactory} instances are reponsible for tearing them
|
||||
* down.
|
||||
*/
|
||||
public ImmutableMap<String, String> getJpaProperties() {
|
||||
Map<String, String> mergedProperties =
|
||||
Maps.newHashMap(PersistenceModule.provideDefaultDatabaseConfigs());
|
||||
if (!userProperties.isEmpty()) {
|
||||
// If there are user properties, create a new properties object with these added.
|
||||
Map<String, String> mergedProperties = Maps.newHashMap();
|
||||
mergedProperties.putAll(properties);
|
||||
mergedProperties.putAll(userProperties);
|
||||
properties = ImmutableMap.copyOf(mergedProperties);
|
||||
}
|
||||
mergedProperties.put(Environment.URL, getJdbcUrl());
|
||||
mergedProperties.put(Environment.USER, database.getUsername());
|
||||
mergedProperties.put(Environment.PASS, database.getPassword());
|
||||
// Tell Postgresql JDBC driver to retry on errors caused by out-of-band schema change between
|
||||
// tests while the connection pool stays open (e.g., "cached plan must not change result type").
|
||||
// We don't set this property in production since it has performance impact, and production
|
||||
// schema is always compatible with the binary (enforced by our release process).
|
||||
mergedProperties.put("hibernate.hikari.dataSource.autosave", "conservative");
|
||||
|
||||
// Forbid Hibernate push to stay consistent with flyway-based schema management.
|
||||
checkState(
|
||||
Objects.equals(properties.get(Environment.HBM2DDL_AUTO), "none"),
|
||||
Objects.equals(mergedProperties.get(Environment.HBM2DDL_AUTO), "none"),
|
||||
"The HBM2DDL_AUTO property must be 'none'.");
|
||||
assertReasonableNumDbConnections();
|
||||
emf =
|
||||
createEntityManagerFactory(
|
||||
getJdbcUrl(), database.getUsername(), database.getPassword(), properties);
|
||||
emfEntityHash = entityHash;
|
||||
|
||||
return ImmutableMap.copyOf(mergedProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -307,15 +321,7 @@ abstract class JpaTransactionManagerExtension implements BeforeEachCallback, Aft
|
||||
}
|
||||
|
||||
/** Constructs the {@link EntityManagerFactory} instance. */
|
||||
private EntityManagerFactory createEntityManagerFactory(
|
||||
String jdbcUrl, String username, String password, ImmutableMap<String, String> configs) {
|
||||
HashMap<String, String> properties = Maps.newHashMap(configs);
|
||||
properties.put(Environment.URL, jdbcUrl);
|
||||
properties.put(Environment.USER, username);
|
||||
properties.put(Environment.PASS, password);
|
||||
// Tell Postgresql JDBC driver to expect out-of-band schema change.
|
||||
properties.put("hibernate.hikari.dataSource.autosave", "conservative");
|
||||
|
||||
private EntityManagerFactory createEntityManagerFactory(ImmutableMap<String, String> properties) {
|
||||
ParsedPersistenceXmlDescriptor descriptor =
|
||||
PersistenceXmlUtility.getParsedPersistenceXmlDescriptor();
|
||||
|
||||
|
||||
@@ -146,6 +146,10 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
|
||||
private ImmutableList<Class<?>> ofyTestEntities;
|
||||
private ImmutableList<Class<?>> jpaTestEntities;
|
||||
|
||||
public Optional<JpaIntegrationTestExtension> getJpaIntegrationTestExtension() {
|
||||
return Optional.ofNullable(jpaIntegrationTestExtension);
|
||||
}
|
||||
|
||||
/** Builder for {@link AppEngineExtension}. */
|
||||
public static class Builder {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user