mirror of
https://github.com/google/nomulus.git
synced 2025-07-09 12:43:24 +02:00
Store DatabaseMigrationSchedule in SQL instead of Datastore (#1269)
* Store DatabaseMigrationSchedule in SQL instead of Datastore This requires messing around with some of the JPA unit test rule creation since it requires saving / retrieving the schedule pretty much always (which itself includes the hstore extension).
This commit is contained in:
parent
ab68d7c3b0
commit
fbdd278abd
36 changed files with 239 additions and 122 deletions
Binary file not shown.
|
@ -17,7 +17,6 @@ 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.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.EntityGroupRoot;
|
||||
import google.registry.model.common.GaeUserIdConverter;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
|
@ -72,7 +71,6 @@ public final class EntityClasses {
|
|||
ContactHistory.class,
|
||||
ContactResource.class,
|
||||
Cursor.class,
|
||||
DatabaseMigrationStateSchedule.class,
|
||||
DomainBase.class,
|
||||
DomainHistory.class,
|
||||
EntityGroupRoot.class,
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
package google.registry.model.common;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
@ -26,16 +24,13 @@ 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.Key;
|
||||
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 com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.common.TimedTransitionProperty.TimedTransition;
|
||||
import google.registry.model.replay.DatastoreOnlyEntity;
|
||||
import google.registry.model.replay.SqlOnlyEntity;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.PersistenceException;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
|
@ -45,9 +40,9 @@ import org.joda.time.DateTime;
|
|||
* of access.
|
||||
*/
|
||||
@Entity
|
||||
@InCrossTld
|
||||
public class DatabaseMigrationStateSchedule extends CrossTldSingleton
|
||||
implements DatastoreOnlyEntity {
|
||||
public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements SqlOnlyEntity {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
public enum PrimaryDatabase {
|
||||
CLOUD_SQL,
|
||||
|
@ -96,12 +91,11 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton
|
|||
}
|
||||
}
|
||||
|
||||
@Embed
|
||||
public static class MigrationStateTransition extends TimedTransition<MigrationState> {
|
||||
private MigrationState migrationState;
|
||||
|
||||
@Override
|
||||
protected MigrationState getValue() {
|
||||
public MigrationState getValue() {
|
||||
return migrationState;
|
||||
}
|
||||
|
||||
|
@ -185,7 +179,6 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton
|
|||
MigrationStateTransition.class);
|
||||
|
||||
@VisibleForTesting
|
||||
@Mapify(TimeMapper.class)
|
||||
public TimedTransitionProperty<MigrationState, MigrationStateTransition> migrationTransitions =
|
||||
TimedTransitionProperty.forMapify(
|
||||
MigrationState.DATASTORE_ONLY, MigrationStateTransition.class);
|
||||
|
@ -201,7 +194,7 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton
|
|||
|
||||
/** Sets and persists to Datastore the provided migration transition schedule. */
|
||||
public static void set(ImmutableSortedMap<DateTime, MigrationState> migrationTransitionMap) {
|
||||
ofyTm().assertInTransaction();
|
||||
jpaTm().assertInTransaction();
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> transitions =
|
||||
TimedTransitionProperty.make(
|
||||
migrationTransitionMap,
|
||||
|
@ -211,7 +204,7 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton
|
|||
MigrationState.DATASTORE_ONLY,
|
||||
"migrationTransitionMap must start with DATASTORE_ONLY");
|
||||
validateTransitionAtCurrentTime(transitions);
|
||||
ofyTm().put(new DatabaseMigrationStateSchedule(transitions));
|
||||
jpaTm().put(new DatabaseMigrationStateSchedule(transitions));
|
||||
CACHE.invalidateAll();
|
||||
}
|
||||
|
||||
|
@ -228,20 +221,23 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton
|
|||
/** Loads the currently-set migration schedule from Datastore, or the default if none exists. */
|
||||
@VisibleForTesting
|
||||
static TimedTransitionProperty<MigrationState, MigrationStateTransition> getUncached() {
|
||||
return Optional.ofNullable(
|
||||
auditedOfy()
|
||||
.doTransactionless(
|
||||
() ->
|
||||
auditedOfy()
|
||||
.load()
|
||||
.key(
|
||||
Key.create(
|
||||
getCrossTldKey(),
|
||||
DatabaseMigrationStateSchedule.class,
|
||||
CrossTldSingleton.SINGLETON_ID))
|
||||
.now()))
|
||||
.map(s -> s.migrationTransitions)
|
||||
.orElse(DEFAULT_TRANSITION_MAP);
|
||||
return jpaTm()
|
||||
.transactNew(
|
||||
() -> {
|
||||
try {
|
||||
return jpaTm()
|
||||
.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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -252,8 +248,8 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton
|
|||
*/
|
||||
private static void validateTransitionAtCurrentTime(
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> newTransitions) {
|
||||
MigrationState currentValue = getUncached().getValueAtTime(ofyTm().getTransactionTime());
|
||||
MigrationState nextCurrentValue = newTransitions.getValueAtTime(ofyTm().getTransactionTime());
|
||||
MigrationState currentValue = getUncached().getValueAtTime(jpaTm().getTransactionTime());
|
||||
MigrationState nextCurrentValue = newTransitions.getValueAtTime(jpaTm().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",
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// 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 com.google.common.collect.Maps;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationStateTransition;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Converter;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** JPA converter for {@link DatabaseMigrationStateSchedule} transitions. */
|
||||
@Converter(autoApply = true)
|
||||
public class DatabaseMigrationScheduleTransitionConverter
|
||||
extends TimedTransitionPropertyConverterBase<MigrationState, MigrationStateTransition> {
|
||||
|
||||
@Override
|
||||
Map.Entry<String, String> convertToDatabaseMapEntry(
|
||||
Map.Entry<DateTime, MigrationStateTransition> entry) {
|
||||
return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().name());
|
||||
}
|
||||
|
||||
@Override
|
||||
Map.Entry<DateTime, MigrationState> convertToEntityMapEntry(Map.Entry<String, String> entry) {
|
||||
return Maps.immutableEntry(
|
||||
DateTime.parse(entry.getKey()), MigrationState.valueOf(entry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<MigrationStateTransition> getTimedTransitionSubclass() {
|
||||
return MigrationStateTransition.class;
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
@ -47,11 +47,11 @@ public class SetDatabaseMigrationStateCommand extends ConfirmingCommand
|
|||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
return ofyTm()
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
StringBuilder result = new StringBuilder();
|
||||
DateTime now = ofyTm().getTransactionTime();
|
||||
DateTime now = jpaTm().getTransactionTime();
|
||||
DateTime nextTransition = transitionSchedule.ceilingKey(now);
|
||||
if (nextTransition != null && nextTransition.isBefore(now.plusMinutes(10))) {
|
||||
result.append(WARNING_MESSAGE);
|
||||
|
@ -64,7 +64,7 @@ public class SetDatabaseMigrationStateCommand extends ConfirmingCommand
|
|||
|
||||
@Override
|
||||
protected String execute() {
|
||||
ofyTm().transact(() -> DatabaseMigrationStateSchedule.set(transitionSchedule));
|
||||
jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(transitionSchedule));
|
||||
return String.format("Successfully set new migration state schedule %s", transitionSchedule);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
<class>google.registry.model.billing.BillingEvent$OneTime</class>
|
||||
<class>google.registry.model.billing.BillingEvent$Recurring</class>
|
||||
<class>google.registry.model.common.Cursor</class>
|
||||
<class>google.registry.model.common.DatabaseMigrationStateSchedule</class>
|
||||
<class>google.registry.model.contact.ContactHistory</class>
|
||||
<class>google.registry.model.contact.ContactResource</class>
|
||||
<class>google.registry.model.domain.DomainBase</class>
|
||||
|
@ -85,6 +86,7 @@
|
|||
<class>google.registry.persistence.converter.CreateAutoTimestampConverter</class>
|
||||
<class>google.registry.persistence.converter.CurrencyToBillingConverter</class>
|
||||
<class>google.registry.persistence.converter.CurrencyUnitConverter</class>
|
||||
<class>google.registry.persistence.converter.DatabaseMigrationScheduleTransitionConverter</class>
|
||||
<class>google.registry.persistence.converter.DateTimeConverter</class>
|
||||
<class>google.registry.persistence.converter.DurationConverter</class>
|
||||
<class>google.registry.persistence.converter.InetAddressSetConverter</class>
|
||||
|
|
|
@ -135,7 +135,7 @@ public class ReplayCommitLogsToSqlActionTest {
|
|||
action.diffLister.gcsUtils = gcsUtils;
|
||||
action.diffLister.executorProvider = MoreExecutors::newDirectExecutorService;
|
||||
action.diffLister.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||
ofyTm()
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
|
@ -469,7 +469,7 @@ public class ReplayCommitLogsToSqlActionTest {
|
|||
|
||||
@Test
|
||||
void testFailure_notEnabled() {
|
||||
ofyTm().transact(() -> DatabaseMigrationStateSchedule.set(DEFAULT_TRANSITION_MAP.toValueMap()));
|
||||
jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(DEFAULT_TRANSITION_MAP.toValueMap()));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_NO_CONTENT);
|
||||
assertThat(response.getPayload())
|
||||
|
|
|
@ -63,7 +63,7 @@ public final class BackupTestStore implements AutoCloseable {
|
|||
this.fakeClock = fakeClock;
|
||||
this.appEngine =
|
||||
new AppEngineExtension.Builder()
|
||||
.withDatastore()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withoutCannedData()
|
||||
.withClock(fakeClock)
|
||||
.build();
|
||||
|
|
|
@ -21,7 +21,7 @@ import static google.registry.model.common.DatabaseMigrationStateSchedule.Migrat
|
|||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.SQL_ONLY;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.SQL_PRIMARY;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.SQL_PRIMARY_READ_ONLY;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
@ -36,6 +36,7 @@ import org.junit.jupiter.api.AfterEach;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link DatabaseMigrationStateSchedule}. */
|
||||
public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
||||
|
||||
@BeforeEach
|
||||
|
@ -123,7 +124,7 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
|||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> ofyTm().transact(() -> DatabaseMigrationStateSchedule.set(nowInvalidMap))))
|
||||
() -> jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(nowInvalidMap))))
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Cannot transition from current state-as-of-now DATASTORE_ONLY "
|
||||
|
@ -139,7 +140,7 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
|||
DatabaseMigrationStateSchedule.set(
|
||||
DatabaseMigrationStateSchedule.DEFAULT_TRANSITION_MAP.toValueMap())))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Must be called in a transaction");
|
||||
.isEqualTo("Not in a transaction");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -154,7 +155,7 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
|||
private void runValidTransition(MigrationState from, MigrationState to) {
|
||||
ImmutableSortedMap<DateTime, MigrationState> transitions =
|
||||
createMapEndingWithTransition(from, to);
|
||||
ofyTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions));
|
||||
jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions));
|
||||
assertThat(DatabaseMigrationStateSchedule.getUncached().toValueMap())
|
||||
.containsExactlyEntriesIn(transitions);
|
||||
}
|
||||
|
@ -165,7 +166,7 @@ public class DatabaseMigrationStateScheduleTest extends EntityTestCase {
|
|||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> ofyTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions))))
|
||||
() -> jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions))))
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
String.format("validStateTransitions map cannot transition from %s to %s.", from, to));
|
||||
|
|
|
@ -38,7 +38,7 @@ public class DatastoreTransactionManagerTest {
|
|||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder()
|
||||
.withDatastore()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(InCrossTldTestEntity.class)
|
||||
.build();
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
|||
class EntityWritePrioritiesTest {
|
||||
|
||||
@RegisterExtension
|
||||
AppEngineExtension appEngine = new AppEngineExtension.Builder().withDatastore().build();
|
||||
AppEngineExtension appEngine =
|
||||
new AppEngineExtension.Builder().withDatastoreAndCloudSql().build();
|
||||
|
||||
@Test
|
||||
void testGetPriority() {
|
||||
|
|
|
@ -196,7 +196,7 @@ public class ReplicateToDatastoreActionTest {
|
|||
void testNotInMigrationState_doesNothing() {
|
||||
// set a schedule that backtracks the current status to DATASTORE_PRIMARY_READ_ONLY
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
ofyTm()
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
|
|
|
@ -19,16 +19,13 @@ import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableO
|
|||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class SignedMarkRevocationListDaoTest extends EntityTestCase {
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationWithCoverageExtension jpa =
|
||||
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
|
||||
public SignedMarkRevocationListDaoTest() {
|
||||
super(JpaEntityCoverageCheck.ENABLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSave_success() {
|
||||
|
|
|
@ -36,7 +36,10 @@ public class VKeyTranslatorFactoryTest {
|
|||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastore().withOfyTestEntities(TestObject.class).build();
|
||||
AppEngineExtension.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(TestObject.class)
|
||||
.build();
|
||||
|
||||
VKeyTranslatorFactoryTest() {}
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ public class AllocationTokenStatusTransitionConverterTest {
|
|||
@RegisterExtension
|
||||
public final JpaUnitTestExtension jpa =
|
||||
new JpaTestRules.Builder()
|
||||
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
|
||||
.withEntityClass(AllocationTokenStatusTransitionConverterTestEntity.class)
|
||||
.buildUnitTestRule();
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@ public class BillingCostTransitionConverterTest {
|
|||
@RegisterExtension
|
||||
public final JpaUnitTestExtension jpa =
|
||||
new JpaTestRules.Builder()
|
||||
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
|
||||
.withEntityClass(TestEntity.class)
|
||||
.buildUnitTestRule();
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ public class CurrencyToBillingConverterTest {
|
|||
@RegisterExtension
|
||||
public final JpaUnitTestExtension jpaExtension =
|
||||
new JpaTestRules.Builder()
|
||||
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
|
||||
.withEntityClass(TestEntity.class)
|
||||
.buildUnitTestRule();
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
// 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 static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationStateTransition;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestExtension;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link DatabaseMigrationScheduleTransitionConverter}. */
|
||||
public class DatabaseMigrationScheduleTransitionConverterTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final JpaUnitTestExtension jpa =
|
||||
new JpaTestRules.Builder()
|
||||
.withEntityClass(DatabaseMigrationScheduleTransitionConverterTestEntity.class)
|
||||
.buildUnitTestRule();
|
||||
|
||||
private static final ImmutableSortedMap<DateTime, MigrationState> values =
|
||||
ImmutableSortedMap.of(
|
||||
START_OF_TIME,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
DateTime.parse("2001-01-01T00:00:00.0Z"),
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
DateTime.parse("2002-01-01T00:00:00.0Z"),
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
DateTime.parse("2002-01-02T00:00:00.0Z"),
|
||||
MigrationState.SQL_PRIMARY,
|
||||
DateTime.parse("2002-01-03T00:00:00.0Z"),
|
||||
MigrationState.SQL_ONLY);
|
||||
|
||||
@Test
|
||||
void roundTripConversion_returnsSameTimedTransitionProperty() {
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> timedTransitionProperty =
|
||||
TimedTransitionProperty.fromValueMap(values, MigrationStateTransition.class);
|
||||
DatabaseMigrationScheduleTransitionConverterTestEntity testEntity =
|
||||
new DatabaseMigrationScheduleTransitionConverterTestEntity(timedTransitionProperty);
|
||||
jpaTm().transact(() -> jpaTm().insert(testEntity));
|
||||
DatabaseMigrationScheduleTransitionConverterTestEntity persisted =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.getEntityManager()
|
||||
.find(DatabaseMigrationScheduleTransitionConverterTestEntity.class, "id"));
|
||||
assertThat(persisted.timedTransitionProperty).containsExactlyEntriesIn(timedTransitionProperty);
|
||||
}
|
||||
|
||||
@Entity
|
||||
private static class DatabaseMigrationScheduleTransitionConverterTestEntity
|
||||
extends ImmutableObject {
|
||||
|
||||
@Id String name = "id";
|
||||
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> timedTransitionProperty;
|
||||
|
||||
private DatabaseMigrationScheduleTransitionConverterTestEntity() {}
|
||||
|
||||
private DatabaseMigrationScheduleTransitionConverterTestEntity(
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> timedTransitionProperty) {
|
||||
this.timedTransitionProperty = timedTransitionProperty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,7 +37,6 @@ public class StringMapConverterBaseTest {
|
|||
@RegisterExtension
|
||||
public final JpaUnitTestExtension jpaExtension =
|
||||
new JpaTestRules.Builder()
|
||||
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
|
||||
.withEntityClass(TestStringMapConverter.class, TestEntity.class)
|
||||
.buildUnitTestRule();
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@ class TimedTransitionPropertyConverterBaseTest {
|
|||
@RegisterExtension
|
||||
public final JpaUnitTestExtension jpa =
|
||||
new JpaTestRules.Builder()
|
||||
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
|
||||
.withEntityClass(TestTimedTransitionPropertyConverter.class, TestEntity.class)
|
||||
.buildUnitTestRule();
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ class TldStateTransitionConverterTest {
|
|||
@RegisterExtension
|
||||
public final JpaUnitTestExtension jpa =
|
||||
new JpaTestRules.Builder()
|
||||
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
|
||||
.withEntityClass(TestEntity.class)
|
||||
.buildUnitTestRule();
|
||||
|
||||
|
|
|
@ -39,15 +39,14 @@ import org.junit.jupiter.api.extension.ExtensionContext;
|
|||
*/
|
||||
public class JpaEntityCoverageExtension implements BeforeEachCallback, AfterEachCallback {
|
||||
|
||||
// TODO(weiminyu): update this set when entities written to Cloud SQL and tests are added.
|
||||
private static final ImmutableSet<String> IGNORE_ENTITIES =
|
||||
ImmutableSet.of(
|
||||
"DelegationSignerData",
|
||||
"DesignatedContact",
|
||||
"GracePeriod",
|
||||
"RegistrarContact",
|
||||
|
||||
// TransactionEntity is trivial, its persistence is tested in TransactionTest.
|
||||
// DatabaseMigrationStateSchedule is persisted in tests, however any test that sets it
|
||||
// needs to remove it in order to avoid affecting any other tests running in the same JVM.
|
||||
// TODO(gbrodman): remove this when we implement proper read-only modes for the
|
||||
// transaction managers.
|
||||
"DatabaseMigrationStateSchedule",
|
||||
// TransactionEntity is trivial; its persistence is tested in TransactionTest.
|
||||
"TransactionEntity");
|
||||
|
||||
private static final ImmutableSet<Class<?>> ALL_JPA_ENTITIES =
|
||||
|
|
|
@ -41,6 +41,8 @@ import org.junit.jupiter.api.extension.ExtensionContext;
|
|||
public class JpaTestRules {
|
||||
|
||||
private static final String GOLDEN_SCHEMA_SQL_PATH = "sql/schema/nomulus.golden.sql";
|
||||
private static final String HSTORE_EXTENSION_SQL_PATH =
|
||||
"sql/flyway/V14__load_extension_for_hstore.sql";
|
||||
|
||||
/**
|
||||
* Junit rule for integration tests with JPA framework, when the underlying database is populated
|
||||
|
@ -174,7 +176,8 @@ public class JpaTestRules {
|
|||
"Unit tests must not depend on the Nomulus schema.");
|
||||
return new JpaUnitTestExtension(
|
||||
clock == null ? new FakeClock(DateTime.now(UTC)) : clock,
|
||||
Optional.ofNullable(initScript),
|
||||
// Use the hstore extension by default so we can save the migration schedule
|
||||
Optional.of(initScript == null ? HSTORE_EXTENSION_SQL_PATH : initScript),
|
||||
ImmutableList.copyOf(extraEntityClasses),
|
||||
ImmutableMap.copyOf(userProperties));
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableMap;
|
|||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.io.Resources;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.persistence.HibernateSchemaExporter;
|
||||
import google.registry.persistence.NomulusPostgreSql;
|
||||
import google.registry.persistence.PersistenceModule;
|
||||
|
@ -341,9 +342,12 @@ abstract class JpaTransactionManagerExtension implements BeforeEachCallback, Aft
|
|||
}
|
||||
|
||||
private ImmutableList<Class<?>> getTestEntities() {
|
||||
// We have to add the TransactionEntity to extra entities, as this is required by the
|
||||
// transaction replication mechanism.
|
||||
return Stream.concat(extraEntityClasses.stream(), Stream.of(TransactionEntity.class))
|
||||
// We have to add the DatabaseMigrationStateSchedule and TransactionEntity classes to extra
|
||||
// entities, as they are required by the transaction manager factory and transaction replication
|
||||
// mechanism, respectively.
|
||||
return Stream.concat(
|
||||
extraEntityClasses.stream(),
|
||||
Stream.of(DatabaseMigrationStateSchedule.class, TransactionEntity.class))
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,8 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
|||
class GenerateInvoicesActionTest extends BeamActionTestBase {
|
||||
|
||||
@RegisterExtension
|
||||
final AppEngineExtension appEngine = AppEngineExtension.builder().withTaskQueue().build();
|
||||
final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().withTaskQueue().build();
|
||||
|
||||
private final BillingEmailUtils emailUtils = mock(BillingEmailUtils.class);
|
||||
private FakeClock clock = new FakeClock();
|
||||
|
|
|
@ -159,12 +159,6 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
|
|||
return this;
|
||||
}
|
||||
|
||||
/** Turns on Datastore only, for use by test data generators. */
|
||||
public Builder withDatastore() {
|
||||
rule.withDatastore = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Turns on Cloud SQL only, for use by test data generators. */
|
||||
public Builder withCloudSql() {
|
||||
rule.withCloudSql = true;
|
||||
|
@ -372,7 +366,8 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
|
|||
checkArgumentNotNull(context, "The ExtensionContext must not be null");
|
||||
setUp();
|
||||
if (withCloudSql) {
|
||||
JpaTestRules.Builder builder = new JpaTestRules.Builder();
|
||||
JpaTestRules.Builder builder =
|
||||
new JpaTestRules.Builder().withEntityClass(jpaTestEntities.toArray(new Class[0]));
|
||||
if (clock != null) {
|
||||
builder.withClock(clock);
|
||||
}
|
||||
|
@ -380,8 +375,7 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
|
|||
jpaIntegrationWithCoverageExtension = builder.buildIntegrationWithCoverageExtension();
|
||||
jpaIntegrationWithCoverageExtension.beforeEach(context);
|
||||
} else if (withJpaUnitTest) {
|
||||
jpaUnitTestRule =
|
||||
builder.withEntityClass(jpaTestEntities.toArray(new Class[0])).buildUnitTestRule();
|
||||
jpaUnitTestRule = builder.buildUnitTestRule();
|
||||
jpaUnitTestRule.beforeEach(context);
|
||||
} else {
|
||||
jpaIntegrationTestRule = builder.buildIntegrationTestRule();
|
||||
|
@ -391,8 +385,10 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
|
|||
if (isWithDatastoreAndCloudSql()) {
|
||||
injectTmForDualDatabaseTest(context);
|
||||
}
|
||||
if (!withoutCannedData && (tm().isOfy() || (withCloudSql && !withJpaUnitTest))) {
|
||||
loadInitialData();
|
||||
if (withDatastore || withCloudSql) {
|
||||
if (!withoutCannedData && (tm().isOfy() || (withCloudSql && !withJpaUnitTest))) {
|
||||
loadInitialData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ import static google.registry.model.ResourceTransferUtils.createTransferResponse
|
|||
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
|
||||
import static google.registry.model.tld.Registry.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.ofyTmOrDoNothing;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
|
@ -1373,7 +1372,7 @@ public class DatabaseHelper {
|
|||
*/
|
||||
public static void setMigrationScheduleToSqlPrimary(FakeClock fakeClock) {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
ofyTm()
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
|
@ -1392,12 +1391,12 @@ public class DatabaseHelper {
|
|||
/** Removes the database migration schedule, in essence transitioning to DATASTORE_ONLY. */
|
||||
public static void removeDatabaseMigrationSchedule() {
|
||||
// use the raw calls because going SQL_PRIMARY -> DATASTORE_ONLY is not valid
|
||||
ofyTm()
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
ofyTm()
|
||||
jpaTm()
|
||||
.loadSingleton(DatabaseMigrationStateSchedule.class)
|
||||
.ifPresent(ofyTm()::delete));
|
||||
.ifPresent(jpaTm()::delete));
|
||||
DatabaseMigrationStateSchedule.CACHE.invalidateAll();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
package google.registry.tools;
|
||||
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.DEFAULT_TRANSITION_MAP;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
|
@ -59,7 +59,7 @@ public class GetDatabaseMigrationStateCommandTest
|
|||
MigrationState.SQL_PRIMARY,
|
||||
now.plusHours(4),
|
||||
MigrationState.SQL_ONLY);
|
||||
ofyTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions));
|
||||
jpaTm().transact(() -> DatabaseMigrationStateSchedule.set(transitions));
|
||||
runCommand();
|
||||
assertStdoutIs(String.format("Current migration schedule: %s\n", transitions));
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ package google.registry.tools;
|
|||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.DEFAULT_TRANSITION_MAP;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
|
@ -44,15 +44,15 @@ public class SetDatabaseMigrationStateCommandTest
|
|||
@TestOfyAndSql
|
||||
void testSuccess_setsBasicSchedule() throws Exception {
|
||||
assertThat(DatabaseMigrationStateSchedule.get()).isEqualTo(DEFAULT_TRANSITION_MAP);
|
||||
assertThat(ofyTm().transact(() -> ofyTm().loadSingleton(DatabaseMigrationStateSchedule.class)))
|
||||
assertThat(jpaTm().transact(() -> jpaTm().loadSingleton(DatabaseMigrationStateSchedule.class)))
|
||||
.isEmpty();
|
||||
runCommandForced("--migration_schedule=1970-01-01T00:00:00.000Z=DATASTORE_ONLY");
|
||||
// use a raw ofy call to check what's in the DB
|
||||
ofyTm()
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
assertThat(
|
||||
ofyTm()
|
||||
jpaTm()
|
||||
.loadSingleton(DatabaseMigrationStateSchedule.class)
|
||||
.get()
|
||||
.migrationTransitions)
|
||||
|
|
|
@ -2,7 +2,6 @@ AllocationToken
|
|||
Cancellation
|
||||
ContactResource
|
||||
Cursor
|
||||
DatabaseMigrationStateSchedule
|
||||
DomainBase
|
||||
EntityGroupRoot
|
||||
EppResourceIndex
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
ClaimsList
|
||||
ClaimsListSingleton
|
||||
Cursor
|
||||
DatabaseMigrationStateSchedule
|
||||
Registrar
|
||||
RegistrarContact
|
||||
Registry
|
||||
|
|
|
@ -78,23 +78,6 @@ class google.registry.model.common.Cursor {
|
|||
google.registry.model.UpdateAutoTimestamp lastUpdateTime;
|
||||
org.joda.time.DateTime cursorTime;
|
||||
}
|
||||
class google.registry.model.common.DatabaseMigrationStateSchedule {
|
||||
@Id long id;
|
||||
@Parent com.googlecode.objectify.Key<google.registry.model.common.EntityGroupRoot> parent;
|
||||
google.registry.model.common.TimedTransitionProperty<google.registry.model.common.DatabaseMigrationStateSchedule$MigrationState, google.registry.model.common.DatabaseMigrationStateSchedule$MigrationStateTransition> migrationTransitions;
|
||||
}
|
||||
enum google.registry.model.common.DatabaseMigrationStateSchedule$MigrationState {
|
||||
DATASTORE_ONLY;
|
||||
DATASTORE_PRIMARY;
|
||||
DATASTORE_PRIMARY_READ_ONLY;
|
||||
SQL_ONLY;
|
||||
SQL_PRIMARY;
|
||||
SQL_PRIMARY_READ_ONLY;
|
||||
}
|
||||
class google.registry.model.common.DatabaseMigrationStateSchedule$MigrationStateTransition {
|
||||
google.registry.model.common.DatabaseMigrationStateSchedule$MigrationState migrationState;
|
||||
org.joda.time.DateTime transitionTime;
|
||||
}
|
||||
class google.registry.model.common.EntityGroupRoot {
|
||||
@Id java.lang.String id;
|
||||
google.registry.model.UpdateAutoTimestamp updateTimestamp;
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS hstore WITH SCHEMA public;
|
||||
|
||||
CREATE TABLE Person (
|
||||
age INT NOT NULL
|
||||
);
|
||||
|
|
|
@ -261,7 +261,7 @@ td.section {
|
|||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">generated on</td>
|
||||
<td class="property_value">2021-08-10 16:04:36.360808</td>
|
||||
<td class="property_value">2021-08-10 20:50:09.993881</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">last flyway file</td>
|
||||
|
@ -284,7 +284,7 @@ td.section {
|
|||
generated on
|
||||
</text>
|
||||
<text text-anchor="start" x="4055.5" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
2021-08-10 16:04:36.360808
|
||||
2021-08-10 20:50:09.993881
|
||||
</text>
|
||||
<polygon fill="none" stroke="#888888" points="3968,-4 3968,-44 4233,-44 4233,-4 3968,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<g id="node1" class="node">
|
||||
|
|
|
@ -261,7 +261,7 @@ td.section {
|
|||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">generated on</td>
|
||||
<td class="property_value">2021-08-10 16:04:34.151068</td>
|
||||
<td class="property_value">2021-08-10 20:50:07.996141</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="property_name">last flyway file</td>
|
||||
|
@ -284,7 +284,7 @@ td.section {
|
|||
generated on
|
||||
</text>
|
||||
<text text-anchor="start" x="4755.52" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||
2021-08-10 16:04:34.151068
|
||||
2021-08-10 20:50:07.996141
|
||||
</text>
|
||||
<polygon fill="none" stroke="#888888" points="4668.02,-4 4668.02,-44 4933.02,-44 4933.02,-4 4668.02,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||
<g id="node1" class="node">
|
||||
|
|
|
@ -238,6 +238,12 @@
|
|||
primary key (scope, type)
|
||||
);
|
||||
|
||||
create table "DatabaseMigrationStateSchedule" (
|
||||
id int8 not null,
|
||||
migration_transitions hstore,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table "DelegationSignerData" (
|
||||
algorithm int4 not null,
|
||||
digest bytea not null,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue