mirror of
https://github.com/google/nomulus
synced 2026-07-08 17:16:54 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 149fb66ac5 | |||
| 8c96940a27 |
@@ -18,7 +18,6 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.flows.FlowUtils.persistEntityChanges;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.COLLISION_MESSAGE;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.checkHasBillingAccount;
|
||||
@@ -224,6 +223,7 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
@Inject DomainCreateFlowCustomLogic flowCustomLogic;
|
||||
@Inject DomainFlowTmchUtils tmchUtils;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DomainDeletionTimeCache domainDeletionTimeCache;
|
||||
|
||||
@Inject DomainCreateFlow() {}
|
||||
|
||||
@@ -239,13 +239,13 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
verifyRegistrarIsActive(registrarId);
|
||||
extensionManager.validate();
|
||||
verifyDomainDoesNotExist();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
DomainCommand.Create command = cloneAndLinkReferences((Create) resourceCommand, now);
|
||||
Period period = command.getPeriod();
|
||||
verifyUnitIsYears(period);
|
||||
int years = period.getValue();
|
||||
validateRegistrationPeriod(years);
|
||||
verifyResourceDoesNotExist(Domain.class, targetId, now, registrarId);
|
||||
// Validate that this is actually a legal domain name on a TLD that the registrar has access to.
|
||||
InternetDomainName domainName = validateDomainName(command.getDomainName());
|
||||
String domainLabel = domainName.parts().getFirst();
|
||||
@@ -649,6 +649,15 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
.build();
|
||||
}
|
||||
|
||||
private void verifyDomainDoesNotExist() throws ResourceCreateContentionException {
|
||||
Optional<DateTime> previousDeletionTime =
|
||||
domainDeletionTimeCache.getDeletionTimeForDomain(targetId);
|
||||
if (previousDeletionTime.isPresent()
|
||||
&& !tm().getTransactionTime().isAfter(previousDeletionTime.get())) {
|
||||
throw new ResourceCreateContentionException(targetId);
|
||||
}
|
||||
}
|
||||
|
||||
private static BillingEvent createEapBillingEvent(
|
||||
FeesAndCredits feesAndCredits, BillingEvent createBillingEvent) {
|
||||
return new BillingEvent.Builder()
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright 2025 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.flows.domain;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.Expiry;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Functionally-static loading cache that keeps track of deletion (AKA drop) times for domains.
|
||||
*
|
||||
* <p>Some domain names may have many create requests issued shortly before (and directly after) the
|
||||
* name is released due to a previous registrant deleting it. In those cases, caching the deletion
|
||||
* time of the existing domain allows us to short-circuit the request and avoid any load on the
|
||||
* database checking the existing domain (at least, in cases where the request hits a particular
|
||||
* node more than once).
|
||||
*
|
||||
* <p>The cache is fairly short-lived (as we're concerned about many requests at basically the same
|
||||
* time), and entries also expire when the drop actually happens. If the domain is re-created after
|
||||
* a drop, the next load attempt will populate the cache with a deletion time of END_OF_TIME, which
|
||||
* will be read from the cache by subsequent attempts.
|
||||
*
|
||||
* <p>We take advantage of the fact that Caffeine caches don't store nulls returned from the
|
||||
* CacheLoader, so a null result (meaning the domain doesn't exist) won't affect future calls (this
|
||||
* avoids a stale-cache situation where the cache "thinks" the domain doesn't exist, but it does).
|
||||
* Put another way, if a domain really doesn't exist, we'll re-attempt the database load every time.
|
||||
*
|
||||
* <p>We don't explicitly set the cache inside domain create/delete flows, in case the transaction
|
||||
* fails at commit time. It's better to have stale data, or to require an additional database load,
|
||||
* than to have incorrect data.
|
||||
*
|
||||
* <p>Note: this should be injected as a singleton -- it's essentially static, but we have it as a
|
||||
* non-static object for concurrent testing purposes.
|
||||
*/
|
||||
public class DomainDeletionTimeCache {
|
||||
|
||||
// Max expiry time is ten minutes
|
||||
private static final int MAX_EXPIRY_MILLIS = 10 * 60 * 1000;
|
||||
private static final int MAX_ENTRIES = 500;
|
||||
private static final int NANOS_IN_ONE_MILLISECOND = 100000;
|
||||
|
||||
/**
|
||||
* Expire after the max duration, or after the domain is set to drop (whichever comes first).
|
||||
*
|
||||
* <p>If the domain has already been deleted (the deletion time is <= now), the entry will
|
||||
* immediately be expired/removed.
|
||||
*
|
||||
* <p>NB: the Expiry class requires the return value in <b>nanoseconds</b>, not milliseconds
|
||||
*/
|
||||
private static final Expiry<String, DateTime> EXPIRY_POLICY =
|
||||
new Expiry<>() {
|
||||
@Override
|
||||
public long expireAfterCreate(String key, DateTime value, long currentTime) {
|
||||
long millisUntilDeletion = value.getMillis() - tm().getTransactionTime().getMillis();
|
||||
return NANOS_IN_ONE_MILLISECOND
|
||||
* Math.max(0L, Math.min(MAX_EXPIRY_MILLIS, millisUntilDeletion));
|
||||
}
|
||||
|
||||
/** Reset the time entirely on update, as if we were creating the entry anew. */
|
||||
@Override
|
||||
public long expireAfterUpdate(
|
||||
String key, DateTime value, long currentTime, long currentDuration) {
|
||||
return expireAfterCreate(key, value, currentTime);
|
||||
}
|
||||
|
||||
/** Reads do not change the expiry duration. */
|
||||
@Override
|
||||
public long expireAfterRead(
|
||||
String key, DateTime value, long currentTime, long currentDuration) {
|
||||
return currentDuration;
|
||||
}
|
||||
};
|
||||
|
||||
/** Attempt to load the domain's deletion time if the domain exists. */
|
||||
private static final CacheLoader<String, DateTime> CACHE_LOADER =
|
||||
(domainName) -> {
|
||||
ForeignKeyUtils.MostRecentResource mostRecentResource =
|
||||
ForeignKeyUtils.loadMostRecentResources(
|
||||
Domain.class, ImmutableSet.of(domainName), false)
|
||||
.get(domainName);
|
||||
return mostRecentResource == null ? null : mostRecentResource.deletionTime();
|
||||
};
|
||||
|
||||
public static DomainDeletionTimeCache create() {
|
||||
return new DomainDeletionTimeCache(
|
||||
Caffeine.newBuilder()
|
||||
.expireAfter(EXPIRY_POLICY)
|
||||
.maximumSize(MAX_ENTRIES)
|
||||
.build(CACHE_LOADER));
|
||||
}
|
||||
|
||||
private final LoadingCache<String, DateTime> cache;
|
||||
|
||||
private DomainDeletionTimeCache(LoadingCache<String, DateTime> cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
/** Returns the domain's deletion time, or null if it doesn't currently exist. */
|
||||
public Optional<DateTime> getDeletionTimeForDomain(String domainName) {
|
||||
return Optional.ofNullable(cache.get(domainName));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2025 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.flows.domain;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import jakarta.inject.Singleton;
|
||||
|
||||
/** Dagger module to provide the {@link DomainDeletionTimeCache}. */
|
||||
@Module
|
||||
public class DomainDeletionTimeCacheModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public static DomainDeletionTimeCache provideDomainDeletionTimeCache() {
|
||||
return DomainDeletionTimeCache.create();
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ public final class ForeignKeyUtils {
|
||||
*/
|
||||
public static <E extends EppResource> ImmutableMap<String, VKey<E>> load(
|
||||
Class<E> clazz, Collection<String> foreignKeys, final DateTime now) {
|
||||
return load(clazz, foreignKeys, false).entrySet().stream()
|
||||
return loadMostRecentResources(clazz, foreignKeys, false).entrySet().stream()
|
||||
.filter(e -> now.isBefore(e.getValue().deletionTime()))
|
||||
.collect(toImmutableMap(Entry::getKey, e -> VKey.create(clazz, e.getValue().repoId())));
|
||||
}
|
||||
@@ -104,8 +104,9 @@ public final class ForeignKeyUtils {
|
||||
* same max {@code deleteTime}, usually {@code END_OF_TIME}, lest this method throws an error due
|
||||
* to duplicate keys.
|
||||
*/
|
||||
private static <E extends EppResource> ImmutableMap<String, MostRecentResource> load(
|
||||
Class<E> clazz, Collection<String> foreignKeys, boolean useReplicaTm) {
|
||||
public static <E extends EppResource>
|
||||
ImmutableMap<String, MostRecentResource> loadMostRecentResources(
|
||||
Class<E> clazz, Collection<String> foreignKeys, boolean useReplicaTm) {
|
||||
String fkProperty = RESOURCE_TYPE_TO_FK_PROPERTY.get(clazz);
|
||||
JpaTransactionManager tmToUse = useReplicaTm ? replicaTm() : tm();
|
||||
return tmToUse.reTransact(
|
||||
@@ -148,7 +149,7 @@ public final class ForeignKeyUtils {
|
||||
ImmutableList<String> foreignKeys =
|
||||
keys.stream().map(key -> (String) key.getKey()).collect(toImmutableList());
|
||||
ImmutableMap<String, MostRecentResource> existingKeys =
|
||||
ForeignKeyUtils.load(clazz, foreignKeys, true);
|
||||
ForeignKeyUtils.loadMostRecentResources(clazz, foreignKeys, true);
|
||||
// The above map only contains keys that exist in the database, so we re-add the
|
||||
// missing ones with Optional.empty() values for caching.
|
||||
return Maps.asMap(
|
||||
@@ -234,7 +235,7 @@ public final class ForeignKeyUtils {
|
||||
e -> VKey.create(clazz, e.getValue().get().repoId())));
|
||||
}
|
||||
|
||||
record MostRecentResource(String repoId, DateTime deletionTime) {
|
||||
public record MostRecentResource(String repoId, DateTime deletionTime) {
|
||||
|
||||
static MostRecentResource create(String repoId, DateTime deletionTime) {
|
||||
return new MostRecentResource(repoId, deletionTime);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.model.tmch;
|
||||
|
||||
import static google.registry.config.RegistryConfig.getClaimsListCacheDuration;
|
||||
import static google.registry.persistence.transaction.QueryComposer.Comparator.EQ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
@@ -79,14 +78,11 @@ public class ClaimsListDao {
|
||||
*/
|
||||
private static ClaimsList getUncached() {
|
||||
return tm().reTransact(
|
||||
() -> {
|
||||
Long revisionId =
|
||||
tm().query("SELECT MAX(revisionId) FROM ClaimsList", Long.class)
|
||||
.getSingleResult();
|
||||
return tm().createQueryComposer(ClaimsList.class)
|
||||
.where("revisionId", EQ, revisionId)
|
||||
.first();
|
||||
})
|
||||
() ->
|
||||
tm().query("FROM ClaimsList ORDER BY revisionId DESC", ClaimsList.class)
|
||||
.setMaxResults(1)
|
||||
.getResultStream()
|
||||
.findFirst())
|
||||
.orElse(ClaimsList.create(START_OF_TIME, ImmutableMap.of()));
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import google.registry.export.DriveModule;
|
||||
import google.registry.export.sheet.SheetsServiceModule;
|
||||
import google.registry.flows.ServerTridProviderModule;
|
||||
import google.registry.flows.custom.CustomLogicFactoryModule;
|
||||
import google.registry.flows.domain.DomainDeletionTimeCacheModule;
|
||||
import google.registry.groups.DirectoryModule;
|
||||
import google.registry.groups.GmailModule;
|
||||
import google.registry.groups.GroupsModule;
|
||||
@@ -66,6 +67,7 @@ import jakarta.inject.Singleton;
|
||||
CredentialModule.class,
|
||||
CustomLogicFactoryModule.class,
|
||||
DirectoryModule.class,
|
||||
DomainDeletionTimeCacheModule.class,
|
||||
DriveModule.class,
|
||||
GmailModule.class,
|
||||
GroupsModule.class,
|
||||
|
||||
@@ -26,6 +26,7 @@ import google.registry.export.DriveModule;
|
||||
import google.registry.export.sheet.SheetsServiceModule;
|
||||
import google.registry.flows.ServerTridProviderModule;
|
||||
import google.registry.flows.custom.CustomLogicFactoryModule;
|
||||
import google.registry.flows.domain.DomainDeletionTimeCacheModule;
|
||||
import google.registry.groups.DirectoryModule;
|
||||
import google.registry.groups.GmailModule;
|
||||
import google.registry.groups.GroupsModule;
|
||||
@@ -56,6 +57,7 @@ import jakarta.inject.Singleton;
|
||||
CloudTasksUtilsModule.class,
|
||||
CredentialModule.class,
|
||||
CustomLogicFactoryModule.class,
|
||||
DomainDeletionTimeCacheModule.class,
|
||||
DirectoryModule.class,
|
||||
DriveModule.class,
|
||||
GmailModule.class,
|
||||
|
||||
@@ -22,6 +22,7 @@ import google.registry.config.CredentialModule;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.flows.ServerTridProviderModule;
|
||||
import google.registry.flows.custom.CustomLogicFactoryModule;
|
||||
import google.registry.flows.domain.DomainDeletionTimeCacheModule;
|
||||
import google.registry.groups.DirectoryModule;
|
||||
import google.registry.groups.GmailModule;
|
||||
import google.registry.groups.GroupsModule;
|
||||
@@ -50,6 +51,7 @@ import jakarta.inject.Singleton;
|
||||
CustomLogicFactoryModule.class,
|
||||
CloudTasksUtilsModule.class,
|
||||
DirectoryModule.class,
|
||||
DomainDeletionTimeCacheModule.class,
|
||||
FrontendRequestComponentModule.class,
|
||||
GmailModule.class,
|
||||
GroupsModule.class,
|
||||
|
||||
@@ -23,6 +23,7 @@ import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.export.DriveModule;
|
||||
import google.registry.flows.ServerTridProviderModule;
|
||||
import google.registry.flows.custom.CustomLogicFactoryModule;
|
||||
import google.registry.flows.domain.DomainDeletionTimeCacheModule;
|
||||
import google.registry.groups.DirectoryModule;
|
||||
import google.registry.groups.GroupsModule;
|
||||
import google.registry.groups.GroupssettingsModule;
|
||||
@@ -47,6 +48,7 @@ import jakarta.inject.Singleton;
|
||||
CustomLogicFactoryModule.class,
|
||||
CloudTasksUtilsModule.class,
|
||||
DirectoryModule.class,
|
||||
DomainDeletionTimeCacheModule.class,
|
||||
DriveModule.class,
|
||||
GroupsModule.class,
|
||||
GroupssettingsModule.class,
|
||||
|
||||
@@ -25,6 +25,7 @@ import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
|
||||
import google.registry.flows.custom.CustomLogicFactory;
|
||||
import google.registry.flows.custom.TestCustomLogicFactory;
|
||||
import google.registry.flows.domain.DomainDeletionTimeCache;
|
||||
import google.registry.flows.domain.DomainFlowTmchUtils;
|
||||
import google.registry.monitoring.whitebox.EppMetric;
|
||||
import google.registry.request.RequestScope;
|
||||
@@ -126,6 +127,11 @@ public interface EppTestComponent {
|
||||
ServerTridProvider provideServerTridProvider() {
|
||||
return new FakeServerTridProvider();
|
||||
}
|
||||
|
||||
@Provides
|
||||
DomainDeletionTimeCache provideDomainDeletionTimeCache() {
|
||||
return DomainDeletionTimeCache.create();
|
||||
}
|
||||
}
|
||||
|
||||
class FakeServerTridProvider implements ServerTridProvider {
|
||||
|
||||
@@ -149,7 +149,6 @@ import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeem
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.NonexistentAllocationTokenException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.OnlyToolCanPassMetadataException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException;
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
import google.registry.model.billing.BillingBase;
|
||||
import google.registry.model.billing.BillingBase.Flag;
|
||||
@@ -1238,8 +1237,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
void testFailure_alreadyExists() throws Exception {
|
||||
persistContactsAndHosts();
|
||||
persistActiveDomain(getUniqueIdFromCommand());
|
||||
ResourceAlreadyExistsForThisClientException thrown =
|
||||
assertThrows(ResourceAlreadyExistsForThisClientException.class, this::runFlow);
|
||||
ResourceCreateContentionException thrown =
|
||||
assertThrows(ResourceCreateContentionException.class, this::runFlow);
|
||||
assertAboutEppExceptions()
|
||||
.that(thrown)
|
||||
.marshalsToXml()
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2025 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.flows.domain;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainAsDeleted;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link DomainDeletionTimeCache}. */
|
||||
public class DomainDeletionTimeCacheTest {
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2025-10-01T00:00:00.000Z"));
|
||||
private final DomainDeletionTimeCache cache = DomainDeletionTimeCache.create();
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
DatabaseHelper.createTld("tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDomainAvailable_null() {
|
||||
assertThat(getDeletionTimeFromCache("nonexistent.tld")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDomainNotAvailable_notDeleted() {
|
||||
persistActiveDomain("active.tld");
|
||||
assertThat(getDeletionTimeFromCache("active.tld")).hasValue(END_OF_TIME);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDomainAvailable_deletedInFuture() {
|
||||
persistDomainAsDeleted(persistActiveDomain("domain.tld"), clock.nowUtc().plusDays(1));
|
||||
assertThat(getDeletionTimeFromCache("domain.tld")).hasValue(clock.nowUtc().plusDays(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCache_returnsOldData() {
|
||||
Domain domain = persistActiveDomain("domain.tld");
|
||||
assertThat(getDeletionTimeFromCache("domain.tld")).hasValue(END_OF_TIME);
|
||||
persistDomainAsDeleted(domain, clock.nowUtc().plusDays(1));
|
||||
// Without intervention, the cache should have the old data
|
||||
assertThat(getDeletionTimeFromCache("domain.tld")).hasValue(END_OF_TIME);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCache_returnsNewDataAfterDomainCreate() {
|
||||
// Null deletion dates (meaning an avilable domain) shouldn't be cached
|
||||
assertThat(getDeletionTimeFromCache("domain.tld")).isEmpty();
|
||||
persistDomainAsDeleted(persistActiveDomain("domain.tld"), clock.nowUtc().plusDays(1));
|
||||
assertThat(getDeletionTimeFromCache("domain.tld")).hasValue(clock.nowUtc().plusDays(1));
|
||||
}
|
||||
|
||||
private Optional<DateTime> getDeletionTimeFromCache(String domainName) {
|
||||
return tm().transact(() -> cache.getDeletionTimeForDomain(domainName));
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import google.registry.config.CredentialModule;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.flows.ServerTridProviderModule;
|
||||
import google.registry.flows.custom.CustomLogicFactoryModule;
|
||||
import google.registry.flows.domain.DomainDeletionTimeCacheModule;
|
||||
import google.registry.groups.GmailModule;
|
||||
import google.registry.groups.GroupsModule;
|
||||
import google.registry.groups.GroupssettingsModule;
|
||||
@@ -43,6 +44,7 @@ import jakarta.inject.Singleton;
|
||||
CredentialModule.class,
|
||||
CustomLogicFactoryModule.class,
|
||||
CloudTasksUtilsModule.class,
|
||||
DomainDeletionTimeCacheModule.class,
|
||||
FrontendRequestComponent.FrontendRequestComponentModule.class,
|
||||
GmailModule.class,
|
||||
GroupsModule.class,
|
||||
|
||||
Reference in New Issue
Block a user