Use a more efficient query to find resources in histories (#1354)

This commit is contained in:
gbrodman 2021-10-06 15:20:31 -04:00 committed by GitHub
parent c8e51b0e53
commit 1e6287372d
2 changed files with 24 additions and 22 deletions

View file

@ -213,6 +213,7 @@ PRESUBMITS = {
{"src/test", "ActivityReportingQueryBuilder.java", {"src/test", "ActivityReportingQueryBuilder.java",
# This class contains helper method to make queries in Beam. # This class contains helper method to make queries in Beam.
"RegistryJpaIO.java", "RegistryJpaIO.java",
"CreateSyntheticHistoryEntriesAction.java",
# TODO(b/179158393): Remove everything below, which should be done # TODO(b/179158393): Remove everything below, which should be done
# using Criteria # using Criteria
"JpaTransactionManager.java", "JpaTransactionManager.java",

View file

@ -21,6 +21,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm; import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import com.google.appengine.tools.mapreduce.Mapper; import com.google.appengine.tools.mapreduce.Mapper;
import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
@ -29,16 +30,12 @@ import google.registry.mapreduce.inputs.EppResourceInputs;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import google.registry.rde.RdeStagingAction; import google.registry.rde.RdeStagingAction;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.Response; import google.registry.request.Response;
import google.registry.request.auth.Auth; import google.registry.request.auth.Auth;
import google.registry.tools.server.GenerateZoneFilesAction; import google.registry.tools.server.GenerateZoneFilesAction;
import java.util.Optional;
import javax.inject.Inject; import javax.inject.Inject;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
/** /**
* A mapreduce that creates synthetic history objects in SQL for all {@link EppResource} objects. * A mapreduce that creates synthetic history objects in SQL for all {@link EppResource} objects.
@ -105,8 +102,9 @@ public class CreateSyntheticHistoryEntriesAction implements Runnable {
.sendLinkToMapreduceConsole(response); .sendLinkToMapreduceConsole(response);
} }
// Lifted from HistoryEntryDao // Returns true iff any of the *History objects in SQL contain a representation of this resource
private static Optional<? extends HistoryEntry> mostRecentHistoryFromSql(EppResource resource) { // at the point in time that the *History object was created.
private static boolean hasHistoryContainingResource(EppResource resource) {
return jpaTm() return jpaTm()
.transact( .transact(
() -> { () -> {
@ -114,18 +112,24 @@ public class CreateSyntheticHistoryEntriesAction implements Runnable {
Class<? extends HistoryEntry> historyClass = Class<? extends HistoryEntry> historyClass =
getHistoryClassFromParent(resource.getClass()); getHistoryClassFromParent(resource.getClass());
// The field representing repo ID unfortunately varies by history class // The field representing repo ID unfortunately varies by history class
String repoIdFieldName = getRepoIdFieldNameFromHistoryClass(historyClass); String repoIdFieldName =
CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder(); CaseFormat.LOWER_CAMEL.to(
CriteriaQuery<? extends HistoryEntry> criteriaQuery = CaseFormat.LOWER_UNDERSCORE,
CriteriaQueryBuilder.create(historyClass) getRepoIdFieldNameFromHistoryClass(historyClass));
.where(repoIdFieldName, criteriaBuilder::equal, resource.getRepoId()) // The "history" fields in the *History objects are all prefixed with "history_". If
.orderByDesc("modificationTime") // any of the non-"history_" fields are non-null, that means that that row contains
.build(); // a representation of that EppResource at that point in time. We use creation_time as
return jpaTm() // a marker since it's the simplest field and all EppResources will have it.
.criteriaQuery(criteriaQuery) return (boolean)
.setMaxResults(1) jpaTm()
.getResultStream() .getEntityManager()
.findFirst(); .createNativeQuery(
String.format(
"SELECT EXISTS (SELECT 1 FROM \"%s\" WHERE %s = :repoId AND"
+ " creation_time IS NOT NULL)",
historyClass.getSimpleName(), repoIdFieldName))
.setParameter("repoId", resource.getRepoId())
.getSingleResult();
}); });
} }
@ -164,10 +168,7 @@ public class CreateSyntheticHistoryEntriesAction implements Runnable {
EppResource eppResource = auditedOfy().load().key(resourceKey).now(); EppResource eppResource = auditedOfy().load().key(resourceKey).now();
// Only save new history entries if the most recent history for this object in SQL does not // Only save new history entries if the most recent history for this object in SQL does not
// have the resource at that point in time already // have the resource at that point in time already
Optional<? extends HistoryEntry> maybeHistory = mostRecentHistoryFromSql(eppResource); if (!hasHistoryContainingResource(eppResource)) {
if (maybeHistory
.map(history -> !history.getResourceAtPointInTime().isPresent())
.orElse(true)) {
ofyTm() ofyTm()
.transact( .transact(
() -> () ->