diff --git a/core/src/main/java/google/registry/model/DatabaseMigrationUtils.java b/core/src/main/java/google/registry/model/DatabaseMigrationUtils.java deleted file mode 100644 index 0612ee883..000000000 --- a/core/src/main/java/google/registry/model/DatabaseMigrationUtils.java +++ /dev/null @@ -1,55 +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; - -import static google.registry.persistence.transaction.TransactionManagerFactory.tm; - -import com.google.common.flogger.FluentLogger; -import google.registry.config.RegistryEnvironment; -import google.registry.model.common.DatabaseTransitionSchedule; -import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase; -import google.registry.model.common.DatabaseTransitionSchedule.TransitionId; - -/** Utility methods related to migrating dual-read/dual-write entities. */ -public class DatabaseMigrationUtils { - - private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - - /** Throws exceptions only in unit tests, otherwise only logs exceptions. */ - public static void suppressExceptionUnlessInTest(Runnable work, String message) { - try { - work.run(); - } catch (Exception e) { - if (RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)) { - throw e; - } - logger.atWarning().withCause(e).log(message); - } - } - - /** Gets the value for the database currently considered primary. */ - public static PrimaryDatabase getPrimaryDatabase(TransitionId transitionId) { - return DatabaseTransitionSchedule.getCached(transitionId) - .map(DatabaseTransitionSchedule::getPrimaryDatabase) - .orElse(PrimaryDatabase.DATASTORE); - } - - public static boolean isDatastore(TransitionId transitionId) { - return tm().transactNew(() -> DatabaseMigrationUtils.getPrimaryDatabase(transitionId)) - .equals(PrimaryDatabase.DATASTORE); - } - - private DatabaseMigrationUtils() {} -} diff --git a/core/src/main/java/google/registry/model/EntityClasses.java b/core/src/main/java/google/registry/model/EntityClasses.java index 8aa0fc1f7..ed2d08976 100644 --- a/core/src/main/java/google/registry/model/EntityClasses.java +++ b/core/src/main/java/google/registry/model/EntityClasses.java @@ -17,8 +17,7 @@ package google.registry.model; import com.google.common.collect.ImmutableSet; import google.registry.model.billing.BillingEvent; import google.registry.model.common.Cursor; -import google.registry.model.common.DatabaseMigrationStateWrapper; -import google.registry.model.common.DatabaseTransitionSchedule; +import google.registry.model.common.DatabaseMigrationStateSchedule; import google.registry.model.common.EntityGroupRoot; import google.registry.model.common.GaeUserIdConverter; import google.registry.model.contact.ContactHistory; @@ -76,8 +75,7 @@ public final class EntityClasses { ContactHistory.class, ContactResource.class, Cursor.class, - DatabaseMigrationStateWrapper.class, - DatabaseTransitionSchedule.class, + DatabaseMigrationStateSchedule.class, DomainBase.class, DomainHistory.class, EntityGroupRoot.class, diff --git a/core/src/main/java/google/registry/model/common/DatabaseMigrationStateSchedule.java b/core/src/main/java/google/registry/model/common/DatabaseMigrationStateSchedule.java new file mode 100644 index 000000000..0976868db --- /dev/null +++ b/core/src/main/java/google/registry/model/common/DatabaseMigrationStateSchedule.java @@ -0,0 +1,232 @@ +// 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.common; + +import static com.google.common.base.Preconditions.checkArgument; +import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm; +import static google.registry.util.DateTimeUtils.START_OF_TIME; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSortedMap; +import com.googlecode.objectify.annotation.Embed; +import com.googlecode.objectify.annotation.Entity; +import com.googlecode.objectify.annotation.Mapify; +import google.registry.model.annotations.InCrossTld; +import google.registry.model.common.TimedTransitionProperty.TimeMapper; +import google.registry.model.common.TimedTransitionProperty.TimedTransition; +import google.registry.schema.replay.DatastoreOnlyEntity; +import java.time.Duration; +import org.joda.time.DateTime; + +/** + * A wrapper object representing the stage-to-time mapping of the Registry 3.0 Cloud SQL migration. + * + *

The entity is stored in Datastore throughout the entire migration so as to have a single point + * of access. + */ +@Entity +@InCrossTld +public class DatabaseMigrationStateSchedule extends CrossTldSingleton + implements DatastoreOnlyEntity { + + public enum PrimaryDatabase { + CLOUD_SQL, + DATASTORE + } + + /** + * The current phase of the migration plus information about which database to use and whether or + * not the phase is read-only. + */ + public enum MigrationState { + DATASTORE_ONLY(PrimaryDatabase.DATASTORE, false), + DATASTORE_PRIMARY(PrimaryDatabase.DATASTORE, false), + DATASTORE_PRIMARY_READ_ONLY(PrimaryDatabase.DATASTORE, true), + SQL_PRIMARY_READ_ONLY(PrimaryDatabase.CLOUD_SQL, true), + SQL_PRIMARY(PrimaryDatabase.CLOUD_SQL, false), + SQL_ONLY(PrimaryDatabase.CLOUD_SQL, false); + + private final PrimaryDatabase primaryDatabase; + private final boolean isReadOnly; + + public PrimaryDatabase getPrimaryDatabase() { + return primaryDatabase; + } + + public boolean isReadOnly() { + return isReadOnly; + } + + MigrationState(PrimaryDatabase primaryDatabase, boolean isReadOnly) { + this.primaryDatabase = primaryDatabase; + this.isReadOnly = isReadOnly; + } + } + + @Embed + public static class MigrationStateTransition extends TimedTransition { + private MigrationState migrationState; + + @Override + protected MigrationState getValue() { + return migrationState; + } + + @Override + protected void setValue(MigrationState migrationState) { + this.migrationState = migrationState; + } + } + + /** + * Cache of the current migration schedule. The key is meaningless; this is essentially a memoized + * Supplier that can be reset for testing purposes and after writes. + */ + private static final LoadingCache< + Class, + TimedTransitionProperty> + // Each instance should cache the migration schedule for five minutes before reloading + CACHE = + CacheBuilder.newBuilder() + .expireAfterWrite(Duration.ofMinutes(5)) + .build( + new CacheLoader< + Class, + TimedTransitionProperty>() { + @Override + public TimedTransitionProperty load( + Class unused) { + return DatabaseMigrationStateSchedule.getUncached(); + } + }); + + // Restrictions on the state transitions, e.g. no going from DATASTORE_ONLY to SQL_ONLY + private static final ImmutableMultimap VALID_STATE_TRANSITIONS = + createValidStateTransitions(); + + /** + * The valid state transitions. Generally, one can advance the state one step or move backward any + * number of steps, as long as the step we're moving back to has the same primary database as the + * one we're in. Otherwise, we must move to the corresponding READ_ONLY stage first. + */ + private static ImmutableMultimap createValidStateTransitions() { + ImmutableMultimap.Builder builder = + new ImmutableMultimap.Builder() + .put(MigrationState.DATASTORE_ONLY, MigrationState.DATASTORE_PRIMARY) + .putAll( + MigrationState.DATASTORE_PRIMARY, + MigrationState.DATASTORE_ONLY, + MigrationState.DATASTORE_PRIMARY_READ_ONLY) + .putAll( + MigrationState.DATASTORE_PRIMARY_READ_ONLY, + MigrationState.DATASTORE_ONLY, + MigrationState.DATASTORE_PRIMARY, + MigrationState.SQL_PRIMARY_READ_ONLY, + MigrationState.SQL_PRIMARY) + .putAll( + MigrationState.SQL_PRIMARY_READ_ONLY, + MigrationState.DATASTORE_PRIMARY_READ_ONLY, + MigrationState.SQL_PRIMARY) + .putAll( + MigrationState.SQL_PRIMARY, + MigrationState.SQL_PRIMARY_READ_ONLY, + MigrationState.SQL_ONLY) + .putAll( + MigrationState.SQL_ONLY, + MigrationState.SQL_PRIMARY_READ_ONLY, + MigrationState.SQL_PRIMARY); + // In addition, we can always transition from a state to itself (useful when updating the map). + for (MigrationState migrationState : MigrationState.values()) { + builder.put(migrationState, migrationState); + } + return builder.build(); + } + + // Default map to return if we have never saved any -- only use Datastore. + @VisibleForTesting + public static final TimedTransitionProperty + DEFAULT_TRANSITION_MAP = + TimedTransitionProperty.fromValueMap( + ImmutableSortedMap.of(START_OF_TIME, MigrationState.DATASTORE_ONLY), + MigrationStateTransition.class); + + @Mapify(TimeMapper.class) + TimedTransitionProperty migrationTransitions = + TimedTransitionProperty.forMapify( + MigrationState.DATASTORE_ONLY, MigrationStateTransition.class); + + // Required for Objectify initialization + private DatabaseMigrationStateSchedule() {} + + @VisibleForTesting + DatabaseMigrationStateSchedule( + TimedTransitionProperty migrationTransitions) { + this.migrationTransitions = migrationTransitions; + } + + /** Sets and persists to Datastore the provided migration transition schedule. */ + public static void set(ImmutableSortedMap migrationTransitionMap) { + ofyTm().assertInTransaction(); + TimedTransitionProperty transitions = + TimedTransitionProperty.make( + migrationTransitionMap, + MigrationStateTransition.class, + VALID_STATE_TRANSITIONS, + "validStateTransitions", + MigrationState.DATASTORE_ONLY, + "migrationTransitionMap must start with DATASTORE_ONLY"); + validateTransitionAtCurrentTime(transitions); + ofyTm().put(new DatabaseMigrationStateSchedule(transitions)); + CACHE.invalidateAll(); + } + + /** Loads the currently-set migration schedule from the cache, or the default if none exists. */ + public static TimedTransitionProperty get() { + return CACHE.getUnchecked(DatabaseMigrationStateSchedule.class); + } + + /** Loads the currently-set migration schedule from Datastore, or the default if none exists. */ + @VisibleForTesting + static TimedTransitionProperty getUncached() { + return ofyTm() + .transact( + () -> + ofyTm() + .loadSingleton(DatabaseMigrationStateSchedule.class) + .map(s -> s.migrationTransitions) + .orElse(DEFAULT_TRANSITION_MAP)); + } + + /** + * A provided map of transitions may be valid by itself (i.e. it shifts states properly, doesn't + * skip states, and doesn't backtrack incorrectly) while still being invalid. In addition to the + * transitions in the map being valid, the single transition from the current map at the current + * time to the new map at the current time time must also be valid. + */ + private static void validateTransitionAtCurrentTime( + TimedTransitionProperty newTransitions) { + MigrationState currentValue = getUncached().getValueAtTime(ofyTm().getTransactionTime()); + MigrationState nextCurrentValue = newTransitions.getValueAtTime(ofyTm().getTransactionTime()); + checkArgument( + VALID_STATE_TRANSITIONS.get(currentValue).contains(nextCurrentValue), + "Cannot transition from current state-as-of-now %s to new state-as-of-now %s", + currentValue, + nextCurrentValue); + } +} diff --git a/core/src/main/java/google/registry/model/common/DatabaseMigrationStateWrapper.java b/core/src/main/java/google/registry/model/common/DatabaseMigrationStateWrapper.java deleted file mode 100644 index 1063651c9..000000000 --- a/core/src/main/java/google/registry/model/common/DatabaseMigrationStateWrapper.java +++ /dev/null @@ -1,122 +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.common; - -import static com.google.common.base.Preconditions.checkArgument; -import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.googlecode.objectify.annotation.Entity; -import google.registry.model.annotations.InCrossTld; -import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase; -import google.registry.schema.replay.DatastoreOnlyEntity; - -/** - * A wrapper object representing the current stage of the Registry 3.0 Cloud SQL migration. - * - *

The entity is stored in Datastore throughout the entire migration so as to have a single point - * of access (avoiding a two-phase commit problem). - */ -@Entity -@InCrossTld -public class DatabaseMigrationStateWrapper extends CrossTldSingleton - implements DatastoreOnlyEntity { - - /** - * The current phase of the migration plus information about which database to use and whether or - * not the phase is read-only. - */ - public enum MigrationState { - DATASTORE_ONLY(PrimaryDatabase.DATASTORE, false), - DATASTORE_PRIMARY(PrimaryDatabase.DATASTORE, false), - DATASTORE_PRIMARY_READ_ONLY(PrimaryDatabase.DATASTORE, true), - SQL_PRIMARY(PrimaryDatabase.CLOUD_SQL, false), - SQL_ONLY(PrimaryDatabase.CLOUD_SQL, false); - - private final PrimaryDatabase primaryDatabase; - private final boolean readOnly; - - public PrimaryDatabase getPrimaryDatabase() { - return primaryDatabase; - } - - public boolean isReadOnly() { - return readOnly; - } - - MigrationState(PrimaryDatabase primaryDatabase, boolean readOnly) { - this.primaryDatabase = primaryDatabase; - this.readOnly = readOnly; - } - } - - private MigrationState migrationState; - - // Required for Objectify initialization - private DatabaseMigrationStateWrapper() {} - - DatabaseMigrationStateWrapper(MigrationState migrationState) { - this.migrationState = migrationState; - } - - // The valid state transitions. Basically, at state N, state N+1 is valid as well as all previous - // states, with one type of exception: when in either of the SQL states, we can only move back - // one step so that we can make sure that any modifications have been replayed back to Datastore. - private static final ImmutableMap> - VALID_STATE_TRANSITIONS = - ImmutableMap.of( - MigrationState.DATASTORE_ONLY, - ImmutableSet.of(MigrationState.DATASTORE_PRIMARY), - MigrationState.DATASTORE_PRIMARY, - ImmutableSet.of( - MigrationState.DATASTORE_ONLY, MigrationState.DATASTORE_PRIMARY_READ_ONLY), - MigrationState.DATASTORE_PRIMARY_READ_ONLY, - ImmutableSet.of( - MigrationState.DATASTORE_ONLY, - MigrationState.DATASTORE_PRIMARY, - MigrationState.SQL_PRIMARY), - MigrationState.SQL_PRIMARY, - ImmutableSet.of(MigrationState.DATASTORE_PRIMARY_READ_ONLY, MigrationState.SQL_ONLY), - MigrationState.SQL_ONLY, - ImmutableSet.of(MigrationState.SQL_PRIMARY)); - - private static boolean isValidStateTransition(MigrationState from, MigrationState to) { - return VALID_STATE_TRANSITIONS.get(from).contains(to); - } - - /** Sets and persists to Datastore the current database migration state. */ - public static void set(MigrationState newState) { - MigrationState currentState = get(); - checkArgument( - isValidStateTransition(currentState, newState), - "Moving from migration state %s to %s is not a valid transition", - currentState, - newState); - DatabaseMigrationStateWrapper wrapper = new DatabaseMigrationStateWrapper(newState); - ofyTm().transact(() -> ofyTm().put(wrapper)); - } - - /** Retrieves the current state of the migration (or DATASTORE_ONLY if it hasn't started). */ - public static MigrationState get() { - return ofyTm() - .transact( - () -> - ofyTm() - .loadSingleton(DatabaseMigrationStateWrapper.class) - .map(s -> s.migrationState) - .orElse(MigrationState.DATASTORE_ONLY)); - } -} diff --git a/core/src/main/java/google/registry/model/common/DatabaseTransitionSchedule.java b/core/src/main/java/google/registry/model/common/DatabaseTransitionSchedule.java deleted file mode 100644 index 199840b64..000000000 --- a/core/src/main/java/google/registry/model/common/DatabaseTransitionSchedule.java +++ /dev/null @@ -1,166 +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.common; - -import static com.google.common.base.Preconditions.checkNotNull; -import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration; -import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; -import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm; -import static google.registry.persistence.transaction.TransactionManagerFactory.tm; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableSortedMap; -import com.googlecode.objectify.Key; -import com.googlecode.objectify.annotation.Embed; -import com.googlecode.objectify.annotation.Entity; -import com.googlecode.objectify.annotation.Id; -import com.googlecode.objectify.annotation.Mapify; -import com.googlecode.objectify.annotation.Parent; -import google.registry.model.ImmutableObject; -import google.registry.model.UpdateAutoTimestamp; -import google.registry.model.annotations.InCrossTld; -import google.registry.model.common.TimedTransitionProperty.TimeMapper; -import google.registry.model.common.TimedTransitionProperty.TimedTransition; -import google.registry.model.registry.label.PremiumList; -import google.registry.model.registry.label.ReservedList; -import google.registry.model.smd.SignedMarkRevocationList; -import google.registry.model.tmch.ClaimsList; -import google.registry.persistence.VKey; -import google.registry.schema.replay.DatastoreOnlyEntity; -import java.util.Optional; -import javax.annotation.concurrent.Immutable; -import org.joda.time.DateTime; - -@Entity -@Immutable -@InCrossTld -public class DatabaseTransitionSchedule extends ImmutableObject implements DatastoreOnlyEntity { - - /** - * The name of the database to be treated as the primary database. The first entry in the schedule - * will always be Datastore. - */ - public enum PrimaryDatabase { - CLOUD_SQL, - DATASTORE - } - - /** The id of the transition schedule. */ - public enum TransitionId { - /** The schedule for migration of {@link ClaimsList} entities. */ - CLAIMS_LIST, - /** The schedule for the migration of {@link PremiumList} and {@link ReservedList}. */ - DOMAIN_LABEL_LISTS, - /** The schedule for the migration of the {@link SignedMarkRevocationList} entity. */ - SIGNED_MARK_REVOCATION_LIST, - /** The schedule for all asynchronously-replayed entities, ones not dually-written. */ - REPLAYED_ENTITIES, - } - - /** - * The transition to a specified primary database at a specific point in time, for use in a - * TimedTransitionProperty. - */ - @Embed - public static class PrimaryDatabaseTransition extends TimedTransition { - private PrimaryDatabase primaryDatabase; - - @Override - protected PrimaryDatabase getValue() { - return primaryDatabase; - } - - @Override - protected void setValue(PrimaryDatabase primaryDatabase) { - this.primaryDatabase = primaryDatabase; - } - } - - @Parent Key parent = getCrossTldKey(); - - @Id String transitionId; - - /** An automatically managed timestamp of when this schedule was last written to Datastore. */ - UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null); - - /** A property that tracks the primary database for a dual-read/dual-write database migration. */ - @Mapify(TimeMapper.class) - TimedTransitionProperty databaseTransitions = - TimedTransitionProperty.forMapify(PrimaryDatabase.DATASTORE, PrimaryDatabaseTransition.class); - - /** A cache that loads the {@link DatabaseTransitionSchedule} for a given id. */ - private static final LoadingCache> CACHE = - CacheBuilder.newBuilder() - .expireAfterWrite( - java.time.Duration.ofMillis(getSingletonCacheRefreshDuration().getMillis())) - .build( - new CacheLoader>() { - @Override - public Optional load(TransitionId transitionId) { - return DatabaseTransitionSchedule.get(transitionId); - } - }); - - public static DatabaseTransitionSchedule create( - TransitionId transitionId, - TimedTransitionProperty databaseTransitions) { - checkNotNull(transitionId, "Id cannot be null"); - checkNotNull(databaseTransitions, "databaseTransitions cannot be null"); - databaseTransitions.checkValidity(); - DatabaseTransitionSchedule instance = new DatabaseTransitionSchedule(); - instance.transitionId = transitionId.name(); - instance.databaseTransitions = databaseTransitions; - return instance; - } - - /** Returns the database that is indicated as primary at the given time. */ - public PrimaryDatabase getPrimaryDatabase() { - return databaseTransitions.getValueAtTime(tm().getTransactionTime()); - } - - /** Returns the database transitions as a map of start time to primary database. */ - public ImmutableSortedMap getDatabaseTransitions() { - return databaseTransitions.toValueMap(); - } - - /** - * Returns the current cached schedule for the given id. - * - *

WARNING: The schedule returned by this method could be up to 10 minutes out of date. - */ - public static Optional getCached(TransitionId id) { - return CACHE.getUnchecked(id); - } - - /** Returns the schedule for a given id. */ - public static Optional get(TransitionId transitionId) { - VKey key = - VKey.create( - DatabaseTransitionSchedule.class, - transitionId, - Key.create(getCrossTldKey(), DatabaseTransitionSchedule.class, transitionId.name())); - - return ofyTm().transact(() -> ofyTm().loadByKeyIfPresent(key)); - } - - @Override - public String toString() { - return String.format( - "%s(last updated at %s): %s", - transitionId, lastUpdateTime.getTimestamp(), databaseTransitions.toValueMap()); - } -} diff --git a/core/src/main/java/google/registry/rdap/RdapActionBase.java b/core/src/main/java/google/registry/rdap/RdapActionBase.java index 8f24822a1..4ea60e07b 100644 --- a/core/src/main/java/google/registry/rdap/RdapActionBase.java +++ b/core/src/main/java/google/registry/rdap/RdapActionBase.java @@ -17,7 +17,6 @@ package google.registry.rdap; import static com.google.common.base.Charsets.UTF_8; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN; -import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.request.Actions.getPathForAction; import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; @@ -29,10 +28,7 @@ import com.google.common.net.MediaType; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import google.registry.config.RegistryConfig.Config; -import google.registry.model.DatabaseMigrationUtils; import google.registry.model.EppResource; -import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase; -import google.registry.model.common.DatabaseTransitionSchedule.TransitionId; import google.registry.model.registrar.Registrar; import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.rdap.RdapObjectClasses.ErrorResponse; @@ -261,10 +257,4 @@ public abstract class RdapActionBase implements Runnable { return rdapJsonFormatter.getRequestTime(); } - static boolean isDatastore() { - return tm().transact( - () -> - DatabaseMigrationUtils.getPrimaryDatabase(TransitionId.REPLAYED_ENTITIES) - .equals(PrimaryDatabase.DATASTORE)); - } } diff --git a/core/src/main/java/google/registry/rdap/RdapDomainSearchAction.java b/core/src/main/java/google/registry/rdap/RdapDomainSearchAction.java index 46013dcd2..ed64d50e6 100644 --- a/core/src/main/java/google/registry/rdap/RdapDomainSearchAction.java +++ b/core/src/main/java/google/registry/rdap/RdapDomainSearchAction.java @@ -19,6 +19,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey; import static google.registry.model.ofy.ObjectifyService.auditedOfy; import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; +import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.request.Action.Method.GET; import static google.registry.request.Action.Method.HEAD; import static google.registry.util.DateTimeUtils.END_OF_TIME; @@ -204,7 +205,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase { // need it. int querySizeLimit = RESULT_SET_SIZE_SCALING_FACTOR * rdapResultSetMaxSize; RdapResultSet resultSet; - if (isDatastore()) { + if (tm().isOfy()) { Query query = auditedOfy() .load() @@ -260,7 +261,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase { // Don't use queryItems, because it doesn't handle pending deletes. int querySizeLimit = RESULT_SET_SIZE_SCALING_FACTOR * rdapResultSetMaxSize; RdapResultSet resultSet; - if (isDatastore()) { + if (tm().isOfy()) { Query query = auditedOfy().load().type(DomainBase.class).filter("tld", tld); if (cursorString.isPresent()) { query = query.filter("fullyQualifiedDomainName >", cursorString.get()); @@ -337,7 +338,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase { // incomplete result set if a search asks for something like "ns*", but we need to enforce a // limit in order to avoid arbitrarily long-running queries. Optional desiredRegistrar = getDesiredRegistrar(); - if (isDatastore()) { + if (tm().isOfy()) { Query query = queryItems( HostResource.class, @@ -472,7 +473,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase { private DomainSearchResponse searchByNameserverIp(final InetAddress inetAddress) { Optional desiredRegistrar = getDesiredRegistrar(); ImmutableSet> hostKeys; - if (isDatastore()) { + if (tm().isOfy()) { Query query = queryItems( HostResource.class, @@ -544,7 +545,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase { int numHostKeysSearched = 0; for (List> chunk : Iterables.partition(hostKeys, 30)) { numHostKeysSearched += chunk.size(); - if (isDatastore()) { + if (tm().isOfy()) { Query query = auditedOfy() .load() diff --git a/core/src/main/java/google/registry/rdap/RdapEntitySearchAction.java b/core/src/main/java/google/registry/rdap/RdapEntitySearchAction.java index 93419aef7..9aacb2821 100644 --- a/core/src/main/java/google/registry/rdap/RdapEntitySearchAction.java +++ b/core/src/main/java/google/registry/rdap/RdapEntitySearchAction.java @@ -262,7 +262,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase { || (cursorType == CursorType.REGISTRAR)) { resultSet = RdapResultSet.create(ImmutableList.of()); } else { - if (isDatastore()) { + if (tm().isOfy()) { Query query = queryItems( ContactResource.class, @@ -386,7 +386,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase { if (subtype == Subtype.REGISTRARS) { contactResultSet = RdapResultSet.create(ImmutableList.of()); } else { - if (isDatastore()) { + if (tm().isOfy()) { contactResultSet = getMatchingResources( queryItemsByKey( diff --git a/core/src/main/java/google/registry/rdap/RdapNameserverSearchAction.java b/core/src/main/java/google/registry/rdap/RdapNameserverSearchAction.java index 58b3c8c19..7972e7c6d 100644 --- a/core/src/main/java/google/registry/rdap/RdapNameserverSearchAction.java +++ b/core/src/main/java/google/registry/rdap/RdapNameserverSearchAction.java @@ -16,6 +16,7 @@ package google.registry.rdap; import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; +import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.request.Action.Method.GET; import static google.registry.request.Action.Method.HEAD; import static google.registry.util.DateTimeUtils.END_OF_TIME; @@ -220,7 +221,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase { private NameserverSearchResponse searchByNameUsingPrefix(RdapSearchPattern partialStringQuery) { // Add 1 so we can detect truncation. int querySizeLimit = getStandardQuerySizeLimit(); - if (isDatastore()) { + if (tm().isOfy()) { Query query = queryItems( HostResource.class, @@ -254,7 +255,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase { // Add 1 so we can detect truncation. int querySizeLimit = getStandardQuerySizeLimit(); RdapResultSet rdapResultSet; - if (isDatastore()) { + if (tm().isOfy()) { Query query = queryItems( HostResource.class, diff --git a/core/src/main/java/google/registry/tools/GetDatabaseTransitionScheduleCommand.java b/core/src/main/java/google/registry/tools/GetDatabaseTransitionScheduleCommand.java deleted file mode 100644 index 53822df0b..000000000 --- a/core/src/main/java/google/registry/tools/GetDatabaseTransitionScheduleCommand.java +++ /dev/null @@ -1,44 +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 google.registry.util.PreconditionsUtils.checkArgumentPresent; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import google.registry.model.common.DatabaseTransitionSchedule; -import google.registry.model.common.DatabaseTransitionSchedule.TransitionId; -import java.util.List; - -/** Command to show the {@link DatabaseTransitionSchedule} for a transition id. */ -@Parameters(separators = " =", commandDescription = "Show database transition schedule") -final class GetDatabaseTransitionScheduleCommand implements CommandWithRemoteApi { - - @Parameter(description = "Transition id(s) for the schedules to get", required = true) - private List mainParameters; - - @Override - public void run() { - for (TransitionId transitionId : mainParameters) { - DatabaseTransitionSchedule schedule = - checkArgumentPresent( - DatabaseTransitionSchedule.get(transitionId), - "A database transition schedule for %s does not exist", - transitionId); - - System.out.println(schedule); - } - } -} diff --git a/core/src/main/java/google/registry/tools/RegistryTool.java b/core/src/main/java/google/registry/tools/RegistryTool.java index 591bdb20e..2e2d596de 100644 --- a/core/src/main/java/google/registry/tools/RegistryTool.java +++ b/core/src/main/java/google/registry/tools/RegistryTool.java @@ -69,7 +69,6 @@ public final class RegistryTool { .put("get_allocation_token", GetAllocationTokenCommand.class) .put("get_claims_list", GetClaimsListCommand.class) .put("get_contact", GetContactCommand.class) - .put("get_database_transition_schedule", GetDatabaseTransitionScheduleCommand.class) .put("get_domain", GetDomainCommand.class) .put("get_history_entries", GetHistoryEntriesCommand.class) .put("get_host", GetHostCommand.class) @@ -111,7 +110,6 @@ public final class RegistryTool { .put("resave_epp_resource", ResaveEppResourceCommand.class) .put("save_sql_credential", SaveSqlCredentialCommand.class) .put("send_escrow_report_to_icann", SendEscrowReportToIcannCommand.class) - .put("set_database_transition_schedule", SetDatabaseTransitionScheduleCommand.class) .put("set_num_instances", SetNumInstancesCommand.class) .put("set_sql_replay_checkpoint", SetSqlReplayCheckpointCommand.class) .put("setup_ote", SetupOteCommand.class) diff --git a/core/src/main/java/google/registry/tools/SetDatabaseTransitionScheduleCommand.java b/core/src/main/java/google/registry/tools/SetDatabaseTransitionScheduleCommand.java deleted file mode 100644 index 6e5f13e43..000000000 --- a/core/src/main/java/google/registry/tools/SetDatabaseTransitionScheduleCommand.java +++ /dev/null @@ -1,69 +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 google.registry.persistence.transaction.TransactionManagerFactory.ofyTm; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.google.common.collect.ImmutableSortedMap; -import google.registry.model.common.DatabaseTransitionSchedule; -import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase; -import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabaseTransition; -import google.registry.model.common.DatabaseTransitionSchedule.TransitionId; -import google.registry.model.common.TimedTransitionProperty; -import google.registry.tools.params.TransitionListParameter.PrimaryDatabaseTransitions; -import org.joda.time.DateTime; - -/** Command to update {@link DatabaseTransitionSchedule}. */ -@Parameters( - separators = " =", - commandDescription = "Set the database transition schedule for transition id.") -public class SetDatabaseTransitionScheduleCommand extends ConfirmingCommand - implements CommandWithRemoteApi { - - @Parameter( - names = "--transition_schedule", - converter = PrimaryDatabaseTransitions.class, - validateWith = PrimaryDatabaseTransitions.class, - description = - "Comma-delimited list of database transitions, of the form" - + "