Cut over to batched async deletion for contacts/hosts

Also consolidates the DNS refresh functionality in AsyncFlowUtils that was
being used by HostUpdateFlow into AsyncFlowEnqueuer.

TESTED=I threw together some batch scripts to create dozens of contacts on
alpha and then request their deletion, and the [] ran fine and
successfully deleted them in batches.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=133714691
This commit is contained in:
mcilwain 2016-09-20 09:29:39 -07:00 committed by Ben McIlwain
parent 65ff6b45d1
commit 2dcac3ca68
11 changed files with 74 additions and 190 deletions

View file

@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.EppResourceUtils.loadByUniqueId;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.tmch.ClaimsListShardTest.createTestClaimsListShard;
import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
@ -34,8 +35,10 @@ import google.registry.model.index.EppResourceIndexBucket;
import google.registry.model.tmch.ClaimsListShard.ClaimsListRevision;
import google.registry.model.tmch.ClaimsListShard.ClaimsListSingleton;
import google.registry.testing.ExceptionRule;
import google.registry.testing.TaskQueueHelper.TaskMatcher;
import google.registry.util.TypeUtils.TypeInstantiator;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.Rule;
import org.junit.Test;
@ -136,4 +139,18 @@ public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource
assertThat(indices.get(0).getBucket())
.isEqualTo(EppResourceIndexBucket.getBucketKey(Key.create(resource)));
}
/** Asserts the presence of a single enqueued async contact or host deletion */
protected static <T extends EppResource> void assertAsyncDeletionTaskEnqueued(
T resource, String requestingClientId, boolean isSuperuser) throws Exception {
String expectedPayload =
String.format(
"resourceKey=%s&requestingClientId=%s&isSuperuser=%s",
Key.create(resource).getString(), requestingClientId, Boolean.toString(isSuperuser));
assertTasksEnqueued(
"async-delete-pull",
new TaskMatcher()
.etaDelta(Duration.standardSeconds(75), Duration.standardSeconds(105)) // expected: 90
.payload(expectedPayload));
}
}

View file

@ -126,7 +126,7 @@ public class DeleteContactsAndHostsActionTest
public void setup() throws Exception {
enqueuer = new AsyncFlowEnqueuer();
enqueuer.asyncDeleteDelay = Duration.ZERO;
enqueuer.queue = QueueFactory.getQueue(QUEUE_ASYNC_DELETE);
enqueuer.asyncDeletePullQueue = QueueFactory.getQueue(QUEUE_ASYNC_DELETE);
enqueuer.retrier = new Retrier(new FakeSleeper(clock), 1);
action = new DeleteContactsAndHostsAction();

View file

@ -14,8 +14,6 @@
package google.registry.flows.contact;
import static google.registry.flows.async.AsyncFlowUtils.ASYNC_FLOW_QUEUE_NAME;
import static google.registry.request.Actions.getPathForAction;
import static google.registry.testing.ContactResourceSubject.assertAboutContacts;
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static google.registry.testing.DatastoreHelper.createTld;
@ -24,22 +22,16 @@ import static google.registry.testing.DatastoreHelper.newDomainResource;
import static google.registry.testing.DatastoreHelper.persistActiveContact;
import static google.registry.testing.DatastoreHelper.persistDeletedContact;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import google.registry.flows.async.DeleteContactResourceAction;
import google.registry.flows.async.DeleteEppResourceAction;
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
import google.registry.flows.exceptions.ResourceToDeleteIsReferencedException;
import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException;
import google.registry.model.contact.ContactResource;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.TaskQueueHelper.TaskMatcher;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Test;
@ -76,18 +68,7 @@ public class ContactDeleteFlowTest
runFlowAssertResponse(readFile("contact_delete_response.xml"));
ContactResource deletedContact = reloadResourceByUniqueId();
assertAboutContacts().that(deletedContact).hasStatusValue(StatusValue.PENDING_DELETE);
assertTasksEnqueued(ASYNC_FLOW_QUEUE_NAME, new TaskMatcher()
.url(getPathForAction(DeleteContactResourceAction.class))
.etaDelta(Duration.standardSeconds(75), Duration.standardSeconds(105)) // expected: 90
.param(
DeleteEppResourceAction.PARAM_REQUESTING_CLIENT_ID,
"TheRegistrar")
.param(
DeleteEppResourceAction.PARAM_IS_SUPERUSER,
Boolean.toString(false))
.param(
DeleteEppResourceAction.PARAM_RESOURCE_KEY,
Key.create(deletedContact).getString()));
assertAsyncDeletionTaskEnqueued(deletedContact, "TheRegistrar", false);
assertAboutContacts().that(deletedContact)
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.CONTACT_PENDING_DELETE);
@ -146,18 +127,7 @@ public class ContactDeleteFlowTest
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("contact_delete_response.xml"));
ContactResource deletedContact = reloadResourceByUniqueId();
assertAboutContacts().that(deletedContact).hasStatusValue(StatusValue.PENDING_DELETE);
assertTasksEnqueued(ASYNC_FLOW_QUEUE_NAME, new TaskMatcher()
.url(getPathForAction(DeleteContactResourceAction.class))
.etaDelta(Duration.standardSeconds(75), Duration.standardSeconds(105)) // expected: 90
.param(
DeleteEppResourceAction.PARAM_REQUESTING_CLIENT_ID,
"NewRegistrar")
.param(
DeleteEppResourceAction.PARAM_IS_SUPERUSER,
Boolean.toString(true))
.param(
DeleteEppResourceAction.PARAM_RESOURCE_KEY,
Key.create(deletedContact).getString()));
assertAsyncDeletionTaskEnqueued(deletedContact, "NewRegistrar", true);
assertAboutContacts().that(deletedContact)
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.CONTACT_PENDING_DELETE);

