Delete EppResourceIndex and EppResourceIndexBucket (#1774)

This commit is contained in:
Lai Jiang 2022-09-15 10:50:22 -04:00 committed by GitHub
parent b8f1b2fc4f
commit e6fde54966
17 changed files with 22 additions and 290 deletions

View file

@ -44,7 +44,7 @@ import javax.inject.Inject;
/** /**
* Hard deletes load-test Contacts, Hosts, their subordinate history entries, and the associated * Hard deletes load-test Contacts, Hosts, their subordinate history entries, and the associated
* ForeignKey and EppResourceIndex entities. * ForeignKey entities.
* *
* <p>This only deletes contacts and hosts, NOT domains. To delete domains, use {@link * <p>This only deletes contacts and hosts, NOT domains. To delete domains, use {@link
* DeleteProberDataAction} and pass it the TLD(s) that the load test domains were created on. Note * DeleteProberDataAction} and pass it the TLD(s) that the load test domains were created on. Note

View file

@ -53,7 +53,7 @@ import org.joda.time.Duration;
/** /**
* Deletes all prober {@link Domain}s and their subordinate history entries, poll messages, and * Deletes all prober {@link Domain}s and their subordinate history entries, poll messages, and
* billing events, along with their ForeignKeyDomainIndex and EppResourceIndex entities. * billing events, along with their ForeignKeyDomainIndex entities.
*/ */
@Action( @Action(
service = Action.Service.BACKEND, service = Action.Service.BACKEND,

View file

@ -1482,11 +1482,6 @@ public final class RegistryConfig {
return CONFIG_SETTINGS.get().registryPolicy.defaultRegistrarWhoisServer; return CONFIG_SETTINGS.get().registryPolicy.defaultRegistrarWhoisServer;
} }
/** 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() { public static Duration getBaseOfyRetryDuration() {
return Duration.millis(CONFIG_SETTINGS.get().datastore.baseOfyRetryMillis); return Duration.millis(CONFIG_SETTINGS.get().datastore.baseOfyRetryMillis);

View file

@ -108,7 +108,6 @@ public class RegistryConfigSettings {
/** Configuration for Cloud Datastore. */ /** Configuration for Cloud Datastore. */
public static class Datastore { public static class Datastore {
public int eppResourceIndexBucketsNum;
public int baseOfyRetryMillis; public int baseOfyRetryMillis;
} }

View file

@ -183,10 +183,6 @@ registryPolicy:
requireSslCertificates: true requireSslCertificates: true
datastore: datastore:
# Number of EPP resource index buckets in Datastore. Dont change after
# initial install.
eppResourceIndexBucketsNum: 997
# Milliseconds that Objectify waits to retry a Datastore transaction (this # Milliseconds that Objectify waits to retry a Datastore transaction (this
# doubles after each failure). # doubles after each failure).
baseOfyRetryMillis: 100 baseOfyRetryMillis: 100

View file

@ -23,7 +23,6 @@ import static google.registry.model.IdService.allocateId;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
@ -40,7 +39,6 @@ import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CreateData.ContactCreateData; import google.registry.model.eppoutput.CreateData.ContactCreateData;
import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import javax.inject.Inject; import javax.inject.Inject;
@ -95,11 +93,7 @@ public final class ContactCreateFlow implements TransactionalFlow {
.setType(HistoryEntry.Type.CONTACT_CREATE) .setType(HistoryEntry.Type.CONTACT_CREATE)
.setXmlBytes(null) // We don't want to store contact details in the history entry. .setXmlBytes(null) // We don't want to store contact details in the history entry.
.setContact(newContact); .setContact(newContact);
tm().insertAll( tm().insertAll(ImmutableSet.of(newContact, historyBuilder.build()));
ImmutableSet.of(
newContact,
historyBuilder.build(),
EppResourceIndex.create(Key.create(newContact))));
return responseBuilder return responseBuilder
.setResData(ContactCreateData.create(newContact.getContactId(), now)) .setResData(ContactCreateData.create(newContact.getContactId(), now))
.build(); .build();

View file

@ -59,7 +59,6 @@ import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
import com.googlecode.objectify.Key;
import google.registry.dns.DnsQueue; import google.registry.dns.DnsQueue;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.CommandUseErrorException;
@ -105,7 +104,6 @@ import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CreateData.DomainCreateData; import google.registry.model.eppoutput.CreateData.DomainCreateData;
import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse; import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.poll.PollMessage.Autorenew; import google.registry.model.poll.PollMessage.Autorenew;
@ -404,10 +402,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
entitiesToSave.add( entitiesToSave.add(
createNameCollisionOneTimePollMessage(targetId, domainHistory, registrarId, now)); createNameCollisionOneTimePollMessage(targetId, domainHistory, registrarId, now));
} }
entitiesToSave.add( entitiesToSave.add(domain, domainHistory);
domain,
domainHistory,
EppResourceIndex.create(Key.create(domain)));
if (allocationToken.isPresent() if (allocationToken.isPresent()
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) { && TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
entitiesToSave.add( entitiesToSave.add(

View file

@ -27,7 +27,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.isNullOrEmpty;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.dns.DnsQueue; import google.registry.dns.DnsQueue;
import google.registry.flows.EppException; import google.registry.flows.EppException;
@ -49,7 +48,6 @@ import google.registry.model.eppoutput.EppResponse;
import google.registry.model.host.Host; import google.registry.model.host.Host;
import google.registry.model.host.HostCommand.Create; import google.registry.model.host.HostCommand.Create;
import google.registry.model.host.HostHistory; import google.registry.model.host.HostHistory;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import java.util.Optional; import java.util.Optional;
import javax.inject.Inject; import javax.inject.Inject;
@ -106,7 +104,7 @@ public final class HostCreateFlow implements TransactionalFlow {
DateTime now = tm().getTransactionTime(); DateTime now = tm().getTransactionTime();
verifyResourceDoesNotExist(Host.class, targetId, now, registrarId); verifyResourceDoesNotExist(Host.class, targetId, now, registrarId);
// The superordinate domain of the host object if creating an in-bailiwick host, or null if // The superordinate domain of the host object if creating an in-bailiwick host, or null if
// creating an external host. This is looked up before we actually create the Host object so // creating an external host. This is looked up before we actually create the Host object, so
// we can detect error conditions earlier. // we can detect error conditions earlier.
Optional<Domain> superordinateDomain = Optional<Domain> superordinateDomain =
lookupSuperordinateDomain(validateHostName(targetId), now); lookupSuperordinateDomain(validateHostName(targetId), now);
@ -130,11 +128,7 @@ public final class HostCreateFlow implements TransactionalFlow {
.setSuperordinateDomain(superordinateDomain.map(Domain::createVKey).orElse(null)) .setSuperordinateDomain(superordinateDomain.map(Domain::createVKey).orElse(null))
.build(); .build();
historyBuilder.setType(HOST_CREATE).setHost(newHost); historyBuilder.setType(HOST_CREATE).setHost(newHost);
ImmutableSet<ImmutableObject> entitiesToSave = ImmutableSet<ImmutableObject> entitiesToSave = ImmutableSet.of(newHost, historyBuilder.build());
ImmutableSet.of(
newHost,
historyBuilder.build(),
EppResourceIndex.create(Key.create(newHost)));
if (superordinateDomain.isPresent()) { if (superordinateDomain.isPresent()) {
tm().update( tm().update(
superordinateDomain superordinateDomain
@ -152,14 +146,14 @@ public final class HostCreateFlow implements TransactionalFlow {
/** Subordinate hosts must have an ip address. */ /** Subordinate hosts must have an ip address. */
static class SubordinateHostMustHaveIpException extends RequiredParameterMissingException { static class SubordinateHostMustHaveIpException extends RequiredParameterMissingException {
public SubordinateHostMustHaveIpException() { SubordinateHostMustHaveIpException() {
super("Subordinate hosts must have an ip address"); super("Subordinate hosts must have an ip address");
} }
} }
/** External hosts must not have ip addresses. */ /** External hosts must not have ip addresses. */
static class UnexpectedExternalHostIpException extends ParameterValueRangeErrorException { static class UnexpectedExternalHostIpException extends ParameterValueRangeErrorException {
public UnexpectedExternalHostIpException() { UnexpectedExternalHostIpException() {
super("External hosts must not have ip addresses"); super("External hosts must not have ip addresses");
} }
} }

View file

@ -23,8 +23,6 @@ import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.host.Host; import google.registry.model.host.Host;
import google.registry.model.host.HostHistory; import google.registry.model.host.HostHistory;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.EppResourceIndexBucket;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
/** Sets of classes of the Objectify-registered entities in use throughout the model. */ /** Sets of classes of the Objectify-registered entities in use throughout the model. */
@ -38,8 +36,6 @@ public final class EntityClasses {
ContactHistory.class, ContactHistory.class,
Domain.class, Domain.class,
DomainHistory.class, DomainHistory.class,
EppResourceIndex.class,
EppResourceIndexBucket.class,
GaeUserIdConverter.class, GaeUserIdConverter.class,
HistoryEntry.class, HistoryEntry.class,
Host.class, Host.class,

View file

@ -1,79 +0,0 @@
// Copyright 2017 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.index;
import static google.registry.util.TypeUtils.instantiate;
import com.google.common.annotations.VisibleForTesting;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.BackupGroupRoot;
import google.registry.model.EppResource;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.annotations.ReportedOn;
import google.registry.persistence.VKey;
/** An index that allows for quick enumeration of all EppResource entities (e.g. via map reduce). */
@ReportedOn
@Entity
@DeleteAfterMigration
public class EppResourceIndex extends BackupGroupRoot {
@Id String id;
@Parent Key<EppResourceIndexBucket> bucket;
/** Although this field holds a {@link Key} it is named "reference" for historical reasons. */
Key<? extends EppResource> reference;
@Index String kind;
public String getId() {
return id;
}
public String getKind() {
return kind;
}
public Key<? extends EppResource> getKey() {
return reference;
}
@VisibleForTesting
public Key<EppResourceIndexBucket> getBucket() {
return bucket;
}
@VisibleForTesting
public static <T extends EppResource> EppResourceIndex create(
Key<EppResourceIndexBucket> bucket, Key<T> resourceKey) {
EppResourceIndex instance = instantiate(EppResourceIndex.class);
instance.reference = resourceKey;
instance.kind = resourceKey.getKind();
// creates a web-safe key string, this value is never used
// TODO(b/211785379): remove unused id
instance.id = VKey.from(resourceKey).stringify();
instance.bucket = bucket;
return instance;
}
public static <T extends EppResource> EppResourceIndex create(Key<T> resourceKey) {
return create(EppResourceIndexBucket.getBucketKey(resourceKey), resourceKey);
}
}

View file

@ -1,68 +0,0 @@
// Copyright 2017 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.index;
import static google.registry.config.RegistryConfig.getEppResourceIndexBucketCount;
import com.google.common.collect.ImmutableList;
import com.google.common.hash.Hashing;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.annotations.VirtualEntity;
/** A virtual entity to represent buckets to which EppResourceIndex objects are randomly added. */
@Entity
@VirtualEntity
@DeleteAfterMigration
public class EppResourceIndexBucket extends ImmutableObject {
@SuppressWarnings("unused")
@Id
private long bucketId;
/**
* Deterministic function that returns a bucket id based on the resource's roid.
* NB: At the moment, nothing depends on this being deterministic, so we have the ability to
* change the number of buckets and utilize a random distribution once we do.
*/
private static long getBucketIdFromEppResource(Key<? extends EppResource> resourceKey) {
int numBuckets = getEppResourceIndexBucketCount();
// IDs can't be 0, so add 1 to the hash.
return Hashing.consistentHash(resourceKey.getName().hashCode(), numBuckets) + 1L;
}
/** Gets a bucket key as a function of an EppResource to be indexed. */
public static Key<EppResourceIndexBucket> getBucketKey(Key<? extends EppResource> resourceKey) {
return Key.create(EppResourceIndexBucket.class, getBucketIdFromEppResource(resourceKey));
}
/** Gets the specified numbered bucket key. */
public static Key<EppResourceIndexBucket> getBucketKey(int bucketId) {
return Key.create(EppResourceIndexBucket.class, bucketId);
}
/** Returns the keys to all buckets. */
public static Iterable<Key<EppResourceIndexBucket>> getAllBuckets() {
ImmutableList.Builder<Key<EppResourceIndexBucket>> builder = new ImmutableList.Builder<>();
for (int bucketId = 1; bucketId <= getEppResourceIndexBucketCount(); bucketId++) {
builder.add(getBucketKey(bucketId));
}
return builder.build();
}
}

View file

@ -30,7 +30,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams; import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.index.EppResourceIndex;
import google.registry.persistence.JpaRetries; import google.registry.persistence.JpaRetries;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.util.Clock; import google.registry.util.Clock;
@ -74,14 +73,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final Retrier retrier = new Retrier(new SystemSleeper(), 3); private static final Retrier retrier = new Retrier(new SystemSleeper(), 3);
// The entity of classes in this set will be simply ignored when passed to modification
// operations, i.e. insert, put, update and delete. This is to help maintain a single code path
// when we switch from ofy to tm() for the database migration as we don't need have a condition
// to exclude the Datastore specific entities when the underlying tm() is jpaTm().
// TODO(b/176108270): Remove this property after database migration.
private static final ImmutableSet<Class<? extends ImmutableObject>> IGNORED_ENTITY_CLASSES =
ImmutableSet.of(EppResourceIndex.class);
// EntityManagerFactory is thread safe. // EntityManagerFactory is thread safe.
private final EntityManagerFactory emf; private final EntityManagerFactory emf;
private final Clock clock; private final Clock clock;
@ -267,9 +258,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
@Override @Override
public void insert(Object entity) { public void insert(Object entity) {
checkArgumentNotNull(entity, "entity must be specified"); checkArgumentNotNull(entity, "entity must be specified");
if (isEntityOfIgnoredClass(entity)) {
return;
}
assertInTransaction(); assertInTransaction();
transactionInfo.get().insertObject(entity); transactionInfo.get().insertObject(entity);
} }
@ -289,9 +277,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
@Override @Override
public void put(Object entity) { public void put(Object entity) {
checkArgumentNotNull(entity, "entity must be specified"); checkArgumentNotNull(entity, "entity must be specified");
if (isEntityOfIgnoredClass(entity)) {
return;
}
assertInTransaction(); assertInTransaction();
transactionInfo.get().updateObject(entity); transactionInfo.get().updateObject(entity);
} }
@ -315,9 +300,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
@Override @Override
public void update(Object entity) { public void update(Object entity) {
checkArgumentNotNull(entity, "entity must be specified"); checkArgumentNotNull(entity, "entity must be specified");
if (isEntityOfIgnoredClass(entity)) {
return;
}
assertInTransaction(); assertInTransaction();
checkArgument(exists(entity), "Given entity does not exist"); checkArgument(exists(entity), "Given entity does not exist");
transactionInfo.get().updateObject(entity); transactionInfo.get().updateObject(entity);
@ -472,9 +454,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
private int internalDelete(VKey<?> key) { private int internalDelete(VKey<?> key) {
checkArgumentNotNull(key, "key must be specified"); checkArgumentNotNull(key, "key must be specified");
assertInTransaction(); assertInTransaction();
if (IGNORED_ENTITY_CLASSES.contains(key.getKind())) {
return 0;
}
EntityType<?> entityType = getEntityType(key.getKind()); EntityType<?> entityType = getEntityType(key.getKind());
ImmutableSet<EntityId> entityIds = getEntityIdsFromSqlKey(entityType, key.getSqlKey()); ImmutableSet<EntityId> entityIds = getEntityIdsFromSqlKey(entityType, key.getSqlKey());
String sql = String sql =
@ -498,9 +477,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
@Override @Override
public <T> T delete(T entity) { public <T> T delete(T entity) {
checkArgumentNotNull(entity, "entity must be specified"); checkArgumentNotNull(entity, "entity must be specified");
if (isEntityOfIgnoredClass(entity)) {
return entity;
}
assertInTransaction(); assertInTransaction();
T managedEntity = entity; T managedEntity = entity;
if (!getEntityManager().contains(entity)) { if (!getEntityManager().contains(entity)) {
@ -549,10 +525,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
} }
} }
private static boolean isEntityOfIgnoredClass(Object entity) {
return IGNORED_ENTITY_CLASSES.contains(entity.getClass());
}
private static ImmutableSet<EntityId> getEntityIdsFromEntity( private static ImmutableSet<EntityId> getEntityIdsFromEntity(
EntityType<?> entityType, Object entity) { EntityType<?> entityType, Object entity) {
if (entityType.hasSingleIdAttribute()) { if (entityType.hasSingleIdAttribute()) {
@ -615,12 +587,12 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
} }
/** Gets the field definition from clazz or any superclass. */ /** Gets the field definition from clazz or any superclass. */
private static Field getField(Class clazz, String fieldName) throws NoSuchFieldException { private static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
try { try {
// Note that we have to use getDeclaredField() for this, getField() just finds public fields. // Note that we have to use getDeclaredField() for this, getField() just finds public fields.
return clazz.getDeclaredField(fieldName); return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
Class base = clazz.getSuperclass(); Class<?> base = clazz.getSuperclass();
if (base != null) { if (base != null) {
return getField(base, fieldName); return getField(base, fieldName);
} else { } else {

View file

@ -48,7 +48,6 @@ import google.registry.model.common.Cursor.CursorType;
import google.registry.model.contact.Contact; import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.host.Host; import google.registry.model.host.Host;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.rde.RdeMode; import google.registry.model.rde.RdeMode;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.persistence.PersistenceModule.JpaTransactionManagerType; import google.registry.persistence.PersistenceModule.JpaTransactionManagerType;
@ -169,13 +168,6 @@ import org.joda.time.Duration;
* the initial query for the history entry running at {@code READ_COMMITTED} transaction isolation * the initial query for the history entry running at {@code READ_COMMITTED} transaction isolation
* level. * level.
* *
* <p>This is also true in Datastore because:
*
* <ol>
* <li>{@code EppResource} queries are strongly consistent thanks to {@link EppResourceIndex}
* <li>{@code EppResource} entities are rewinded to the point-in-time of the watermark
* </ol>
*
* <p>Here's what's not deterministic: * <p>Here's what's not deterministic:
* *
* <ul> * <ul>

View file

@ -18,7 +18,6 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat; import static com.google.common.truth.Truth8.assertThat;
import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadByEntitiesIfPresent; import static google.registry.testing.DatabaseHelper.loadByEntitiesIfPresent;
import static google.registry.testing.DatabaseHelper.loadByEntity; import static google.registry.testing.DatabaseHelper.loadByEntity;
@ -30,12 +29,10 @@ import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DatabaseHelper.persistSimpleResource; import static google.registry.testing.DatabaseHelper.persistSimpleResource;
import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued; import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryEnvironment; import google.registry.config.RegistryEnvironment;
import google.registry.dns.DnsQueue; import google.registry.dns.DnsQueue;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
@ -43,8 +40,6 @@ import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
@ -279,8 +274,7 @@ class DeleteProberDataActionTest {
} }
/** /**
* Persists and returns a domain and a descendant history entry, billing event, and poll message, * Persists and returns a domain and a descendant history entry, billing event, and poll message.
* along with the ForeignKeyIndex and EppResourceIndex.
*/ */
private static Set<ImmutableObject> persistDomainAndDescendants(String fqdn) { private static Set<ImmutableObject> persistDomainAndDescendants(String fqdn) {
Domain domain = persistDeletedDomain(fqdn, DELETION_TIME); Domain domain = persistDeletedDomain(fqdn, DELETION_TIME);
@ -318,11 +312,6 @@ class DeleteProberDataActionTest {
.add(historyEntry) .add(historyEntry)
.add(billingEvent) .add(billingEvent)
.add(pollMessage); .add(pollMessage);
if (tm().isOfy()) {
builder
.add(ForeignKeyIndex.load(Domain.class, fqdn, START_OF_TIME))
.add(loadByEntity(EppResourceIndex.create(Key.create(domain))));
}
return builder.build(); return builder.build();
} }

View file

@ -21,8 +21,6 @@ import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.host.Host; import google.registry.model.host.Host;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.EppResourceIndexBucket;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.TestObject; import google.registry.testing.TestObject;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -43,11 +41,8 @@ public class ClassPathManagerTest {
assertThat(ClassPathManager.getClass("Host")).isEqualTo(Host.class); assertThat(ClassPathManager.getClass("Host")).isEqualTo(Host.class);
assertThat(ClassPathManager.getClass("Contact")).isEqualTo(Contact.class); assertThat(ClassPathManager.getClass("Contact")).isEqualTo(Contact.class);
assertThat(ClassPathManager.getClass("GaeUserIdConverter")).isEqualTo(GaeUserIdConverter.class); assertThat(ClassPathManager.getClass("GaeUserIdConverter")).isEqualTo(GaeUserIdConverter.class);
assertThat(ClassPathManager.getClass("EppResourceIndexBucket"))
.isEqualTo(EppResourceIndexBucket.class);
assertThat(ClassPathManager.getClass("Domain")).isEqualTo(Domain.class); assertThat(ClassPathManager.getClass("Domain")).isEqualTo(Domain.class);
assertThat(ClassPathManager.getClass("HistoryEntry")).isEqualTo(HistoryEntry.class); assertThat(ClassPathManager.getClass("HistoryEntry")).isEqualTo(HistoryEntry.class);
assertThat(ClassPathManager.getClass("EppResourceIndex")).isEqualTo(EppResourceIndex.class);
} }
@Test @Test
@ -84,11 +79,8 @@ public class ClassPathManagerTest {
assertThat(ClassPathManager.getClassName(Contact.class)).isEqualTo("Contact"); assertThat(ClassPathManager.getClassName(Contact.class)).isEqualTo("Contact");
assertThat(ClassPathManager.getClassName(GaeUserIdConverter.class)) assertThat(ClassPathManager.getClassName(GaeUserIdConverter.class))
.isEqualTo("GaeUserIdConverter"); .isEqualTo("GaeUserIdConverter");
assertThat(ClassPathManager.getClassName(EppResourceIndexBucket.class))
.isEqualTo("EppResourceIndexBucket");
assertThat(ClassPathManager.getClassName(Domain.class)).isEqualTo("Domain"); assertThat(ClassPathManager.getClassName(Domain.class)).isEqualTo("Domain");
assertThat(ClassPathManager.getClassName(HistoryEntry.class)).isEqualTo("HistoryEntry"); assertThat(ClassPathManager.getClassName(HistoryEntry.class)).isEqualTo("HistoryEntry");
assertThat(ClassPathManager.getClassName(EppResourceIndex.class)).isEqualTo("EppResourceIndex");
} }
@Test @Test

View file

@ -51,7 +51,6 @@ import static org.joda.money.CurrencyUnit.USD;
import com.google.common.base.Ascii; import com.google.common.base.Ascii;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@ -59,7 +58,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.net.InetAddresses; import com.google.common.net.InetAddresses;
import com.googlecode.objectify.Key;
import google.registry.dns.writer.VoidDnsWriter; import google.registry.dns.writer.VoidDnsWriter;
import google.registry.model.Buildable; import google.registry.model.Buildable;
import google.registry.model.EppResource; import google.registry.model.EppResource;
@ -88,8 +86,6 @@ import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.host.Host; import google.registry.model.host.Host;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.EppResourceIndexBucket;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.pricing.StaticPremiumListPricingEngine; import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
@ -119,6 +115,7 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.money.CurrencyUnit; import org.joda.money.CurrencyUnit;
import org.joda.money.Money; import org.joda.money.Money;
@ -127,7 +124,7 @@ import org.joda.time.DateTimeZone;
import org.joda.time.Duration; import org.joda.time.Duration;
/** Static utils for setting up test resources. */ /** Static utils for setting up test resources. */
public class DatabaseHelper { public final class DatabaseHelper {
// If the clock is defined, it will always be advanced by one millsecond after a transaction. // If the clock is defined, it will always be advanced by one millsecond after a transaction.
private static FakeClock clock; private static FakeClock clock;
@ -321,10 +318,8 @@ public class DatabaseHelper {
/** Persists a domain and enqueues a LORDN task of the appropriate type for it. */ /** Persists a domain and enqueues a LORDN task of the appropriate type for it. */
public static Domain persistDomainAndEnqueueLordn(final Domain domain) { public static Domain persistDomainAndEnqueueLordn(final Domain domain) {
final Domain persistedDomain = persistResource(domain); final Domain persistedDomain = persistResource(domain);
/** // Calls {@link LordnTaskUtils#enqueueDomainTask} wrapped in a transaction so that the
* Calls {@link LordnTaskUtils#enqueueDomainTask} wrapped in a transaction so that the // transaction time is set correctly.
* transaction time is set correctly.
*/
tm().transactNew(() -> LordnTaskUtils.enqueueDomainTask(persistedDomain)); tm().transactNew(() -> LordnTaskUtils.enqueueDomainTask(persistedDomain));
maybeAdvanceClock(); maybeAdvanceClock();
return persistedDomain; return persistedDomain;
@ -995,8 +990,7 @@ public class DatabaseHelper {
saver.accept(resource); saver.accept(resource);
if (resource instanceof EppResource) { if (resource instanceof EppResource) {
EppResource eppResource = (EppResource) resource; EppResource eppResource = (EppResource) resource;
persistEppResourceExtras( persistEppResourceExtras(eppResource, saver);
eppResource, EppResourceIndex.create(Key.create(eppResource)), saver);
} }
} else { } else {
tm().put(resource); tm().put(resource);
@ -1004,11 +998,10 @@ public class DatabaseHelper {
} }
private static <R extends EppResource> void persistEppResourceExtras( private static <R extends EppResource> void persistEppResourceExtras(
R resource, EppResourceIndex index, Consumer<ImmutableObject> saver) { R resource, Consumer<ImmutableObject> saver) {
assertWithMessage("Cannot persist an EppResource with a missing repoId in tests") assertWithMessage("Cannot persist an EppResource with a missing repoId in tests")
.that(resource.getRepoId()) .that(resource.getRepoId())
.isNotEmpty(); .isNotEmpty();
saver.accept(index);
} }
/** Persists an object in the DB for tests. */ /** Persists an object in the DB for tests. */
@ -1026,25 +1019,6 @@ public class DatabaseHelper {
return tm().transact(() -> tm().loadByEntity(resource)); return tm().transact(() -> tm().loadByEntity(resource));
} }
/** Persists an EPP resource with the {@link EppResourceIndex} always going into bucket one. */
public static <R extends EppResource> R persistEppResourceInFirstBucket(final R resource) {
final EppResourceIndex eppResourceIndex =
EppResourceIndex.create(Key.create(EppResourceIndexBucket.class, 1), Key.create(resource));
tm().transact(
() -> {
if (tm().isOfy()) {
Consumer<ImmutableObject> saver = tm()::put;
saver.accept(resource);
persistEppResourceExtras(resource, eppResourceIndex, saver);
} else {
tm().put(resource);
}
});
maybeAdvanceClock();
tm().clearSessionCache();
return tm().transact(() -> tm().loadByEntity(resource));
}
/** Persists the specified resources to the DB. */ /** Persists the specified resources to the DB. */
public static <R extends ImmutableObject> void persistResources(final Iterable<R> resources) { public static <R extends ImmutableObject> void persistResources(final Iterable<R> resources) {
for (R resource : resources) { for (R resource : resources) {
@ -1070,7 +1044,7 @@ public class DatabaseHelper {
* <p><b>Warning:</b> If you call this multiple times in a single test, you need to inject Ofy's * <p><b>Warning:</b> If you call this multiple times in a single test, you need to inject Ofy's
* clock field and forward it by a millisecond between each subsequent call. * clock field and forward it by a millisecond between each subsequent call.
* *
* @see #persistResource(Object) * @see #persistResource(ImmutableObject)
*/ */
public static <R extends EppResource> R persistEppResource(final R resource) { public static <R extends EppResource> R persistEppResource(final R resource) {
checkState(!tm().inTransaction()); checkState(!tm().inTransaction());
@ -1095,7 +1069,7 @@ public class DatabaseHelper {
} }
/** /**
* Returns all of the history entries that are parented off the given EppResource, casted to the * Returns all of the history entries that are parented off the given EppResource, cast to the
* corresponding subclass. * corresponding subclass.
*/ */
public static <T extends HistoryEntry> List<T> getHistoryEntries( public static <T extends HistoryEntry> List<T> getHistoryEntries(
@ -1116,7 +1090,7 @@ public class DatabaseHelper {
/** /**
* Returns all of the history entries that are parented off the given EppResource with the given * Returns all of the history entries that are parented off the given EppResource with the given
* type and casted to the corresponding subclass. * type and cast to the corresponding subclass.
*/ */
public static <T extends HistoryEntry> ImmutableList<T> getHistoryEntriesOfType( public static <T extends HistoryEntry> ImmutableList<T> getHistoryEntriesOfType(
EppResource resource, final HistoryEntry.Type type, Class<T> subclazz) { EppResource resource, final HistoryEntry.Type type, Class<T> subclazz) {
@ -1135,8 +1109,8 @@ public class DatabaseHelper {
} }
/** /**
* Returns the only history entry of the given type, casted to the corresponding subtype, and * Returns the only history entry of the given type, cast to the corresponding subtype, and throws
* throws an AssertionError if there are zero or more than one. * an AssertionError if there are zero or more than one.
*/ */
public static <T extends HistoryEntry> T getOnlyHistoryEntryOfType( public static <T extends HistoryEntry> T getOnlyHistoryEntryOfType(
EppResource resource, final HistoryEntry.Type type, Class<T> subclazz) { EppResource resource, final HistoryEntry.Type type, Class<T> subclazz) {

View file

@ -138,15 +138,6 @@ class google.registry.model.host.HostHistory {
java.lang.String reason; java.lang.String reason;
org.joda.time.DateTime modificationTime; org.joda.time.DateTime modificationTime;
} }
class google.registry.model.index.EppResourceIndex {
@Id java.lang.String id;
@Parent com.googlecode.objectify.Key<google.registry.model.index.EppResourceIndexBucket> bucket;
com.googlecode.objectify.Key<? extends google.registry.model.EppResource> reference;
java.lang.String kind;
}
class google.registry.model.index.EppResourceIndexBucket {
@Id long bucketId;
}
class google.registry.model.reporting.HistoryEntry { class google.registry.model.reporting.HistoryEntry {
@Id java.lang.Long id; @Id java.lang.Long id;
@Parent com.googlecode.objectify.Key<? extends google.registry.model.EppResource> parent; @Parent com.googlecode.objectify.Key<? extends google.registry.model.EppResource> parent;