diff --git a/core/src/main/java/google/registry/model/common/DatabaseMigrationStateSchedule.java b/core/src/main/java/google/registry/model/common/DatabaseMigrationStateSchedule.java
deleted file mode 100644
index 4f1375ca8..000000000
--- a/core/src/main/java/google/registry/model/common/DatabaseMigrationStateSchedule.java
+++ /dev/null
@@ -1,275 +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.tm;
-import static google.registry.util.DateTimeUtils.START_OF_TIME;
-
-import com.github.benmanes.caffeine.cache.LoadingCache;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableSortedMap;
-import com.google.common.flogger.FluentLogger;
-import google.registry.config.RegistryEnvironment;
-import google.registry.model.CacheUtils;
-import google.registry.model.annotations.DeleteAfterMigration;
-import java.time.Duration;
-import java.util.Arrays;
-import javax.persistence.Entity;
-import javax.persistence.PersistenceException;
-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 SQL throughout the entire migration so as to have a single point of
- * access.
- */
-@DeleteAfterMigration
-@Entity
-public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
-
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private static boolean useUncachedForTest = false;
-
- public enum PrimaryDatabase {
- CLOUD_SQL,
- DATASTORE
- }
-
- public enum ReplayDirection {
- NO_REPLAY,
- DATASTORE_TO_SQL,
- SQL_TO_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 is the only DB being used. */
- DATASTORE_ONLY(PrimaryDatabase.DATASTORE, false, ReplayDirection.NO_REPLAY),
-
- /** Datastore is the primary DB, with changes replicated to Cloud SQL. */
- DATASTORE_PRIMARY(PrimaryDatabase.DATASTORE, false, ReplayDirection.DATASTORE_TO_SQL),
-
- /** Datastore is the primary DB, with replication, and async actions are disallowed. */
- DATASTORE_PRIMARY_NO_ASYNC(PrimaryDatabase.DATASTORE, false, ReplayDirection.DATASTORE_TO_SQL),
-
- /** Datastore is the primary DB, with replication, and all mutating actions are disallowed. */
- DATASTORE_PRIMARY_READ_ONLY(PrimaryDatabase.DATASTORE, true, ReplayDirection.DATASTORE_TO_SQL),
-
- /**
- * Cloud SQL is the primary DB, with replication back to Datastore, and all mutating actions are
- * disallowed.
- */
- SQL_PRIMARY_READ_ONLY(PrimaryDatabase.CLOUD_SQL, true, ReplayDirection.SQL_TO_DATASTORE),
-
- /** Cloud SQL is the primary DB, with changes replicated to Datastore. */
- SQL_PRIMARY(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.SQL_TO_DATASTORE),
-
- /** Cloud SQL is the only DB being used. */
- SQL_ONLY(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY),
-
- /** Toggles SQL Sequence based allocateId */
- SEQUENCE_BASED_ALLOCATE_ID(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY),
-
- /** Use SQL-based Nordn upload flow instead of the pull queue-based one. */
- NORDN_SQL(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY),
-
- /** Use SQL-based DNS update flow instead of the pull queue-based one. */
- DNS_SQL(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY);
-
- private final PrimaryDatabase primaryDatabase;
- private final boolean isReadOnly;
- private final ReplayDirection replayDirection;
-
- public PrimaryDatabase getPrimaryDatabase() {
- return primaryDatabase;
- }
-
- public boolean isReadOnly() {
- return isReadOnly;
- }
-
- public ReplayDirection getReplayDirection() {
- return replayDirection;
- }
-
- MigrationState(
- PrimaryDatabase primaryDatabase, boolean isReadOnly, ReplayDirection replayDirection) {
- this.primaryDatabase = primaryDatabase;
- this.isReadOnly = isReadOnly;
- this.replayDirection = replayDirection;
- }
- }
-
- /**
- * 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.
- */
- @VisibleForTesting
- public static final LoadingCache<
- Class, TimedTransitionProperty>
- // Each instance should cache the migration schedule for five minutes before reloading
- CACHE =
- CacheUtils.newCacheBuilder(Duration.ofMinutes(5))
- .build(singletonClazz -> 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_NO_ASYNC)
- .putAll(
- MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
- MigrationState.DATASTORE_ONLY,
- MigrationState.DATASTORE_PRIMARY,
- MigrationState.DATASTORE_PRIMARY_READ_ONLY)
- .putAll(
- MigrationState.DATASTORE_PRIMARY_READ_ONLY,
- MigrationState.DATASTORE_ONLY,
- MigrationState.DATASTORE_PRIMARY,
- MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
- 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)
- .putAll(MigrationState.SQL_ONLY, MigrationState.SEQUENCE_BASED_ALLOCATE_ID)
- .putAll(MigrationState.SEQUENCE_BASED_ALLOCATE_ID, MigrationState.NORDN_SQL)
- .putAll(
- MigrationState.NORDN_SQL,
- MigrationState.SEQUENCE_BASED_ALLOCATE_ID,
- MigrationState.DNS_SQL)
- .putAll(MigrationState.DNS_SQL, MigrationState.NORDN_SQL);
-
- // In addition, we can always transition from a state to itself (useful when updating the map).
- Arrays.stream(MigrationState.values()).forEach(state -> builder.put(state, state));
- 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));
-
- @VisibleForTesting
- public TimedTransitionProperty migrationTransitions =
- TimedTransitionProperty.withInitialValue(MigrationState.DATASTORE_ONLY);
-
- // Required for Hibernate initialization
- protected DatabaseMigrationStateSchedule() {}
-
- @VisibleForTesting
- public DatabaseMigrationStateSchedule(
- TimedTransitionProperty migrationTransitions) {
- this.migrationTransitions = migrationTransitions;
- }
-
- /** Sets and persists to SQL the provided migration transition schedule. */
- public static void set(ImmutableSortedMap migrationTransitionMap) {
- tm().assertInTransaction();
- TimedTransitionProperty transitions =
- TimedTransitionProperty.make(
- migrationTransitionMap,
- VALID_STATE_TRANSITIONS,
- "validStateTransitions",
- MigrationState.DATASTORE_ONLY,
- "migrationTransitionMap must start with DATASTORE_ONLY");
- validateTransitionAtCurrentTime(transitions);
- tm().put(new DatabaseMigrationStateSchedule(transitions));
- CACHE.invalidateAll();
- }
-
- @VisibleForTesting
- public static void useUncachedForTest() {
- useUncachedForTest = true;
- }
-
- /** Loads the currently-set migration schedule from the cache, or the default if none exists. */
- public static TimedTransitionProperty get() {
- return CACHE.get(DatabaseMigrationStateSchedule.class);
- }
-
- /** Returns the database migration status at the given time. */
- public static MigrationState getValueAtTime(DateTime dateTime) {
- return useUncachedForTest
- ? getUncached().getValueAtTime(dateTime)
- : get().getValueAtTime(dateTime);
- }
-
- /** Loads the currently-set migration schedule from SQL, or the default if none exists. */
- @VisibleForTesting
- static TimedTransitionProperty getUncached() {
- return tm().transact(
- () -> {
- try {
- return tm().loadSingleton(DatabaseMigrationStateSchedule.class)
- .map(s -> s.migrationTransitions)
- .orElse(DEFAULT_TRANSITION_MAP);
- } catch (PersistenceException e) {
- if (!RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)) {
- throw e;
- }
- logger.atWarning().withCause(e).log(
- "Error when retrieving migration schedule; this should only happen in tests.");
- return 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 must also be valid.
- */
- private static void validateTransitionAtCurrentTime(
- TimedTransitionProperty newTransitions) {
- MigrationState currentValue = getUncached().getValueAtTime(tm().getTransactionTime());
- MigrationState nextCurrentValue = newTransitions.getValueAtTime(tm().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/persistence/converter/DatabaseMigrationScheduleTransitionConverter.java b/core/src/main/java/google/registry/persistence/converter/DatabaseMigrationScheduleTransitionConverter.java
deleted file mode 100644
index 04722089c..000000000
--- a/core/src/main/java/google/registry/persistence/converter/DatabaseMigrationScheduleTransitionConverter.java
+++ /dev/null
@@ -1,37 +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.persistence.converter;
-
-import google.registry.model.annotations.DeleteAfterMigration;
-import google.registry.model.common.DatabaseMigrationStateSchedule;
-import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
-import javax.persistence.Converter;
-
-/** JPA converter for {@link DatabaseMigrationStateSchedule} transitions. */
-@DeleteAfterMigration
-@Converter(autoApply = true)
-public class DatabaseMigrationScheduleTransitionConverter
- extends TimedTransitionPropertyConverterBase {
-
- @Override
- protected String convertValueToString(MigrationState value) {
- return value.name();
- }
-
- @Override
- protected MigrationState convertStringToValue(String string) {
- return MigrationState.valueOf(string);
- }
-}
diff --git a/core/src/main/java/google/registry/tools/GetDatabaseMigrationStateCommand.java b/core/src/main/java/google/registry/tools/GetDatabaseMigrationStateCommand.java
deleted file mode 100644
index 16cc2c6cc..000000000
--- a/core/src/main/java/google/registry/tools/GetDatabaseMigrationStateCommand.java
+++ /dev/null
@@ -1,34 +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 com.beust.jcommander.Parameters;
-import google.registry.model.annotations.DeleteAfterMigration;
-import google.registry.model.common.DatabaseMigrationStateSchedule;
-import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
-import google.registry.model.common.TimedTransitionProperty;
-
-/** A command to check the current Registry 3.0 migration state of the database. */
-@DeleteAfterMigration
-@Parameters(separators = " =", commandDescription = "Check current Registry 3.0 migration state")
-public class GetDatabaseMigrationStateCommand implements Command {
-
- @Override
- public void run() throws Exception {
- TimedTransitionProperty migrationSchedule =
- DatabaseMigrationStateSchedule.get();
- System.out.printf("Current migration schedule: %s%n", migrationSchedule.toValueMap());
- }
-}
diff --git a/core/src/main/java/google/registry/tools/RegistryTool.java b/core/src/main/java/google/registry/tools/RegistryTool.java
index 214e81e20..3bd783da2 100644
--- a/core/src/main/java/google/registry/tools/RegistryTool.java
+++ b/core/src/main/java/google/registry/tools/RegistryTool.java
@@ -68,7 +68,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_migration_state", GetDatabaseMigrationStateCommand.class)
.put("get_domain", GetDomainCommand.class)
.put("get_history_entries", GetHistoryEntriesCommand.class)
.put("get_host", GetHostCommand.class)
@@ -98,7 +97,6 @@ public final class RegistryTool {
.put("renew_domain", RenewDomainCommand.class)
.put("save_sql_credential", SaveSqlCredentialCommand.class)
.put("send_escrow_report_to_icann", SendEscrowReportToIcannCommand.class)
- .put("set_database_migration_state", SetDatabaseMigrationStateCommand.class)
.put("setup_ote", SetupOteCommand.class)
.put("uniform_rapid_suspension", UniformRapidSuspensionCommand.class)
.put("unlock_domain", UnlockDomainCommand.class)
diff --git a/core/src/main/java/google/registry/tools/SetDatabaseMigrationStateCommand.java b/core/src/main/java/google/registry/tools/SetDatabaseMigrationStateCommand.java
deleted file mode 100644
index e50a9e8a4..000000000
--- a/core/src/main/java/google/registry/tools/SetDatabaseMigrationStateCommand.java
+++ /dev/null
@@ -1,70 +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.tm;
-
-import com.beust.jcommander.Parameter;
-import com.beust.jcommander.Parameters;
-import com.google.common.collect.ImmutableSortedMap;
-import google.registry.model.annotations.DeleteAfterMigration;
-import google.registry.model.common.DatabaseMigrationStateSchedule;
-import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
-import google.registry.tools.params.TransitionListParameter.MigrationStateTransitions;
-import org.joda.time.DateTime;
-
-/** Command to set the Registry 3.0 database migration state schedule. */
-@DeleteAfterMigration
-@Parameters(
- separators = " =",
- commandDescription = "Set the current database migration state schedule.")
-public class SetDatabaseMigrationStateCommand extends ConfirmingCommand {
-
- private static final String WARNING_MESSAGE =
- "Attempting to change the schedule with an effect that would take place within the next 10 "
- + "minutes. The cache expiration duration is 5 minutes so this MAY BE DANGEROUS.\n";
-
- @Parameter(
- names = "--migration_schedule",
- converter = MigrationStateTransitions.class,
- validateWith = MigrationStateTransitions.class,
- required = true,
- description =
- "Comma-delimited list of database transitions, of the form"
- + "