Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a transactional parameter to @Action #2133

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,9 @@ public void run() {
!RegistryEnvironment.get().equals(PRODUCTION),
"This action is not safe to run on PRODUCTION.");

tm().transact(
() -> {
LOAD_TEST_REGISTRARS.forEach(this::deletePollMessages);
tm().loadAllOfStream(Contact.class).forEach(this::deleteContact);
tm().loadAllOfStream(Host.class).forEach(this::deleteHost);
});
LOAD_TEST_REGISTRARS.forEach(this::deletePollMessages);
tm().loadAllOfStream(Contact.class).forEach(this::deleteContact);
tm().loadAllOfStream(Host.class).forEach(this::deleteHost);
}

private void deletePollMessages(String registrarId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ public Optional<RegistrarPoc> getWhoisAbuseContact() {
}

private ImmutableSet<RegistrarPoc> getContactPocs() {
return tm().transact(
return tm().reTransact(
() ->
tm().query("FROM RegistrarPoc WHERE registrarId = :registrarId", RegistrarPoc.class)
.setParameter("registrarId", registrarId)
Expand Down Expand Up @@ -944,7 +944,7 @@ public static String checkValidEmail(String email) {

/** Loads all registrar entities directly from the database. */
public static Iterable<Registrar> loadAll() {
return tm().transact(() -> tm().loadAllOf(Registrar.class));
return tm().loadAllOf(Registrar.class);
}

/** Loads all registrar entities using an in-memory cache. */
Expand All @@ -962,7 +962,7 @@ public static ImmutableSet<VKey<Registrar>> loadAllKeysCached() {
/** Loads and returns a registrar entity by its id directly from the database. */
public static Optional<Registrar> loadByRegistrarId(String registrarId) {
checkArgument(!Strings.isNullOrEmpty(registrarId), "registrarId must be specified");
return tm().transact(() -> tm().loadByKeyIfPresent(createVKey(registrarId)));
return tm().reTransact(() -> tm().loadByKeyIfPresent(createVKey(registrarId)));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
public void run() {
try {
ImmutableMap<String, String> ianaIdsToUrls = getIanaIdsToUrls();
tm().transact(() -> processAllRegistrars(ianaIdsToUrls));
processAllRegistrars(ianaIdsToUrls);
} catch (Exception e) {
throw new InternalServerErrorException("Error when retrieving RDAP base URL CSV file", e);
}
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/google/registry/request/Action.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ public String getServiceId() {
/** HTTP methods that request processor should allow. */
Method[] method() default Method.GET;

/** Whether to create a wrapping DB transaction around the execution of the action. */
boolean transactional() default true;

/**
* Indicates request processor should print "OK" to the HTTP client on success.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
Expand Down Expand Up @@ -155,7 +156,12 @@ public void handleRequest(HttpServletRequest req, HttpServletResponse rsp) throw
boolean success = true;
DateTime startTime = clock.nowUtc();
try {
route.get().instantiator().apply(component).run();
Runnable action = route.get().instantiator().apply(component);
if (route.get().action().transactional()) {
tm().transact(action::run);
} else {
action.run();
}
if (route.get().action().automaticallyPrintOk()) {
rsp.setContentType(PLAIN_TEXT_UTF_8.toString());
rsp.getWriter().write("OK\n");
Expand Down
35 changes: 21 additions & 14 deletions core/src/main/java/google/registry/tools/ConfirmingCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package google.registry.tools;

import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.tools.CommandUtilities.promptForYes;
import static java.nio.charset.StandardCharsets.UTF_8;

Expand Down Expand Up @@ -44,20 +45,26 @@ protected ConfirmingCommand() {

@Override
public final void run() throws Exception {
if (checkExecutionState()) {
init();
printLineIfNotEmpty(prompt(), printStream);
if (dontRunCommand()) {
// This typically happens when all of the work is accomplished inside of prompt(), so do
// nothing further.
return;
} else if (force || promptForYes("Perform this command?")) {
printStream.println("Running ... ");
printStream.println(execute());
printLineIfNotEmpty(postExecute(), printStream);
} else {
printStream.println("Command aborted.");
}
if (!checkExecutionState()) {
return;
}

tm().transact(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really wish there were a way to make these 3 repeated calls nicer, but I couldn't find any. You can't extract to a helper function that takes a lambda.

() -> {
init();
printLineIfNotEmpty(prompt(), printStream);
});

if (dontRunCommand()) {
// This typically happens when all the work is accomplished inside of prompt(), so do
// nothing further.
return;
} else if (force || promptForYes("Perform this command?")) {
printStream.println("Running ... ");
tm().transact(() -> printStream.println(execute()));
tm().transact(() -> printLineIfNotEmpty(postExecute(), printStream));
} else {
printStream.println("Command aborted.");
}
printStream.close();
errorPrintStream.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

package google.registry.tools;

import static google.registry.persistence.transaction.TransactionManagerFactory.tm;

import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.base.Joiner;
Expand Down Expand Up @@ -43,6 +45,8 @@ final class WhoisQueryCommand implements Command {

@Override
public void run() {
System.out.println(whois.lookup(Joiner.on(' ').join(mainParameters), unicode, fullOutput));
System.out.println(
tm().transact(
() -> whois.lookup(Joiner.on(' ').join(mainParameters), unicode, fullOutput)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@
service = Action.Service.DEFAULT,
path = ConsoleOteSetupAction.PATH,
method = {Method.POST, Method.GET},
auth = Auth.AUTH_PUBLIC_LEGACY)
auth = Auth.AUTH_PUBLIC_LEGACY,
transactional = false // Needs to explicitly run more than one transaction sequentially.
)
public final class ConsoleOteSetupAction extends HtmlAction {

public static final String PATH = "/registrar-ote-setup";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@
service = Action.Service.DEFAULT,
path = RegistrarSettingsAction.PATH,
method = Action.Method.POST,
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
auth = Auth.AUTH_PUBLIC_LOGGED_IN,
transactional = false // Needs to explicitly run more than one transaction sequentially.
)
public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonAction {

public static final String PATH = "/registrar-settings";
Expand Down Expand Up @@ -157,9 +159,9 @@ public Map<String, Object> handleJsonRequest(Map<String, ?> input) {
try {
switch (op) {
case "update":
return update(args, registrarId).toJsonResponse();
return update(args, registrarId);
case "read":
return read(registrarId).toJsonResponse();
return read(registrarId);
default:
throw new IllegalArgumentException("Unknown or unsupported operation: " + op);
}
Expand Down Expand Up @@ -215,8 +217,11 @@ static EmailInfo create(
}
}

private RegistrarResult read(String registrarId) {
return RegistrarResult.create("Success", loadRegistrarUnchecked(registrarId));
private Map<String, Object> read(String registrarId) {
return tm().transact(
() ->
RegistrarResult.create("Success", loadRegistrarUnchecked(registrarId))
.toJsonResponse());
}

private Registrar loadRegistrarUnchecked(String registrarId) {
Expand All @@ -227,15 +232,19 @@ private Registrar loadRegistrarUnchecked(String registrarId) {
}
}

private RegistrarResult update(final Map<String, ?> args, String registrarId) {
private Map<String, Object> update(final Map<String, ?> args, String registrarId) {
// Email the updates
sendExternalUpdatesIfNecessary(tm().transact(() -> saveUpdates(args, registrarId)));
// Reload the result outside the transaction to get the most recent version
return RegistrarResult.create("Saved " + registrarId, loadRegistrarUnchecked(registrarId));
return tm().transact(
() ->
RegistrarResult.create("Saved " + registrarId, loadRegistrarUnchecked(registrarId))
.toJsonResponse());
}

/** Saves the updates and returns info needed for the update email */
private EmailInfo saveUpdates(final Map<String, ?> args, String registrarId) {
tm().assertInTransaction();
// We load the registrar here rather than outside the transaction - to make
// sure we have the latest version. This one is loaded inside the transaction, so it's
// guaranteed to not change before we update it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private void runAction() {
action.gSuiteDomainName = "domain-registry.example";
action.response = response;
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
action.run();
tm().transact(action::run);
}

@Test
Expand Down Expand Up @@ -182,7 +182,11 @@ void test_doPost_addsAndRemovesContacts_acrossMultipleRegistrars() throws Except
"[email protected]",
Role.MEMBER);
verify(response).setStatus(SC_OK);
assertThat(Iterables.filter(Registrar.loadAll(), Registrar::getContactsRequireSyncing))
assertThat(
tm().transact(
() ->
Iterables.filter(
Registrar.loadAll(), Registrar::getContactsRequireSyncing)))
.isEmpty();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2544,11 +2544,13 @@ void testFailure_disabledRegistrarCantCreateDomain() {
private void doFailingTest_invalidRegistrarState(State registrarState) {
persistContactsAndHosts();
persistResource(
Registrar.loadByRegistrarId("TheRegistrar")
.get()
.asBuilder()
.setState(registrarState)
.build());
tm().transact(
() ->
Registrar.loadByRegistrarId("TheRegistrar")
.get()
.asBuilder()
.setState(registrarState)
.build()));
EppException thrown =
assertThrows(RegistrarMustBeActiveForThisOperationException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -977,9 +977,9 @@ public static <R extends ImmutableObject> R persistResource(final R resource) {
assertWithMessage("Attempting to persist a Builder is almost certainly an error in test code")
.that(resource)
.isNotInstanceOf(Buildable.Builder.class);
tm().transact(() -> tm().put(resource));
tm().reTransact(() -> tm().put(resource));
maybeAdvanceClock();
return tm().transact(() -> tm().loadByEntity(resource));
return tm().reTransact(() -> tm().loadByEntity(resource));
}

/** Persists the specified resources to the DB. */
Expand Down Expand Up @@ -1161,7 +1161,7 @@ public static ImmutableMap<String, PremiumEntry> loadPremiumEntries(PremiumList
/** Loads and returns the registrar with the given client ID, or throws IAE if not present. */
public static Registrar loadRegistrar(String registrarId) {
return checkArgumentPresent(
Registrar.loadByRegistrarId(registrarId),
tm().reTransact(() -> Registrar.loadByRegistrarId(registrarId)),
"Error in tests: Registrar %s does not exist",
registrarId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static google.registry.persistence.transaction.JpaTransactionManagerExtension.makeRegistrar2;
import static google.registry.persistence.transaction.JpaTransactionManagerExtension.makeRegistrarContact2;
import static google.registry.persistence.transaction.JpaTransactionManagerExtension.makeRegistrarContact3;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.server.Fixture.BASIC;
import static google.registry.server.Route.route;
import static google.registry.testing.DatabaseHelper.createTld;
Expand Down Expand Up @@ -172,13 +173,15 @@ void settingsContactEdit_setRegistryLockPassword() throws Throwable {
driver.diffPage("contact_view");

RegistrarPoc contact =
loadRegistrar("TheRegistrar").getContacts().stream()
.filter(c -> "[email protected]".equals(c.getEmailAddress()))
.findFirst()
.get();
assertThat(contact.verifyRegistryLockPassword("password")).isTrue();
assertThat(contact.getRegistryLockEmailAddress())
.isEqualTo(Optional.of("[email protected]"));
tm().transact(
() ->
loadRegistrar("TheRegistrar").getContacts().stream()
.filter(c -> "[email protected]".equals(c.getEmailAddress()))
.findFirst()
.get());
assertThat(contact.verifyRegistryLockPassword("password")).isTrue();
assertThat(contact.getRegistryLockEmailAddress())
.isEqualTo(Optional.of("[email protected]"));
}

@RetryingTest(3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package google.registry.webdriver;

import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.server.Fixture.BASIC;
import static google.registry.server.Route.route;
import static google.registry.testing.DatabaseHelper.loadRegistrar;
Expand Down Expand Up @@ -176,7 +177,8 @@ void testWhoisSettingsEdit() throws Throwable {
void testContactSettingsView() throws Throwable {
driver.get(server.getUrl("/registrar#contact-settings"));
driver.waitForDisplayedElement(By.id("reg-app-btn-add"));
ImmutableList<RegistrarPoc> contacts = loadRegistrar("TheRegistrar").getContacts().asList();
ImmutableList<RegistrarPoc> contacts =
tm().transact(() -> loadRegistrar("TheRegistrar").getContacts().asList());
for (RegistrarPoc contact : contacts) {
assertEltTextPresent(By.id("contacts[0].name"), contact.getName());
assertEltTextPresent(By.id("contacts[0].emailAddress"), contact.getEmailAddress());
Expand Down
Loading