mirror of
https://github.com/google/nomulus.git
synced 2025-07-01 16:53:35 +02:00
Use TransactionManager for hosts and contacts (#603)
* Use TransactionManager for hosts and contacts Replace Ofy calls with TransactionManager for most interactions involving hosts and contacts. In the course of this, also convert ForeignKeyIndex and the EppResourceCache. * Minor formatting fix
This commit is contained in:
parent
295cc4059a
commit
e0d0dee953
17 changed files with 127 additions and 144 deletions
|
@ -94,10 +94,10 @@ public final class ResourceFlowUtils {
|
|||
* trust the query and need to do the full mapreduce.
|
||||
*/
|
||||
Iterable<Key<DomainBase>> keys =
|
||||
queryForLinkedDomains(fki.getResourceKey(), now)
|
||||
queryForLinkedDomains(fki.getResourceKey().getOfyKey(), now)
|
||||
.limit(FAILFAST_CHECK_COUNT)
|
||||
.keys();
|
||||
VKey<R> resourceVKey = VKey.createOfy(resourceClass, fki.getResourceKey());
|
||||
VKey<R> resourceVKey = fki.getResourceKey();
|
||||
Predicate<DomainBase> predicate =
|
||||
domain -> getPotentialReferences.apply(domain).contains(resourceVKey);
|
||||
return ofy().load().keys(keys).values().stream().anyMatch(predicate)
|
||||
|
@ -136,9 +136,9 @@ public final class ResourceFlowUtils {
|
|||
|
||||
public static <R extends EppResource> void verifyResourceDoesNotExist(
|
||||
Class<R> clazz, String targetId, DateTime now, String clientId) throws EppException {
|
||||
Key<R> key = loadAndGetKey(clazz, targetId, now);
|
||||
VKey<R> key = loadAndGetKey(clazz, targetId, now);
|
||||
if (key != null) {
|
||||
R resource = ofy().load().key(key).now();
|
||||
R resource = tm().load(key);
|
||||
// These are similar exceptions, but we can track them internally as log-based metrics.
|
||||
if (Objects.equals(clientId, resource.getPersistedCurrentSponsorClientId())) {
|
||||
throw new ResourceAlreadyExistsForThisClientException(targetId);
|
||||
|
|
|
@ -77,7 +77,6 @@ import google.registry.model.billing.BillingEvent;
|
|||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
import google.registry.model.domain.DomainCommand.Create;
|
||||
|
@ -353,14 +352,12 @@ public class DomainCreateFlow implements TransactionalFlow {
|
|||
.setLaunchNotice(hasClaimsNotice ? launchCreate.get().getNotice() : null)
|
||||
.setSmdId(signedMarkId)
|
||||
.setDsData(secDnsCreate.isPresent() ? secDnsCreate.get().getDsData() : null)
|
||||
.setRegistrant(VKey.createOfy(ContactResource.class, command.getRegistrant()))
|
||||
.setRegistrant(command.getRegistrant())
|
||||
.setAuthInfo(command.getAuthInfo())
|
||||
.setFullyQualifiedDomainName(targetId)
|
||||
.setNameservers(
|
||||
(ImmutableSet<VKey<HostResource>>)
|
||||
command.getNameservers().stream()
|
||||
.map(key -> VKey.createOfy(HostResource.class, key))
|
||||
.collect(toImmutableSet()))
|
||||
command.getNameservers().stream().collect(toImmutableSet()))
|
||||
.setStatusValues(statuses.build())
|
||||
.setContacts(command.getContacts())
|
||||
.addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, createBillingEvent))
|
||||
|
|
|
@ -307,14 +307,11 @@ public class DomainFlowUtils {
|
|||
/** Verify that no linked resources have disallowed statuses. */
|
||||
static void verifyNotInPendingDelete(
|
||||
Set<DesignatedContact> contacts,
|
||||
Key<ContactResource> registrant,
|
||||
Set<Key<HostResource>> nameservers)
|
||||
VKey<ContactResource> registrant,
|
||||
Set<VKey<HostResource>> nameservers)
|
||||
throws EppException {
|
||||
ImmutableList.Builder<Key<? extends EppResource>> keysToLoad = new ImmutableList.Builder<>();
|
||||
contacts.stream()
|
||||
.map(DesignatedContact::getContactKey)
|
||||
.map(VKey::getOfyKey)
|
||||
.forEach(keysToLoad::add);
|
||||
ImmutableList.Builder<VKey<? extends EppResource>> keysToLoad = new ImmutableList.Builder<>();
|
||||
contacts.stream().map(DesignatedContact::getContactKey).forEach(keysToLoad::add);
|
||||
Optional.ofNullable(registrant).ifPresent(keysToLoad::add);
|
||||
keysToLoad.addAll(nameservers);
|
||||
verifyNotInPendingDelete(EppResource.loadCached(keysToLoad.build()).values());
|
||||
|
@ -381,7 +378,7 @@ public class DomainFlowUtils {
|
|||
}
|
||||
|
||||
static void validateRequiredContactsPresent(
|
||||
@Nullable Key<ContactResource> registrant, Set<DesignatedContact> contacts)
|
||||
@Nullable VKey<ContactResource> registrant, Set<DesignatedContact> contacts)
|
||||
throws RequiredParameterMissingException {
|
||||
if (registrant == null) {
|
||||
throw new MissingRegistrantException();
|
||||
|
|
|
@ -60,7 +60,6 @@ import google.registry.flows.domain.DomainFlowUtils.MissingRegistrantException;
|
|||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainCommand.Update;
|
||||
import google.registry.model.domain.DomainCommand.Update.AddRemove;
|
||||
|
@ -73,11 +72,9 @@ import google.registry.model.eppcommon.StatusValue;
|
|||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -247,22 +244,11 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
|||
.setLastEppUpdateClientId(clientId)
|
||||
.addStatusValues(add.getStatusValues())
|
||||
.removeStatusValues(remove.getStatusValues())
|
||||
.addNameservers(
|
||||
add.getNameservers().stream()
|
||||
.map(key -> VKey.createOfy(HostResource.class, key))
|
||||
.collect(toImmutableSet()))
|
||||
.removeNameservers(
|
||||
remove.getNameservers().stream()
|
||||
.map(key -> VKey.createOfy(HostResource.class, key))
|
||||
.collect(toImmutableSet()))
|
||||
.addNameservers(add.getNameservers().stream().collect(toImmutableSet()))
|
||||
.removeNameservers(remove.getNameservers().stream().collect(toImmutableSet()))
|
||||
.addContacts(add.getContacts())
|
||||
.removeContacts(remove.getContacts())
|
||||
.setRegistrant(
|
||||
firstNonNull(
|
||||
change.getRegistrant() != null
|
||||
? VKey.createOfy(ContactResource.class, change.getRegistrant())
|
||||
: null,
|
||||
domain.getRegistrant()))
|
||||
.setRegistrant(firstNonNull(change.getRegistrant(), domain.getRegistrant()))
|
||||
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
|
||||
return domainBuilder.build();
|
||||
}
|
||||
|
@ -275,7 +261,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
|||
|
||||
private void validateNewState(DomainBase newDomain) throws EppException {
|
||||
validateNoDuplicateContacts(newDomain.getContacts());
|
||||
validateRequiredContactsPresent(newDomain.getRegistrant().getOfyKey(), newDomain.getContacts());
|
||||
validateRequiredContactsPresent(newDomain.getRegistrant(), newDomain.getContacts());
|
||||
validateDsData(newDomain.getDsData());
|
||||
validateNameserversCountForTld(
|
||||
newDomain.getTld(),
|
||||
|
|
|
@ -16,12 +16,11 @@ package google.registry.model;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.config.RegistryConfig.getEppResourceCachingDuration;
|
||||
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
|
@ -32,11 +31,9 @@ import com.google.common.annotations.VisibleForTesting;
|
|||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import com.googlecode.objectify.annotation.Index;
|
||||
|
@ -47,10 +44,10 @@ import google.registry.model.transfer.TransferData;
|
|||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.StreamSupport;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.Transient;
|
||||
|
@ -333,18 +330,18 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
|||
}
|
||||
}
|
||||
|
||||
static final CacheLoader<Key<? extends EppResource>, EppResource> CACHE_LOADER =
|
||||
new CacheLoader<Key<? extends EppResource>, EppResource>() {
|
||||
static final CacheLoader<VKey<? extends EppResource>, EppResource> CACHE_LOADER =
|
||||
new CacheLoader<VKey<? extends EppResource>, EppResource>() {
|
||||
|
||||
@Override
|
||||
public EppResource load(Key<? extends EppResource> key) {
|
||||
return tm().doTransactionless(() -> ofy().load().key(key).now());
|
||||
public EppResource load(VKey<? extends EppResource> key) {
|
||||
return tm().doTransactionless(() -> tm().load(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Key<? extends EppResource>, EppResource> loadAll(
|
||||
Iterable<? extends Key<? extends EppResource>> keys) {
|
||||
return tm().doTransactionless(() -> loadMultiple(keys));
|
||||
public Map<VKey<? extends EppResource>, EppResource> loadAll(
|
||||
Iterable<? extends VKey<? extends EppResource>> keys) {
|
||||
return tm().doTransactionless(() -> loadAsMap(keys));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -356,10 +353,10 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
|||
* directly should of course never use the cache.
|
||||
*/
|
||||
@NonFinalForTesting
|
||||
private static LoadingCache<Key<? extends EppResource>, EppResource> cacheEppResources =
|
||||
private static LoadingCache<VKey<? extends EppResource>, EppResource> cacheEppResources =
|
||||
createEppResourcesCache(getEppResourceCachingDuration());
|
||||
|
||||
private static LoadingCache<Key<? extends EppResource>, EppResource> createEppResourcesCache(
|
||||
private static LoadingCache<VKey<? extends EppResource>, EppResource> createEppResourcesCache(
|
||||
Duration expiry) {
|
||||
return CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(expiry.getMillis(), MILLISECONDS)
|
||||
|
@ -373,29 +370,16 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
|||
cacheEppResources = createEppResourcesCache(effectiveExpiry);
|
||||
}
|
||||
|
||||
private static ImmutableMap<Key<? extends EppResource>, EppResource> loadMultiple(
|
||||
Iterable<? extends Key<? extends EppResource>> keys) {
|
||||
// This cast is safe because, in Objectify, Key<? extends EppResource> can also be
|
||||
// treated as a Key<EppResource>.
|
||||
@SuppressWarnings("unchecked")
|
||||
ImmutableList<Key<EppResource>> typedKeys =
|
||||
Streams.stream(keys).map(key -> (Key<EppResource>) key).collect(toImmutableList());
|
||||
|
||||
// Typing shenanigans are required to return the map with the correct required generic type.
|
||||
return ofy().load().keys(typedKeys).entrySet().stream()
|
||||
.collect(ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given EppResources by their keys using the cache (if enabled).
|
||||
*
|
||||
* <p>Don't use this unless you really need it for performance reasons, and be sure that you are
|
||||
* OK with the trade-offs in loss of transactional consistency.
|
||||
*/
|
||||
public static ImmutableMap<Key<? extends EppResource>, EppResource> loadCached(
|
||||
Iterable<Key<? extends EppResource>> keys) {
|
||||
public static ImmutableMap<VKey<? extends EppResource>, EppResource> loadCached(
|
||||
Iterable<VKey<? extends EppResource>> keys) {
|
||||
if (!RegistryConfig.isEppResourceCachingEnabled()) {
|
||||
return loadMultiple(keys);
|
||||
return loadAsMap(keys);
|
||||
}
|
||||
try {
|
||||
return cacheEppResources.getAll(keys);
|
||||
|
@ -410,9 +394,9 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
|||
* <p>Don't use this unless you really need it for performance reasons, and be sure that you are
|
||||
* OK with the trade-offs in loss of transactional consistency.
|
||||
*/
|
||||
public static <T extends EppResource> T loadCached(Key<T> key) {
|
||||
public static <T extends EppResource> T loadCached(VKey<T> key) {
|
||||
if (!RegistryConfig.isEppResourceCachingEnabled()) {
|
||||
return ofy().load().key(key).now();
|
||||
return tm().load(key);
|
||||
}
|
||||
try {
|
||||
// Safe to cast because loading a Key<T> returns an entity of type T.
|
||||
|
@ -423,4 +407,17 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
|||
throw new RuntimeException("Error loading cached EppResources", e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
private static ImmutableMap<VKey<? extends EppResource>, EppResource> loadAsMap(
|
||||
Iterable<? extends VKey<? extends EppResource>> keys) {
|
||||
return StreamSupport.stream(keys.spliterator(), false)
|
||||
// It's possible for us to receive the same key more than once which causes
|
||||
// the immutable map build to break with a duplicate key, so we have to ensure key
|
||||
// uniqueness.
|
||||
.distinct()
|
||||
// We have to use "key -> key" here instead of the identity() function, because
|
||||
// the latter breaks the fairly complicated generic type checking required by the
|
||||
// caching interface.
|
||||
.collect(toImmutableMap(key -> key, key -> tm().load(key)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package google.registry.model;
|
|||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static google.registry.util.DateTimeUtils.latestOf;
|
||||
|
@ -141,7 +142,7 @@ public final class EppResourceUtils {
|
|||
T resource =
|
||||
useCache
|
||||
? EppResource.loadCached(fki.getResourceKey())
|
||||
: ofy().load().key(fki.getResourceKey()).now();
|
||||
: tm().maybeLoad(fki.getResourceKey()).orElse(null);
|
||||
if (resource == null || isAtOrAfter(now, resource.getDeletionTime())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import com.google.common.base.MoreObjects;
|
|||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
|
@ -82,8 +81,7 @@ public class DomainCommand {
|
|||
String registrantContactId;
|
||||
|
||||
/** A resolved key to the registrant who registered this domain. */
|
||||
@XmlTransient
|
||||
Key<ContactResource> registrant;
|
||||
@XmlTransient VKey<ContactResource> registrant;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the domain. */
|
||||
DomainAuthInfo authInfo;
|
||||
|
@ -93,7 +91,7 @@ public class DomainCommand {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
public Key<ContactResource> getRegistrant() {
|
||||
public VKey<ContactResource> getRegistrant() {
|
||||
return registrant;
|
||||
}
|
||||
|
||||
|
@ -129,8 +127,7 @@ public class DomainCommand {
|
|||
Set<String> nameserverFullyQualifiedHostNames;
|
||||
|
||||
/** Resolved keys to hosts that are the nameservers for the domain. */
|
||||
@XmlTransient
|
||||
Set<Key<HostResource>> nameservers;
|
||||
@XmlTransient Set<VKey<HostResource>> nameservers;
|
||||
|
||||
/** Foreign keyed associated contacts for the domain (other than registrant). */
|
||||
@XmlElement(name = "contact")
|
||||
|
@ -160,7 +157,7 @@ public class DomainCommand {
|
|||
return nullToEmptyImmutableCopy(nameserverFullyQualifiedHostNames);
|
||||
}
|
||||
|
||||
public ImmutableSet<Key<HostResource>> getNameservers() {
|
||||
public ImmutableSet<VKey<HostResource>> getNameservers() {
|
||||
return nullToEmptyImmutableCopy(nameservers);
|
||||
}
|
||||
|
||||
|
@ -190,7 +187,7 @@ public class DomainCommand {
|
|||
now);
|
||||
for (DesignatedContact contact : contacts) {
|
||||
if (DesignatedContact.Type.REGISTRANT.equals(contact.getType())) {
|
||||
clone.registrant = contact.getContactKey().getOfyKey();
|
||||
clone.registrant = contact.getContactKey();
|
||||
clone.contacts = forceEmptyToNull(difference(contacts, contact));
|
||||
break;
|
||||
}
|
||||
|
@ -354,8 +351,7 @@ public class DomainCommand {
|
|||
Set<String> nameserverFullyQualifiedHostNames;
|
||||
|
||||
/** Resolved keys to hosts that are the nameservers for the domain. */
|
||||
@XmlTransient
|
||||
Set<Key<HostResource>> nameservers;
|
||||
@XmlTransient Set<VKey<HostResource>> nameservers;
|
||||
|
||||
/** Foreign keyed associated contacts for the domain (other than registrant). */
|
||||
@XmlElement(name = "contact")
|
||||
|
@ -369,7 +365,7 @@ public class DomainCommand {
|
|||
return nullSafeImmutableCopy(nameserverFullyQualifiedHostNames);
|
||||
}
|
||||
|
||||
public ImmutableSet<Key<HostResource>> getNameservers() {
|
||||
public ImmutableSet<VKey<HostResource>> getNameservers() {
|
||||
return nullToEmptyImmutableCopy(nameservers);
|
||||
}
|
||||
|
||||
|
@ -419,7 +415,7 @@ public class DomainCommand {
|
|||
}
|
||||
}
|
||||
|
||||
private static Set<Key<HostResource>> linkHosts(
|
||||
private static Set<VKey<HostResource>> linkHosts(
|
||||
Set<String> fullyQualifiedHostNames, DateTime now) throws InvalidReferencesException {
|
||||
if (fullyQualifiedHostNames == null) {
|
||||
return null;
|
||||
|
@ -437,20 +433,18 @@ public class DomainCommand {
|
|||
for (ForeignKeyedDesignatedContact contact : contacts) {
|
||||
foreignKeys.add(contact.contactId);
|
||||
}
|
||||
ImmutableMap<String, Key<ContactResource>> loadedContacts =
|
||||
ImmutableMap<String, VKey<ContactResource>> loadedContacts =
|
||||
loadByForeignKeysCached(foreignKeys.build(), ContactResource.class, now);
|
||||
ImmutableSet.Builder<DesignatedContact> linkedContacts = new ImmutableSet.Builder<>();
|
||||
for (ForeignKeyedDesignatedContact contact : contacts) {
|
||||
linkedContacts.add(
|
||||
DesignatedContact.create(
|
||||
contact.type,
|
||||
VKey.createOfy(ContactResource.class, loadedContacts.get(contact.contactId))));
|
||||
DesignatedContact.create(contact.type, loadedContacts.get(contact.contactId)));
|
||||
}
|
||||
return linkedContacts.build();
|
||||
}
|
||||
|
||||
/** Loads keys to cached EPP resources by their foreign keys. */
|
||||
private static <T extends EppResource> ImmutableMap<String, Key<T>> loadByForeignKeysCached(
|
||||
private static <T extends EppResource> ImmutableMap<String, VKey<T>> loadByForeignKeysCached(
|
||||
final Set<String> foreignKeys, final Class<T> clazz, final DateTime now)
|
||||
throws InvalidReferencesException {
|
||||
Map<String, ForeignKeyIndex<T>> fkis = ForeignKeyIndex.loadCached(clazz, foreignKeys, now);
|
||||
|
|
|
@ -43,6 +43,7 @@ import google.registry.model.annotations.ReportedOn;
|
|||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
@ -99,7 +100,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
|
|||
* <p>This field holds a key to the only referenced resource. It is named "topReference" for
|
||||
* historical reasons.
|
||||
*/
|
||||
Key<E> topReference;
|
||||
VKey<E> topReference;
|
||||
|
||||
public String getForeignKey() {
|
||||
return foreignKey;
|
||||
|
@ -109,7 +110,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
|
|||
return deletionTime;
|
||||
}
|
||||
|
||||
public Key<E> getResourceKey() {
|
||||
public VKey<E> getResourceKey() {
|
||||
return topReference;
|
||||
}
|
||||
|
||||
|
@ -125,7 +126,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
|
|||
@SuppressWarnings("unchecked")
|
||||
Class<E> resourceClass = (Class<E>) resource.getClass();
|
||||
ForeignKeyIndex<E> instance = instantiate(mapToFkiClass(resourceClass));
|
||||
instance.topReference = Key.create(resource);
|
||||
instance.topReference = (VKey<E>) resource.createVKey();
|
||||
instance.foreignKey = resource.getForeignKey();
|
||||
instance.deletionTime = deletionTime;
|
||||
return instance;
|
||||
|
@ -141,18 +142,18 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
|
|||
/**
|
||||
* Loads a {@link Key} to an {@link EppResource} from Datastore by foreign key.
|
||||
*
|
||||
* <p>Returns null if no foreign key index with this foreign key was ever created, or if the
|
||||
* most recently created foreign key index was deleted before time "now". This method does not
|
||||
* actually check that the referenced resource actually exists. However, for normal epp resources,
|
||||
* it is safe to assume that the referenced resource exists if the foreign key index does.
|
||||
* <p>Returns null if no foreign key index with this foreign key was ever created, or if the most
|
||||
* recently created foreign key index was deleted before time "now". This method does not actually
|
||||
* check that the referenced resource actually exists. However, for normal epp resources, it is
|
||||
* safe to assume that the referenced resource exists if the foreign key index does.
|
||||
*
|
||||
* @param clazz the resource type to load
|
||||
* @param foreignKey id to match
|
||||
* @param now the current logical time to use when checking for soft deletion of the foreign key
|
||||
* index
|
||||
* index
|
||||
*/
|
||||
@Nullable
|
||||
public static <E extends EppResource> Key<E> loadAndGetKey(
|
||||
public static <E extends EppResource> VKey<E> loadAndGetKey(
|
||||
Class<E> clazz, String foreignKey, DateTime now) {
|
||||
ForeignKeyIndex<E> index = load(clazz, foreignKey, now);
|
||||
return (index == null) ? null : index.getResourceKey();
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
package google.registry.rdap;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
@ -28,10 +29,10 @@ import com.google.common.collect.Streams;
|
|||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import com.google.common.primitives.Booleans;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.cmd.Query;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
|
||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||
import google.registry.rdap.RdapMetrics.SearchType;
|
||||
|
@ -50,6 +51,7 @@ import java.util.Comparator;
|
|||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
|
@ -243,7 +245,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
|||
*/
|
||||
private DomainSearchResponse searchByNameserverLdhName(
|
||||
final RdapSearchPattern partialStringQuery) {
|
||||
Iterable<Key<HostResource>> hostKeys = getNameserverRefsByLdhName(partialStringQuery);
|
||||
Iterable<VKey<HostResource>> hostKeys = getNameserverRefsByLdhName(partialStringQuery);
|
||||
if (Iterables.isEmpty(hostKeys)) {
|
||||
metricInformationBuilder.setNumHostsRetrieved(0);
|
||||
throw new NotFoundException("No matching nameservers found");
|
||||
|
@ -261,7 +263,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
|||
* initial string is not required (e.g. "*.example.tld" is valid), because we can look up the
|
||||
* domain and just list all of its subordinate hosts.
|
||||
*/
|
||||
private Iterable<Key<HostResource>> getNameserverRefsByLdhName(
|
||||
private Iterable<VKey<HostResource>> getNameserverRefsByLdhName(
|
||||
final RdapSearchPattern partialStringQuery) {
|
||||
// Handle queries without a wildcard.
|
||||
if (!partialStringQuery.getHasWildcard()) {
|
||||
|
@ -292,11 +294,13 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
|||
if (desiredRegistrar.isPresent()) {
|
||||
query = query.filter("currentSponsorClientId", desiredRegistrar.get());
|
||||
}
|
||||
return query.keys();
|
||||
return StreamSupport.stream(query.keys().spliterator(), false)
|
||||
.map(key -> VKey.createOfy(HostResource.class, key))
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
/** Assembles a list of {@link HostResource} keys by name when the pattern has no wildcard. */
|
||||
private Iterable<Key<HostResource>> getNameserverRefsByLdhNameWithoutWildcard(
|
||||
private Iterable<VKey<HostResource>> getNameserverRefsByLdhNameWithoutWildcard(
|
||||
final RdapSearchPattern partialStringQuery) {
|
||||
// If we need to check the sponsoring registrar, we need to load the resource rather than just
|
||||
// the key.
|
||||
|
@ -310,9 +314,9 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
|||
return (!host.isPresent()
|
||||
|| !desiredRegistrar.get().equals(host.get().getPersistedCurrentSponsorClientId()))
|
||||
? ImmutableList.of()
|
||||
: ImmutableList.of(Key.create(host.get()));
|
||||
: ImmutableList.of(host.get().createVKey());
|
||||
} else {
|
||||
Key<HostResource> hostKey =
|
||||
VKey<HostResource> hostKey =
|
||||
loadAndGetKey(
|
||||
HostResource.class,
|
||||
partialStringQuery.getInitialString(),
|
||||
|
@ -322,7 +326,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
|||
}
|
||||
|
||||
/** Assembles a list of {@link HostResource} keys by name using a superordinate domain suffix. */
|
||||
private Iterable<Key<HostResource>> getNameserverRefsByLdhNameWithSuffix(
|
||||
private Iterable<VKey<HostResource>> getNameserverRefsByLdhNameWithSuffix(
|
||||
final RdapSearchPattern partialStringQuery) {
|
||||
// The suffix must be a domain that we manage. That way, we can look up the domain and search
|
||||
// through the subordinate hosts. This is more efficient, and lets us permit wildcard searches
|
||||
|
@ -338,7 +342,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
|||
"A suffix in a lookup by nameserver name "
|
||||
+ "must be a domain defined in the system"));
|
||||
Optional<String> desiredRegistrar = getDesiredRegistrar();
|
||||
ImmutableList.Builder<Key<HostResource>> builder = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<VKey<HostResource>> builder = new ImmutableList.Builder<>();
|
||||
for (String fqhn : ImmutableSortedSet.copyOf(domainBase.getSubordinateHosts())) {
|
||||
// We can't just check that the host name starts with the initial query string, because
|
||||
// then the query ns.exam*.example.com would match against nameserver ns.example.com.
|
||||
|
@ -351,10 +355,10 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
|||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
if (host.isPresent()
|
||||
&& desiredRegistrar.get().equals(host.get().getPersistedCurrentSponsorClientId())) {
|
||||
builder.add(Key.create(host.get()));
|
||||
builder.add(host.get().createVKey());
|
||||
}
|
||||
} else {
|
||||
Key<HostResource> hostKey =
|
||||
VKey<HostResource> hostKey =
|
||||
loadAndGetKey(
|
||||
HostResource.class,
|
||||
fqhn,
|
||||
|
@ -400,7 +404,10 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
|||
if (desiredRegistrar.isPresent()) {
|
||||
query = query.filter("currentSponsorClientId", desiredRegistrar.get());
|
||||
}
|
||||
return searchByNameserverRefs(query.keys());
|
||||
return searchByNameserverRefs(
|
||||
StreamSupport.stream(query.keys().spliterator(), false)
|
||||
.map(key -> VKey.createOfy(HostResource.class, key))
|
||||
.collect(toImmutableSet()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -409,7 +416,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
|||
* <p>This method is called by {@link #searchByNameserverLdhName} and {@link
|
||||
* #searchByNameserverIp} after they assemble the relevant host keys.
|
||||
*/
|
||||
private DomainSearchResponse searchByNameserverRefs(final Iterable<Key<HostResource>> hostKeys) {
|
||||
private DomainSearchResponse searchByNameserverRefs(final Iterable<VKey<HostResource>> hostKeys) {
|
||||
// We must break the query up into chunks, because the in operator is limited to 30 subqueries.
|
||||
// Since it is possible for the same domain to show up more than once in our result list (if
|
||||
// we do a wildcard nameserver search that returns multiple nameservers used by the same
|
||||
|
@ -420,11 +427,13 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
|||
ImmutableSortedSet.orderedBy(
|
||||
Comparator.comparing(DomainBase::getFullyQualifiedDomainName));
|
||||
int numHostKeysSearched = 0;
|
||||
for (List<Key<HostResource>> chunk : Iterables.partition(hostKeys, 30)) {
|
||||
for (List<VKey<HostResource>> chunk : Iterables.partition(hostKeys, 30)) {
|
||||
numHostKeysSearched += chunk.size();
|
||||
Query<DomainBase> query = ofy().load()
|
||||
.type(DomainBase.class)
|
||||
.filter("nsHosts in", chunk);
|
||||
Query<DomainBase> query =
|
||||
ofy()
|
||||
.load()
|
||||
.type(DomainBase.class)
|
||||
.filter("nsHosts in", chunk.stream().map(VKey::getOfyKey).collect(toImmutableSet()));
|
||||
if (!shouldIncludeDeleted()) {
|
||||
query = query.filter("deletionTime >", getRequestTime());
|
||||
// If we are not performing an inequality query, we can filter on the cursor in the query.
|
||||
|
|
|
@ -18,12 +18,12 @@ import static com.google.common.base.Preconditions.checkState;
|
|||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Strings;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.persistence.VKey;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Container class for static utility methods. */
|
||||
|
@ -41,7 +41,7 @@ class CommandUtilities {
|
|||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public Key<? extends EppResource> getKey(String uniqueId, DateTime now) {
|
||||
public VKey<? extends EppResource> getKey(String uniqueId, DateTime now) {
|
||||
return ForeignKeyIndex.loadAndGetKey(clazz, uniqueId, now);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ import static org.joda.time.DateTimeZone.UTC;
|
|||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tools.CommandUtilities.ResourceType;
|
||||
import google.registry.xml.XmlTransformer;
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -57,7 +57,7 @@ final class GetHistoryEntriesCommand implements CommandWithRemoteApi {
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
Key<? extends EppResource> parentKey = null;
|
||||
VKey<? extends EppResource> parentKey = null;
|
||||
if (type != null || uniqueId != null) {
|
||||
checkArgument(
|
||||
type != null && uniqueId != null,
|
||||
|
@ -66,12 +66,12 @@ final class GetHistoryEntriesCommand implements CommandWithRemoteApi {
|
|||
checkArgumentNotNull(parentKey, "Invalid resource ID");
|
||||
}
|
||||
for (HistoryEntry entry :
|
||||
(parentKey == null
|
||||
(parentKey == null
|
||||
? ofy().load().type(HistoryEntry.class)
|
||||
: ofy().load().type(HistoryEntry.class).ancestor(parentKey))
|
||||
.order("modificationTime")
|
||||
.filter("modificationTime >=", after)
|
||||
.filter("modificationTime <=", before)) {
|
||||
: ofy().load().type(HistoryEntry.class).ancestor(parentKey.getOfyKey()))
|
||||
.order("modificationTime")
|
||||
.filter("modificationTime >=", after)
|
||||
.filter("modificationTime <=", before)) {
|
||||
System.out.printf(
|
||||
"Client: %s\nTime: %s\nClient TRID: %s\nServer TRID: %s\n%s\n",
|
||||
entry.getClientId(),
|
||||
|
|
|
@ -14,14 +14,14 @@
|
|||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tools.CommandUtilities.ResourceType;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
|
@ -48,12 +48,15 @@ public final class ResaveEppResourceCommand extends MutatingCommand {
|
|||
|
||||
@Override
|
||||
protected void init() {
|
||||
Key<? extends EppResource> resourceKey = checkArgumentNotNull(
|
||||
type.getKey(uniqueId, DateTime.now(UTC)),
|
||||
"Could not find active resource of type %s: %s", type, uniqueId);
|
||||
VKey<? extends EppResource> resourceKey =
|
||||
checkArgumentNotNull(
|
||||
type.getKey(uniqueId, DateTime.now(UTC)),
|
||||
"Could not find active resource of type %s: %s",
|
||||
type,
|
||||
uniqueId);
|
||||
// Load the resource directly to bypass running cloneProjectedAtTime() automatically, which can
|
||||
// cause stageEntityChange() to fail due to implicit projection changes.
|
||||
EppResource resource = ofy().load().key(resourceKey).now();
|
||||
EppResource resource = tm().load(resourceKey);
|
||||
stageEntityChange(resource, resource);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,7 +156,7 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
|
|||
// If we refer to a contact that doesn't exist, that's a bug. It means referential integrity
|
||||
// has somehow been broken. We skip the rest of this contact, but log it to hopefully bring it
|
||||
// someone's attention.
|
||||
ContactResource contactResource = EppResource.loadCached(contact.get().getOfyKey());
|
||||
ContactResource contactResource = EppResource.loadCached(contact.get());
|
||||
if (contactResource == null) {
|
||||
logger.atSevere().log(
|
||||
"(BUG) Broken reference found from domain %s to contact %s",
|
||||
|
|
|
@ -178,7 +178,7 @@ public class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Hos
|
|||
// The old ForeignKeyIndex is invalidated at the time we did the rename.
|
||||
ForeignKeyIndex<HostResource> oldFkiBeforeRename =
|
||||
ForeignKeyIndex.load(HostResource.class, oldHostName(), clock.nowUtc().minusMillis(1));
|
||||
assertThat(oldFkiBeforeRename.getResourceKey()).isEqualTo(Key.create(renamedHost));
|
||||
assertThat(oldFkiBeforeRename.getResourceKey()).isEqualTo(renamedHost.createVKey());
|
||||
assertThat(oldFkiBeforeRename.getDeletionTime()).isEqualTo(clock.nowUtc());
|
||||
ForeignKeyIndex<HostResource> oldFkiAfterRename =
|
||||
ForeignKeyIndex.load(HostResource.class, oldHostName(), clock.nowUtc());
|
||||
|
|
|
@ -22,7 +22,6 @@ import static google.registry.testing.DatastoreHelper.persistActiveHost;
|
|||
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.testing.TestCacheRule;
|
||||
|
@ -40,12 +39,12 @@ public class EppResourceTest extends EntityTestCase {
|
|||
@Test
|
||||
public void test_loadCached_ignoresContactChange() {
|
||||
ContactResource originalContact = persistActiveContact("contact123");
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(Key.create(originalContact))))
|
||||
.containsExactly(Key.create(originalContact), originalContact);
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(originalContact.createVKey())))
|
||||
.containsExactly(originalContact.createVKey(), originalContact);
|
||||
ContactResource modifiedContact =
|
||||
persistResource(originalContact.asBuilder().setEmailAddress("different@fake.lol").build());
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(Key.create(originalContact))))
|
||||
.containsExactly(Key.create(originalContact), originalContact);
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(originalContact.createVKey())))
|
||||
.containsExactly(originalContact.createVKey(), originalContact);
|
||||
assertThat(loadByForeignKey(ContactResource.class, "contact123", fakeClock.nowUtc()))
|
||||
.hasValue(modifiedContact);
|
||||
}
|
||||
|
@ -53,13 +52,13 @@ public class EppResourceTest extends EntityTestCase {
|
|||
@Test
|
||||
public void test_loadCached_ignoresHostChange() {
|
||||
HostResource originalHost = persistActiveHost("ns1.example.com");
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(Key.create(originalHost))))
|
||||
.containsExactly(Key.create(originalHost), originalHost);
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(originalHost.createVKey())))
|
||||
.containsExactly(originalHost.createVKey(), originalHost);
|
||||
HostResource modifiedHost =
|
||||
persistResource(
|
||||
originalHost.asBuilder().setLastTransferTime(fakeClock.nowUtc().minusDays(60)).build());
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(Key.create(originalHost))))
|
||||
.containsExactly(Key.create(originalHost), originalHost);
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(originalHost.createVKey())))
|
||||
.containsExactly(originalHost.createVKey(), originalHost);
|
||||
assertThat(loadByForeignKey(HostResource.class, "ns1.example.com", fakeClock.nowUtc()))
|
||||
.hasValue(modifiedHost);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ package google.registry.model.index;
|
|||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatastoreHelper.createTld;
|
||||
import static google.registry.testing.DatastoreHelper.deleteResource;
|
||||
import static google.registry.testing.DatastoreHelper.persistActiveContact;
|
||||
|
@ -27,7 +27,6 @@ import static google.registry.testing.DatastoreHelper.persistResource;
|
|||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.host.HostResource;
|
||||
|
@ -56,7 +55,7 @@ public class ForeignKeyIndexTest extends EntityTestCase {
|
|||
HostResource host = persistActiveHost("ns1.example.com");
|
||||
ForeignKeyIndex<HostResource> fki =
|
||||
ForeignKeyIndex.load(HostResource.class, "ns1.example.com", fakeClock.nowUtc());
|
||||
assertThat(ofy().load().key(fki.getResourceKey()).now()).isEqualTo(host);
|
||||
assertThat(tm().load(fki.getResourceKey())).isEqualTo(host);
|
||||
assertThat(fki.getDeletionTime()).isEqualTo(END_OF_TIME);
|
||||
}
|
||||
|
||||
|
@ -89,7 +88,7 @@ public class ForeignKeyIndexTest extends EntityTestCase {
|
|||
fakeClock.advanceOneMilli();
|
||||
ForeignKeyHostIndex fki = new ForeignKeyHostIndex();
|
||||
fki.foreignKey = "ns1.example.com";
|
||||
fki.topReference = Key.create(host1);
|
||||
fki.topReference = host1.createVKey();
|
||||
fki.deletionTime = fakeClock.nowUtc();
|
||||
persistResource(fki);
|
||||
assertThat(ForeignKeyIndex.load(HostResource.class, "ns1.example.com", fakeClock.nowUtc()))
|
||||
|
|
|
@ -314,20 +314,20 @@ class google.registry.model.index.EppResourceIndexBucket {
|
|||
}
|
||||
class google.registry.model.index.ForeignKeyIndex$ForeignKeyContactIndex {
|
||||
@Id java.lang.String foreignKey;
|
||||
com.googlecode.objectify.Key<E> topReference;
|
||||
google.registry.model.UpdateAutoTimestamp updateTimestamp;
|
||||
google.registry.persistence.VKey<E> topReference;
|
||||
org.joda.time.DateTime deletionTime;
|
||||
}
|
||||
class google.registry.model.index.ForeignKeyIndex$ForeignKeyDomainIndex {
|
||||
@Id java.lang.String foreignKey;
|
||||
com.googlecode.objectify.Key<E> topReference;
|
||||
google.registry.model.UpdateAutoTimestamp updateTimestamp;
|
||||
google.registry.persistence.VKey<E> topReference;
|
||||
org.joda.time.DateTime deletionTime;
|
||||
}
|
||||
class google.registry.model.index.ForeignKeyIndex$ForeignKeyHostIndex {
|
||||
@Id java.lang.String foreignKey;
|
||||
com.googlecode.objectify.Key<E> topReference;
|
||||
google.registry.model.UpdateAutoTimestamp updateTimestamp;
|
||||
google.registry.persistence.VKey<E> topReference;
|
||||
org.joda.time.DateTime deletionTime;
|
||||
}
|
||||
class google.registry.model.ofy.CommitLogBucket {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue