1
0
mirror of https://github.com/google/nomulus synced 2026-02-10 06:50:30 +00:00

Make entities serializable for DB validation (#1401)

* Make entities serializable for DB validation

Make entities that are asynchronously replicated between Datastore and
Cloud SQL serializable so that they may be used in BEAM pipeline based
comparison tool.

Introduced an UnsafeSerializable interface (extending Serializable) and
added to relevant classes. Implementing classes are allowed some
shortcuts as explained in the interface's Javadoc. Post migration we
will decide whether to revert this change or properly implement
serialization.

Verified with production data.
This commit is contained in:
Weimin Yu
2021-10-28 12:19:09 -04:00
committed by GitHub
parent 1e7aae26a3
commit 93a479837f
47 changed files with 333 additions and 62 deletions

View File

@@ -25,6 +25,7 @@ import static google.registry.testing.DatabaseHelper.loadByKey;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.SerializeUtils.serializeDeserialize;
import static org.joda.money.CurrencyUnit.USD;
import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -46,6 +47,7 @@ import google.registry.persistence.VKey;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.util.DateTimeUtils;
import org.joda.money.Money;
import org.joda.time.DateTime;
@@ -195,6 +197,20 @@ public class BillingEventTest extends EntityTestCase {
ofyTmOrDoNothing(() -> assertThat(tm().loadByEntity(modification)).isEqualTo(modification));
}
@TestSqlOnly
void testSerializable() {
BillingEvent persisted = loadByEntity(oneTime);
assertThat(serializeDeserialize(persisted)).isEqualTo(persisted);
persisted = loadByEntity(oneTimeSynthetic);
assertThat(serializeDeserialize(persisted)).isEqualTo(persisted);
persisted = loadByEntity(recurring);
assertThat(serializeDeserialize(persisted)).isEqualTo(persisted);
persisted = loadByEntity(cancellationOneTime);
assertThat(serializeDeserialize(persisted)).isEqualTo(persisted);
persisted = loadByEntity(cancellationRecurring);
assertThat(serializeDeserialize(persisted)).isEqualTo(persisted);
}
@TestOfyOnly
void testParenting() {
// Note that these are all tested separately because BillingEvent is an abstract base class that

View File

@@ -30,6 +30,8 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.tld.Registry;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestSqlOnly;
import google.registry.util.SerializeUtils;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -46,6 +48,15 @@ public class CursorTest extends EntityTestCase {
fakeClock.setTo(DateTime.parse("2010-10-17TZ"));
}
@TestSqlOnly
void testSerializable() {
final DateTime time = DateTime.parse("2012-07-12T03:30:00.000Z");
tm().transact(() -> tm().put(Cursor.createGlobal(RECURRING_BILLING, time)));
Cursor persisted =
tm().transact(() -> tm().loadByKey(Cursor.createGlobalVKey(RECURRING_BILLING)));
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
@TestOfyAndSql
void testSuccess_persistScopedCursor() {
createTld("tld");

View File

@@ -48,6 +48,7 @@ import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.util.SerializeUtils;
import org.junit.jupiter.api.BeforeEach;
/** Unit tests for {@link ContactResource}. */
@@ -174,6 +175,14 @@ public class ContactResourceTest extends EntityTestCase {
.hasValue(contactResource);
}
@TestSqlOnly
void testSerializable() {
ContactResource persisted =
loadByForeignKey(ContactResource.class, contactResource.getForeignKey(), fakeClock.nowUtc())
.get();
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
@TestOfyOnly
void testIndexing() throws Exception {
verifyDatastoreIndexing(

View File

@@ -56,6 +56,7 @@ import google.registry.testing.AppEngineExtension;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.FakeClock;
import google.registry.testing.TestSqlOnly;
import google.registry.util.SerializeUtils;
import java.util.Arrays;
import org.joda.money.Money;
import org.joda.time.DateTime;
@@ -354,6 +355,14 @@ public class DomainBaseSqlTest {
});
}
@TestSqlOnly
void testSerializable() {
createTld("com");
insertInDb(contact, contact2, domain, host);
DomainBase persisted = jpaTm().transact(() -> jpaTm().loadByEntity(domain));
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
@TestSqlOnly
void testUpdates() {
createTld("com");

View File

@@ -41,6 +41,8 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.util.SerializeUtils;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -93,6 +95,44 @@ public class AllocationTokenTest extends EntityTestCase {
assertThat(loadByEntity(singleUseToken)).isEqualTo(singleUseToken);
}
@TestSqlOnly
void testSerializable() {
AllocationToken unlimitedUseToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123Unlimited")
.setTokenType(UNLIMITED_USE)
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.setAllowedTlds(ImmutableSet.of("dev", "app"))
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar, NewRegistrar"))
.setDiscountFraction(0.5)
.setDiscountPremiums(true)
.setDiscountYears(3)
.setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
.put(START_OF_TIME, NOT_STARTED)
.put(DateTime.now(UTC), TokenStatus.VALID)
.put(DateTime.now(UTC).plusWeeks(8), TokenStatus.ENDED)
.build())
.build());
AllocationToken persisted = loadByEntity(unlimitedUseToken);
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
DomainBase domain = persistActiveDomain("example.foo");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1);
AllocationToken singleUseToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123Single")
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey))
.setDomainName("example.foo")
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.setTokenType(SINGLE_USE)
.build());
persisted = loadByEntity(singleUseToken);
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
@TestOfyOnly
void testIndexing() throws Exception {
DomainBase domain = persistActiveDomain("blahdomain.foo");

View File

@@ -39,6 +39,7 @@ import google.registry.persistence.VKey;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.util.SerializeUtils;
/** Tests for {@link ContactHistory}. */
@DualDatabaseTest
@@ -64,6 +65,18 @@ public class ContactHistoryTest extends EntityTestCase {
});
}
@TestSqlOnly
void testSerializable() {
ContactResource contact = newContactResourceWithRoid("contactId", "contact1");
insertInDb(contact);
ContactResource contactFromDb = loadByEntity(contact);
ContactHistory contactHistory = createContactHistory(contactFromDb);
insertInDb(contactHistory);
ContactHistory fromDatabase =
jpaTm().transact(() -> jpaTm().loadByKey(contactHistory.createVKey()));
assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
}
@TestSqlOnly
void testLegacyPersistence_nullContactBase() {
ContactResource contact = newContactResourceWithRoid("contactId", "contact1");

View File

@@ -55,6 +55,7 @@ import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.util.SerializeUtils;
import java.util.Optional;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -87,6 +88,16 @@ public class DomainHistoryTest extends EntityTestCase {
});
}
@TestSqlOnly
void testSerializable() {
DomainBase domain = addGracePeriodForSql(createDomainWithContactsAndHosts());
DomainHistory domainHistory = createDomainHistory(domain);
insertInDb(domainHistory);
DomainHistory fromDatabase =
jpaTm().transact(() -> jpaTm().loadByKey(domainHistory.createVKey()));
assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
}
@TestSqlOnly
void testLegacyPersistence_nullResource() {
DomainBase domain = addGracePeriodForSql(createDomainWithContactsAndHosts());

View File

@@ -36,6 +36,7 @@ import google.registry.persistence.VKey;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.util.SerializeUtils;
/** Tests for {@link HostHistory}. */
@DualDatabaseTest
@@ -61,6 +62,17 @@ public class HostHistoryTest extends EntityTestCase {
});
}
@TestSqlOnly
void testSerializable() {
HostResource host = newHostResourceWithRoid("ns1.example.com", "host1");
insertInDb(host);
HostResource hostFromDb = loadByEntity(host);
HostHistory hostHistory = createHostHistory(hostFromDb);
insertInDb(hostHistory);
HostHistory fromDatabase = jpaTm().transact(() -> jpaTm().loadByKey(hostHistory.createVKey()));
assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
}
@TestSqlOnly
void testLegacyPersistence_nullHostBase() {
HostResource host = newHostResourceWithRoid("ns1.example.com", "host1");

View File

@@ -45,6 +45,8 @@ import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.util.SerializeUtils;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -111,6 +113,14 @@ class HostResourceTest extends EntityTestCase {
.containsExactly(newHost);
}
@TestSqlOnly
void testSerializable() {
HostResource newHost = host.asBuilder().setRepoId("NEWHOST").build();
tm().transact(() -> tm().insert(newHost));
HostResource persisted = tm().transact(() -> tm().loadByEntity(newHost));
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
@TestOfyOnly
void testLoadingByForeignKey() {
assertThat(loadByForeignKey(HostResource.class, host.getForeignKey(), fakeClock.nowUtc()))

View File

@@ -36,6 +36,7 @@ import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.util.SerializeUtils;
import org.junit.jupiter.api.BeforeEach;
/** Unit tests for {@link PollMessage}. */
@@ -118,6 +119,20 @@ public class PollMessageTest extends EntityTestCase {
assertThat(tm().transact(() -> tm().loadByEntity(pollMessage))).isEqualTo(pollMessage);
}
@TestSqlOnly
void testSerializableOneTime() {
PollMessage.OneTime pollMessage =
persistResource(
new PollMessage.OneTime.Builder()
.setRegistrarId("TheRegistrar")
.setEventTime(fakeClock.nowUtc())
.setMsg("Test poll message")
.setParent(historyEntry)
.build());
PollMessage persisted = tm().transact(() -> tm().loadByEntity(pollMessage));
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
@TestOfyAndSql
void testPersistenceAutorenew() {
PollMessage.Autorenew pollMessage =
@@ -133,6 +148,22 @@ public class PollMessageTest extends EntityTestCase {
assertThat(tm().transact(() -> tm().loadByEntity(pollMessage))).isEqualTo(pollMessage);
}
@TestSqlOnly
void testSerializableAutorenew() {
PollMessage.Autorenew pollMessage =
persistResource(
new PollMessage.Autorenew.Builder()
.setRegistrarId("TheRegistrar")
.setEventTime(fakeClock.nowUtc())
.setMsg("Test poll message")
.setParent(historyEntry)
.setAutorenewEndTime(fakeClock.nowUtc().plusDays(365))
.setTargetId("foobar.foo")
.build());
PollMessage persisted = tm().transact(() -> tm().loadByEntity(pollMessage));
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
@TestOfyOnly
void testIndexingAutorenew() throws Exception {
PollMessage.Autorenew pollMessage =

View File

@@ -45,7 +45,9 @@ import google.registry.model.tld.Registries;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.util.CidrAddressBlock;
import google.registry.util.SerializeUtils;
import org.joda.money.CurrencyUnit;
import org.junit.jupiter.api.BeforeEach;
@@ -135,6 +137,12 @@ class RegistrarTest extends EntityTestCase {
assertThat(tm().transact(() -> tm().loadByKey(registrar.createVKey()))).isEqualTo(registrar);
}
@TestSqlOnly
void testSerializable() {
Registrar persisted = tm().transact(() -> tm().loadByKey(registrar.createVKey()));
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
@TestOfyOnly
void testIndexing() throws Exception {
verifyDatastoreIndexing(registrar, "registrarName", "ianaIdentifier");

View File

@@ -47,6 +47,8 @@ import google.registry.testing.DatabaseHelper;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.util.SerializeUtils;
import java.math.BigDecimal;
import java.util.Optional;
import org.joda.money.Money;
@@ -87,6 +89,16 @@ public final class RegistryTest extends EntityTestCase {
.isEqualTo(Registry.get("tld"));
}
@TestSqlOnly
void testSerializable() {
ReservedList rl15 = persistReservedList("tld-reserved15", "potato,FULLY_BLOCKED");
Registry registry = Registry.get("tld").asBuilder().setReservedLists(rl15).build();
tm().transact(() -> tm().put(registry));
Registry persisted =
tm().transact(() -> tm().loadByKey(Registry.createVKey(registry.tldStrId)));
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
@TestOfyAndSql
void testFailure_registryNotFound() {
createTld("foo");

View File

@@ -16,6 +16,7 @@ package google.registry.schema.registrar;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.registrar.RegistrarContact.Type.WHOIS;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.DatabaseHelper.insertInDb;
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static google.registry.testing.SqlHelper.saveRegistrar;
@@ -27,6 +28,7 @@ import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.TmOverrideExtension;
import google.registry.util.SerializeUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
@@ -74,4 +76,11 @@ class RegistrarContactTest {
insertInDb(testRegistrarPoc);
assertThat(loadByEntity(testRegistrarPoc)).isEqualTo(testRegistrarPoc);
}
@Test
void testSerializable_succeeds() {
insertInDb(testRegistrarPoc);
RegistrarContact persisted = jpaTm().transact(() -> jpaTm().loadByEntity(testRegistrarPoc));
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
}
}