mirror of
https://github.com/google/nomulus
synced 2026-09-19 22:44:26 +00:00
Skip poll messages on deletions for configured registrars (#2613)
See b/379331882 for more details
This commit is contained in:
@@ -1743,6 +1743,17 @@ public final class RegistryConfig {
|
|||||||
CONFIG_SETTINGS.get().registryPolicy.tieredPricingPromotionRegistrarIds);
|
CONFIG_SETTINGS.get().registryPolicy.tieredPricingPromotionRegistrarIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of registrars for which we do not send poll messages on standard domain deletion.
|
||||||
|
*
|
||||||
|
* <p>For these registrars we won't send a poll message in order to avoid database contention. See
|
||||||
|
* b/379331882 for more details.
|
||||||
|
*/
|
||||||
|
public static ImmutableSet<String> getNoPollMessageOnDeletionRegistrarIds() {
|
||||||
|
return ImmutableSet.copyOf(
|
||||||
|
CONFIG_SETTINGS.get().registryPolicy.noPollMessageOnDeletionRegistrarIds);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Memoizes loading of the {@link RegistryConfigSettings} POJO.
|
* Memoizes loading of the {@link RegistryConfigSettings} POJO.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ public class RegistryConfigSettings {
|
|||||||
public boolean requireSslCertificates;
|
public boolean requireSslCertificates;
|
||||||
public double sunriseDomainCreateDiscount;
|
public double sunriseDomainCreateDiscount;
|
||||||
public Set<String> tieredPricingPromotionRegistrarIds;
|
public Set<String> tieredPricingPromotionRegistrarIds;
|
||||||
|
public Set<String> noPollMessageOnDeletionRegistrarIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Configuration for Hibernate. */
|
/** Configuration for Hibernate. */
|
||||||
|
|||||||
@@ -220,6 +220,9 @@ registryPolicy:
|
|||||||
# In addition, we will return the non-promotional (i.e. incorrect) price on
|
# In addition, we will return the non-promotional (i.e. incorrect) price on
|
||||||
# domain create requests.
|
# domain create requests.
|
||||||
tieredPricingPromotionRegistrarIds: []
|
tieredPricingPromotionRegistrarIds: []
|
||||||
|
# List of registrars for which we won't send poll message on standard domain
|
||||||
|
# deletions.
|
||||||
|
noPollMessageOnDeletionRegistrarIds: []
|
||||||
|
|
||||||
hibernate:
|
hibernate:
|
||||||
# If set to false, calls to tm().transact() cannot be nested. If set to true,
|
# If set to false, calls to tm().transact() cannot be nested. If set to true,
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ registryPolicy:
|
|||||||
Line 2 is this 1.
|
Line 2 is this 1.
|
||||||
tieredPricingPromotionRegistrarIds:
|
tieredPricingPromotionRegistrarIds:
|
||||||
- NewRegistrar
|
- NewRegistrar
|
||||||
|
noPollMessageOnDeletionRegistrarIds:
|
||||||
|
- NewRegistrar
|
||||||
|
|
||||||
caching:
|
caching:
|
||||||
singletonCacheRefreshSeconds: 0
|
singletonCacheRefreshSeconds: 0
|
||||||
|
|||||||
@@ -44,7 +44,9 @@ import com.google.common.collect.ImmutableList;
|
|||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.ImmutableSortedSet;
|
import com.google.common.collect.ImmutableSortedSet;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.common.flogger.FluentLogger;
|
||||||
import google.registry.batch.AsyncTaskEnqueuer;
|
import google.registry.batch.AsyncTaskEnqueuer;
|
||||||
|
import google.registry.config.RegistryConfig;
|
||||||
import google.registry.flows.EppException;
|
import google.registry.flows.EppException;
|
||||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||||
import google.registry.flows.ExtensionManager;
|
import google.registry.flows.ExtensionManager;
|
||||||
@@ -117,6 +119,8 @@ import org.joda.time.Duration;
|
|||||||
@ReportingSpec(ActivityReportField.DOMAIN_DELETE)
|
@ReportingSpec(ActivityReportField.DOMAIN_DELETE)
|
||||||
public final class DomainDeleteFlow implements MutatingFlow {
|
public final class DomainDeleteFlow implements MutatingFlow {
|
||||||
|
|
||||||
|
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||||
|
|
||||||
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of(
|
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of(
|
||||||
StatusValue.CLIENT_DELETE_PROHIBITED,
|
StatusValue.CLIENT_DELETE_PROHIBITED,
|
||||||
StatusValue.PENDING_DELETE,
|
StatusValue.PENDING_DELETE,
|
||||||
@@ -212,10 +216,17 @@ public final class DomainDeleteFlow implements MutatingFlow {
|
|||||||
// superuser (i.e. the registrar didn't request this delete and thus should be notified even if
|
// superuser (i.e. the registrar didn't request this delete and thus should be notified even if
|
||||||
// it is synchronous).
|
// it is synchronous).
|
||||||
if (durationUntilDelete.isLongerThan(Duration.ZERO) || isSuperuser) {
|
if (durationUntilDelete.isLongerThan(Duration.ZERO) || isSuperuser) {
|
||||||
PollMessage.OneTime deletePollMessage =
|
if (RegistryConfig.getNoPollMessageOnDeletionRegistrarIds()
|
||||||
createDeletePollMessage(existingDomain, domainHistoryId, deletionTime);
|
.contains(existingDomain.getCurrentSponsorRegistrarId())) {
|
||||||
entitiesToSave.add(deletePollMessage);
|
logger.atInfo().log(
|
||||||
builder.setDeletePollMessage(deletePollMessage.createVKey());
|
"Skipping poll message on domain deletion for registrar %s due to configuration",
|
||||||
|
existingDomain.getCurrentSponsorRegistrarId());
|
||||||
|
} else {
|
||||||
|
PollMessage.OneTime deletePollMessage =
|
||||||
|
createDeletePollMessage(existingDomain, domainHistoryId, deletionTime);
|
||||||
|
entitiesToSave.add(deletePollMessage);
|
||||||
|
builder.setDeletePollMessage(deletePollMessage.createVKey());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a second poll message immediately if the domain is being deleted asynchronously by a
|
// Send a second poll message immediately if the domain is being deleted asynchronously by a
|
||||||
|
|||||||
@@ -183,7 +183,9 @@ public final class DomainRestoreRequestFlow implements MutatingFlow {
|
|||||||
DomainHistory domainHistory = buildDomainHistory(newDomain, now);
|
DomainHistory domainHistory = buildDomainHistory(newDomain, now);
|
||||||
entitiesToSave.add(newDomain, domainHistory, autorenewEvent, autorenewPollMessage);
|
entitiesToSave.add(newDomain, domainHistory, autorenewEvent, autorenewPollMessage);
|
||||||
tm().putAll(entitiesToSave.build());
|
tm().putAll(entitiesToSave.build());
|
||||||
tm().delete(existingDomain.getDeletePollMessage());
|
if (existingDomain.getDeletePollMessage() != null) {
|
||||||
|
tm().delete(existingDomain.getDeletePollMessage());
|
||||||
|
}
|
||||||
requestDomainDnsRefresh(existingDomain.getDomainName());
|
requestDomainDnsRefresh(existingDomain.getDomainName());
|
||||||
return responseBuilder
|
return responseBuilder
|
||||||
.setExtensions(createResponseExtensions(feesAndCredits, feeUpdate, isExpired))
|
.setExtensions(createResponseExtensions(feesAndCredits, feeUpdate, isExpired))
|
||||||
|
|||||||
@@ -1247,4 +1247,15 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
|||||||
clock.advanceOneMilli();
|
clock.advanceOneMilli();
|
||||||
runFlowAssertResponse(loadFile("domain_delete_response_fee_free_grace.xml"));
|
runFlowAssertResponse(loadFile("domain_delete_response_fee_free_grace.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_skipsPollMessage_whenConfigured() throws Exception {
|
||||||
|
setUpSuccessfulTest();
|
||||||
|
domain =
|
||||||
|
persistResource(
|
||||||
|
domain.asBuilder().setPersistedCurrentSponsorRegistrarId("NewRegistrar").build());
|
||||||
|
setRegistrarIdForFlow("NewRegistrar");
|
||||||
|
runFlowAssertResponse(loadFile("domain_delete_response_pending.xml"));
|
||||||
|
assertPollMessages();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ import google.registry.model.reporting.DomainTransactionRecord;
|
|||||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import google.registry.model.tld.Tld;
|
import google.registry.model.tld.Tld;
|
||||||
|
import google.registry.persistence.VKey;
|
||||||
import google.registry.testing.DatabaseHelper;
|
import google.registry.testing.DatabaseHelper;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -103,12 +104,12 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
|||||||
setEppInput("domain_update_restore_request.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
setEppInput("domain_update_restore_request.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void persistPendingDeleteDomain() throws Exception {
|
Domain persistPendingDeleteDomain() throws Exception {
|
||||||
// The domain is now past what had been its expiration date at the time of deletion.
|
// The domain is now past what had been its expiration date at the time of deletion.
|
||||||
persistPendingDeleteDomain(clock.nowUtc().minusDays(5));
|
return persistPendingDeleteDomain(clock.nowUtc().minusDays(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
void persistPendingDeleteDomain(DateTime expirationTime) throws Exception {
|
Domain persistPendingDeleteDomain(DateTime expirationTime) throws Exception {
|
||||||
Domain domain = persistResource(DatabaseHelper.newDomain(getUniqueIdFromCommand()));
|
Domain domain = persistResource(DatabaseHelper.newDomain(getUniqueIdFromCommand()));
|
||||||
HistoryEntry historyEntry =
|
HistoryEntry historyEntry =
|
||||||
persistResource(
|
persistResource(
|
||||||
@@ -118,29 +119,31 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
|||||||
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
|
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
|
||||||
.setDomain(domain)
|
.setDomain(domain)
|
||||||
.build());
|
.build());
|
||||||
persistResource(
|
domain =
|
||||||
domain
|
persistResource(
|
||||||
.asBuilder()
|
domain
|
||||||
.setRegistrationExpirationTime(expirationTime)
|
.asBuilder()
|
||||||
.setDeletionTime(clock.nowUtc().plusDays(35))
|
.setRegistrationExpirationTime(expirationTime)
|
||||||
.addGracePeriod(
|
.setDeletionTime(clock.nowUtc().plusDays(35))
|
||||||
GracePeriod.create(
|
.addGracePeriod(
|
||||||
GracePeriodStatus.REDEMPTION,
|
GracePeriod.create(
|
||||||
domain.getRepoId(),
|
GracePeriodStatus.REDEMPTION,
|
||||||
clock.nowUtc().plusDays(1),
|
domain.getRepoId(),
|
||||||
"TheRegistrar",
|
clock.nowUtc().plusDays(1),
|
||||||
null))
|
"TheRegistrar",
|
||||||
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
null))
|
||||||
.setDeletePollMessage(
|
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
||||||
persistResource(
|
.setDeletePollMessage(
|
||||||
new PollMessage.OneTime.Builder()
|
persistResource(
|
||||||
.setRegistrarId("TheRegistrar")
|
new PollMessage.OneTime.Builder()
|
||||||
.setEventTime(clock.nowUtc().plusDays(5))
|
.setRegistrarId("TheRegistrar")
|
||||||
.setHistoryEntry(historyEntry)
|
.setEventTime(clock.nowUtc().plusDays(5))
|
||||||
.build())
|
.setHistoryEntry(historyEntry)
|
||||||
.createVKey())
|
.build())
|
||||||
.build());
|
.createVKey())
|
||||||
|
.build());
|
||||||
clock.advanceOneMilli();
|
clock.advanceOneMilli();
|
||||||
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -491,6 +494,15 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
|||||||
loadFile("domain_update_restore_request_response_premium.xml"));
|
loadFile("domain_update_restore_request_response_premium.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_worksWithoutPollMessage() throws Exception {
|
||||||
|
Domain domain = persistPendingDeleteDomain();
|
||||||
|
VKey<PollMessage.OneTime> deletePollMessage = domain.getDeletePollMessage();
|
||||||
|
persistResource(domain.asBuilder().setDeletePollMessage(null).build());
|
||||||
|
DatabaseHelper.deleteByKey(deletePollMessage);
|
||||||
|
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFailure_doesNotExist() throws Exception {
|
void testFailure_doesNotExist() throws Exception {
|
||||||
ResourceDoesNotExistException thrown =
|
ResourceDoesNotExistException thrown =
|
||||||
|
|||||||
@@ -1168,6 +1168,10 @@ public final class DatabaseHelper {
|
|||||||
tm().transact(() -> tm().delete(resource));
|
tm().transact(() -> tm().delete(resource));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void deleteByKey(VKey<?> key) {
|
||||||
|
tm().transact(() -> tm().delete(key));
|
||||||
|
}
|
||||||
|
|
||||||
/** Force the create and update timestamps to get written into the resource. */
|
/** Force the create and update timestamps to get written into the resource. */
|
||||||
public static <R> R cloneAndSetAutoTimestamps(final R resource) {
|
public static <R> R cloneAndSetAutoTimestamps(final R resource) {
|
||||||
// We have to separate the read and write operation into different transactions otherwise JPA
|
// We have to separate the read and write operation into different transactions otherwise JPA
|
||||||
|
|||||||
Reference in New Issue
Block a user