View file

@ -13,9 +13,6 @@
// limitations under the License.
package google.registry.flows.host;
import static google.registry.flows.async.AsyncFlowUtils.ASYNC_FLOW_QUEUE_NAME;
import static google.registry.request.Actions.getPathForAction;
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.newDomainApplication;
@ -26,22 +23,17 @@ import static google.registry.testing.DatastoreHelper.persistDeletedHost;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.HostResourceSubject.assertAboutHosts;
import static google.registry.testing.TaskQueueHelper.assertNoDnsTasksEnqueued;
import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import google.registry.flows.async.DeleteEppResourceAction;
import google.registry.flows.async.DeleteHostResourceAction;
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
import google.registry.flows.exceptions.ResourceToDeleteIsReferencedException;
import google.registry.flows.exceptions.ResourceToMutateDoesNotExistException;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.TaskQueueHelper.TaskMatcher;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Test;
@ -77,18 +69,7 @@ public class HostDeleteFlowTest extends ResourceFlowTestCase<HostDeleteFlow, Hos
runFlowAssertResponse(readFile("host_delete_response.xml"));
HostResource deletedHost = reloadResourceByUniqueId();
assertAboutHosts().that(deletedHost).hasStatusValue(StatusValue.PENDING_DELETE);
assertTasksEnqueued(ASYNC_FLOW_QUEUE_NAME, new TaskMatcher()
.url(getPathForAction(DeleteHostResourceAction.class))
.etaDelta(Duration.standardSeconds(75), Duration.standardSeconds(105)) // expected: 90
.param(
DeleteEppResourceAction.PARAM_REQUESTING_CLIENT_ID,
"TheRegistrar")
.param(
DeleteEppResourceAction.PARAM_IS_SUPERUSER,
Boolean.toString(false))
.param(
DeleteEppResourceAction.PARAM_RESOURCE_KEY,
Key.create(deletedHost).getString()));
assertAsyncDeletionTaskEnqueued(deletedHost, "TheRegistrar", false);
assertAboutHosts().that(deletedHost)
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.HOST_PENDING_DELETE);
@ -148,18 +129,7 @@ public class HostDeleteFlowTest extends ResourceFlowTestCase<HostDeleteFlow, Hos
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("host_delete_response.xml"));
HostResource deletedHost = reloadResourceByUniqueId();
assertAboutHosts().that(deletedHost).hasStatusValue(StatusValue.PENDING_DELETE);
assertTasksEnqueued(ASYNC_FLOW_QUEUE_NAME, new TaskMatcher()
.url(getPathForAction(DeleteHostResourceAction.class))
.etaDelta(Duration.standardSeconds(75), Duration.standardSeconds(105)) // expected: 90
.param(
DeleteEppResourceAction.PARAM_REQUESTING_CLIENT_ID,
"NewRegistrar")
.param(
DeleteEppResourceAction.PARAM_IS_SUPERUSER,
Boolean.toString(true))
.param(
DeleteEppResourceAction.PARAM_RESOURCE_KEY,
Key.create(deletedHost).getString()));
assertAsyncDeletionTaskEnqueued(deletedHost, "NewRegistrar", true);
assertAboutHosts().that(deletedHost)
.hasOnlyOneHistoryEntryWhich()
.hasType(HistoryEntry.Type.HOST_PENDING_DELETE);

View file

@ -15,7 +15,6 @@
package google.registry.flows.host;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.flows.async.AsyncFlowUtils.ASYNC_FLOW_QUEUE_NAME;
import static google.registry.model.EppResourceUtils.loadByUniqueId;
import static google.registry.request.Actions.getPathForAction;
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
@ -161,7 +160,7 @@ public class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Hos
HostResource renamedHost = doSuccessfulTest();
assertThat(renamedHost.getSuperordinateDomain()).isNull();
// Task enqueued to change the NS record of the referencing domain via mapreduce.
assertTasksEnqueued(ASYNC_FLOW_QUEUE_NAME, new TaskMatcher()
assertTasksEnqueued("flows-async", new TaskMatcher()
.url(getPathForAction(DnsRefreshForHostRenameAction.class))
.param(DnsRefreshForHostRenameAction.PARAM_HOST_KEY, Key.create(renamedHost).getString()));
}

View file

@ -44,6 +44,7 @@ import com.google.common.collect.Multimap;
import com.google.common.net.HttpHeaders;
import com.google.common.net.MediaType;
import google.registry.dns.DnsConstants;
import google.registry.model.ImmutableObject;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
@ -277,7 +278,7 @@ public class TaskQueueHelper {
}
/** An adapter to clean up a {@link TaskStateInfo} for ease of matching. */
private static class MatchableTaskInfo {
private static class MatchableTaskInfo extends ImmutableObject {
String taskName;
String method;