Stop writing ReservedList to Datastore (#1163)

This commit is contained in:
sarahcaseybot
2021-05-17 17:46:21 -04:00
committed by GitHub
parent 16641e05a1
commit c1f0c29134
19 changed files with 53 additions and 692 deletions
@@ -255,7 +255,7 @@ public final class ReservedList
new CacheLoader<String, ReservedList>() {
@Override
public ReservedList load(String listName) {
return ReservedListDualDatabaseDao.getLatestRevision(listName).orElse(null);
return ReservedListDao.getLatestRevision(listName).orElse(null);
}
});
@@ -20,16 +20,12 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.flogger.FluentLogger;
import java.util.Optional;
/**
* A {@link ReservedList} DAO for Cloud SQL.
*
* <p>TODO(b/177567432): Rename this class to ReservedListDao after migrating to Cloud SQL.
*/
public class ReservedListSqlDao {
/** A {@link ReservedList} DAO for Cloud SQL. */
public class ReservedListDao {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private ReservedListSqlDao() {}
private ReservedListDao() {}
/** Persist a new reserved list to Cloud SQL. */
public static void save(ReservedList reservedList) {
@@ -1,50 +0,0 @@
// Copyright 2021 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.model.registry.label;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import com.googlecode.objectify.Key;
import google.registry.persistence.VKey;
import java.util.Optional;
/** A {@link ReservedList} DAO for Datastore. */
public class ReservedListDatastoreDao {
private ReservedListDatastoreDao() {}
/** Persist a new reserved list to Datastore. */
public static void save(ReservedList reservedList) {
ofyTm().transact(() -> ofyTm().put(reservedList));
}
/** Delete a reserved list from Datastore. */
public static void delete(ReservedList reservedList) {
ofyTm().transact(() -> ofyTm().delete(reservedList));
}
/**
* Returns the most recent revision of the {@link ReservedList} with the specified name, if it
* exists.
*/
public static Optional<ReservedList> getLatestRevision(String reservedListName) {
return ofyTm()
.loadByKeyIfPresent(
VKey.createOfy(
ReservedList.class,
Key.create(getCrossTldKey(), ReservedList.class, reservedListName)));
}
}
@@ -1,126 +0,0 @@
// Copyright 2020 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.model.registry.label;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import com.google.common.collect.MapDifference;
import com.google.common.collect.MapDifference.ValueDifference;
import com.google.common.collect.Maps;
import google.registry.model.DatabaseMigrationUtils;
import google.registry.model.registry.label.ReservedList.ReservedListEntry;
import java.util.Map;
import java.util.Optional;
/**
* A {@link ReservedList} DAO that does dual-write and dual-read against Datastore and Cloud SQL.
*
* <p>TODO(b/160993806): Delete this DAO and switch to use the SQL only DAO after migrating to Cloud
* SQL.
*/
public class ReservedListDualDatabaseDao {
private ReservedListDualDatabaseDao() {}
/** Persist a new reserved list to the database. */
public static void save(ReservedList reservedList) {
ReservedListSqlDao.save(reservedList);
DatabaseMigrationUtils.suppressExceptionUnlessInTest(
() -> ReservedListDatastoreDao.save(reservedList),
"Error saving the reserved list to Datastore.");
}
/** Delete a reserved list from both databases. */
public static void delete(ReservedList reservedList) {
ReservedListSqlDao.delete(reservedList);
DatabaseMigrationUtils.suppressExceptionUnlessInTest(
() -> ReservedListDatastoreDao.delete(reservedList),
"Error deleting the reserved list from Datastore.");
}
/**
* Returns the most recent revision of the {@link ReservedList} with the specified name, if it
* exists.
*/
public static Optional<ReservedList> getLatestRevision(String reservedListName) {
Optional<ReservedList> maybePrimaryList =
ReservedListSqlDao.getLatestRevision(reservedListName);
DatabaseMigrationUtils.suppressExceptionUnlessInTest(
() -> maybePrimaryList.ifPresent(primaryList -> loadAndCompare(primaryList)),
"Error comparing reserved lists.");
return maybePrimaryList;
}
private static void loadAndCompare(ReservedList primaryList) {
Optional<ReservedList> maybeSecondaryList =
ReservedListDatastoreDao.getLatestRevision(primaryList.getName());
if (!maybeSecondaryList.isPresent()) {
throw new IllegalStateException("Reserved list in Datastore is empty.");
}
Map<String, ReservedListEntry> labelsToReservations =
primaryList.reservedListMap.entrySet().parallelStream()
.collect(
toImmutableMap(
Map.Entry::getKey,
entry ->
ReservedListEntry.create(
entry.getKey(),
entry.getValue().reservationType,
entry.getValue().comment)));
ReservedList secondaryList = maybeSecondaryList.get();
MapDifference<String, ReservedListEntry> diff =
Maps.difference(labelsToReservations, secondaryList.reservedListMap);
if (!diff.areEqual()) {
if (diff.entriesDiffering().size() > 10) {
throw new IllegalStateException(
String.format(
"Unequal reserved lists detected, Datastore list with revision"
+ " id %d has %d different records than the current"
+ " Cloud SQL list.",
secondaryList.getRevisionId(), diff.entriesDiffering().size()));
}
StringBuilder diffMessage = new StringBuilder("Unequal reserved lists detected:\n");
diff.entriesDiffering().entrySet().stream()
.forEach(
entry -> {
String label = entry.getKey();
ValueDifference<ReservedListEntry> valueDiff = entry.getValue();
diffMessage.append(
String.format(
"Domain label %s has entry %s in Cloud SQL and entry"
+ " %s in the Datastore.\n",
label, valueDiff.leftValue(), valueDiff.rightValue()));
});
diff.entriesOnlyOnLeft().entrySet().stream()
.forEach(
entry -> {
String label = entry.getKey();
diffMessage.append(
String.format(
"Domain label %s has entry in Cloud SQL, but not in Datastore.\n", label));
});
diff.entriesOnlyOnRight().entrySet().stream()
.forEach(
entry -> {
String label = entry.getKey();
diffMessage.append(
String.format(
"Domain label %s has entry in Datastore, but not in Cloud SQL.\n", label));
});
throw new IllegalStateException(diffMessage.toString());
}
}
}
@@ -1,82 +0,0 @@
// Copyright 2021 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.tools;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.beust.jcommander.Parameters;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import google.registry.model.registry.label.ReservedList;
import google.registry.model.registry.label.ReservedList.ReservedListEntry;
import google.registry.model.registry.label.ReservedListDatastoreDao;
import google.registry.model.registry.label.ReservedListSqlDao;
/** Command to compare all ReservedLists in Datastore to all ReservedLists in Cloud SQL. */
@Parameters(
separators = " =",
commandDescription = "Compare all the ReservedLists in Datastore to those in Cloud SQL.")
final class CompareReservedListsCommand implements CommandWithRemoteApi {
@Override
public void run() {
ImmutableSet<String> datastoreLists =
auditedOfy().load().type(ReservedList.class).ancestor(getCrossTldKey()).list().stream()
.map(ReservedList::getName)
.collect(toImmutableSet());
ImmutableSet<String> cloudSqlLists =
jpaTm()
.transact(
() ->
jpaTm().loadAllOf(ReservedList.class).stream()
.map(ReservedList::getName)
.collect(toImmutableSet()));
int listsWithDiffs = 0;
for (String listName : Sets.difference(datastoreLists, cloudSqlLists)) {
listsWithDiffs++;
System.out.printf(
"ReservedList '%s' is present in Datastore, but not in Cloud SQL.%n", listName);
}
for (String listName : Sets.difference(cloudSqlLists, datastoreLists)) {
listsWithDiffs++;
System.out.printf(
"ReservedList '%s' is present in Cloud SQL, but not in Datastore.%n", listName);
}
for (String listName : Sets.intersection(datastoreLists, cloudSqlLists)) {
ImmutableMap<String, ReservedListEntry> namesInSql =
ReservedListSqlDao.getLatestRevision(listName).get().getReservedListEntries();
ImmutableMap<String, ReservedListEntry> namesInDatastore =
ReservedListDatastoreDao.getLatestRevision(listName).get().getReservedListEntries();
// This will only print out the name of the unequal list. GetReservedListCommand should be
// used to determine what the actual differences are.
if (!namesInDatastore.equals(namesInSql)) {
listsWithDiffs++;
System.out.printf("ReservedList '%s' has different entries in each database.%n", listName);
}
}
System.out.printf("Found %d unequal list(s).%n", listsWithDiffs);
}
}
@@ -17,7 +17,7 @@ package google.registry.tools;
import com.beust.jcommander.Parameter;
import com.google.common.flogger.FluentLogger;
import google.registry.model.registry.label.ReservedList;
import google.registry.model.registry.label.ReservedListDualDatabaseDao;
import google.registry.model.registry.label.ReservedListDao;
import google.registry.tools.params.PathParameter;
import java.nio.file.Path;
import javax.annotation.Nullable;
@@ -61,7 +61,7 @@ public abstract class CreateOrUpdateReservedListCommand extends MutatingCommand
name, reservedList.getReservedListEntries().size());
try {
logger.atInfo().log("Saving reserved list for TLD %s", name);
ReservedListDualDatabaseDao.save(reservedList);
ReservedListDao.save(reservedList);
logger.atInfo().log(message);
} catch (Throwable e) {
message = "Unexpected error saving reserved list from nomulus tool command";
@@ -21,7 +21,7 @@ import com.beust.jcommander.Parameters;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import google.registry.model.registry.label.ReservedList;
import google.registry.model.registry.label.ReservedListDualDatabaseDao;
import google.registry.model.registry.label.ReservedListDao;
/**
* Command to delete a {@link ReservedList} from the database. This command will fail if the
@@ -53,7 +53,7 @@ final class DeleteReservedListCommand extends ConfirmingCommand implements Comma
@Override
protected String execute() {
ReservedList existing = ReservedList.get(name).get();
ReservedListDualDatabaseDao.delete(existing);
ReservedListDao.delete(existing);
return String.format("Deleted reserved list: %s", name);
}
}
@@ -18,7 +18,7 @@ import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.appengine.repackaged.com.google.common.collect.Streams;
import google.registry.model.registry.label.ReservedList.ReservedListEntry;
import google.registry.model.registry.label.ReservedListDualDatabaseDao;
import google.registry.model.registry.label.ReservedListDao;
import java.util.Comparator;
import java.util.stream.Collectors;
@@ -34,11 +34,11 @@ public class GetReservedListCommand implements CommandWithRemoteApi {
@Override
public void run() throws Exception {
if (ReservedListDualDatabaseDao.getLatestRevision(reservedListName).isPresent()) {
if (ReservedListDao.getLatestRevision(reservedListName).isPresent()) {
System.out.printf(
"%s\n",
Streams.stream(
ReservedListDualDatabaseDao.getLatestRevision(reservedListName)
ReservedListDao.getLatestRevision(reservedListName)
.get()
.getReservedListEntries()
.values())
@@ -38,7 +38,6 @@ public final class RegistryTool {
.put("canonicalize_labels", CanonicalizeLabelsCommand.class)
.put("check_domain", CheckDomainCommand.class)
.put("check_domain_claims", CheckDomainClaimsCommand.class)
.put("compare_reserved_lists", CompareReservedListsCommand.class)
.put("convert_idn", ConvertIdnCommand.class)
.put("count_domains", CountDomainsCommand.class)
.put("create_anchor_tenant", CreateAnchorTenantCommand.class)
@@ -15,14 +15,13 @@
package google.registry.tools.server;
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.POST;
import com.google.common.collect.ImmutableSet;
import google.registry.model.registry.label.ReservedList;
import google.registry.model.registry.label.ReservedListDualDatabaseDao;
import google.registry.model.registry.label.ReservedListDao;
import google.registry.request.Action;
import google.registry.request.auth.Auth;
import java.util.Comparator;
@@ -48,13 +47,14 @@ public final class ListReservedListsAction extends ListObjectsAction<ReservedLis
@Override
public ImmutableSet<ReservedList> loadObjects() {
return transactIfJpaTm(
() ->
tm().loadAllOf(ReservedList.class).stream()
.map(ReservedList::getName)
.map(ReservedListDualDatabaseDao::getLatestRevision)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toImmutableSortedSet(Comparator.comparing(ReservedList::getName))));
return jpaTm()
.transact(
() ->
jpaTm().loadAllOf(ReservedList.class).stream()
.map(ReservedList::getName)
.map(ReservedListDao::getLatestRevision)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toImmutableSortedSet(Comparator.comparing(ReservedList::getName))));
}
}