Add SQL replay checkpoint object to SQL (#868)

* Add SQL replay checkpoint object to Datastore

This will be part of the asynchronous commit-log replay to SQL. Whenever
we successfully export commits up to a particular time, we should
persist that time so we don't replay the same commits again (it is not
idempotent)

* Move SqlReplayCheckpoint from DS to SQL

* Responses to CR
This commit is contained in:
gbrodman 2020-11-10 17:09:18 -05:00 committed by GitHub
parent 82cc7b59fe
commit f1eb1a8fe7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 387 additions and 107 deletions

View file

@ -0,0 +1,58 @@
// 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.schema.replay;
import static google.registry.model.common.CrossTldSingleton.SINGLETON_ID;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableList;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.joda.time.DateTime;
@Entity
public class SqlReplayCheckpoint implements SqlEntity {
// Hibernate doesn't allow us to have a converted DateTime as our primary key so we need this
@Id private long revisionId = SINGLETON_ID;
private DateTime lastReplayTime;
@Override
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
return ImmutableList.of(); // not necessary to persist in Datastore
}
public static DateTime get() {
jpaTm().assertInTransaction();
return jpaTm()
.getEntityManager()
.createQuery("FROM SqlReplayCheckpoint", SqlReplayCheckpoint.class)
.setMaxResults(1)
.getResultStream()
.findFirst()
.map(checkpoint -> checkpoint.lastReplayTime)
.orElse(START_OF_TIME);
}
public static void set(DateTime lastReplayTime) {
jpaTm().assertInTransaction();
SqlReplayCheckpoint checkpoint = new SqlReplayCheckpoint();
checkpoint.lastReplayTime = lastReplayTime;
// this will overwrite the existing object due to the constant revisionId
jpaTm().put(checkpoint);
}
}

View file

@ -70,6 +70,7 @@
<class>google.registry.persistence.transaction.TransactionEntity</class>
<class>google.registry.schema.cursor.Cursor</class>
<class>google.registry.schema.domain.RegistryLock</class>
<class>google.registry.schema.replay.SqlReplayCheckpoint</class>
<class>google.registry.schema.server.Lock</class>
<class>google.registry.schema.tld.PremiumEntry</class>

View file

@ -15,15 +15,11 @@
package google.registry.model.server;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.common.CrossTldSingleton.SINGLETON_ID;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.googlecode.objectify.Key;
import google.registry.model.EntityTestCase;
import google.registry.model.ofy.RequestCapturingAsyncDatastoreService;
import google.registry.persistence.VKey;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -85,11 +81,4 @@ public class ServerSecretTest extends EntityTestCase {
.findFirst()
.get());
}
private static VKey<ServerSecret> createKey() {
return VKey.create(
ServerSecret.class,
SINGLETON_ID,
Key.create(getCrossTldKey(), ServerSecret.class, SINGLETON_ID));
}
}

View file

@ -40,6 +40,7 @@ import google.registry.schema.cursor.CursorDaoTest;
import google.registry.schema.integration.SqlIntegrationTestSuite.AfterSuiteTest;
import google.registry.schema.integration.SqlIntegrationTestSuite.BeforeSuiteTest;
import google.registry.schema.registrar.RegistrarDaoTest;
import google.registry.schema.replay.SqlReplayCheckpointTest;
import google.registry.schema.server.LockDaoTest;
import google.registry.schema.tld.PremiumListDaoTest;
import google.registry.testing.AppEngineExtension;
@ -100,6 +101,7 @@ import org.junit.runner.RunWith;
ServerSecretTest.class,
SignedMarkRevocationListDaoTest.class,
Spec11ThreatMatchTest.class,
SqlReplayCheckpointTest.class,
TmchCrlTest.class,
// AfterSuiteTest must be the last entry. See class javadoc for details.
AfterSuiteTest.class

View file

@ -0,0 +1,61 @@
// 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.schema.replay;
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 google.registry.model.EntityTestCase;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Test;
/** Tests for {@link SqlReplayCheckpoint}. */
public class SqlReplayCheckpointTest extends EntityTestCase {
SqlReplayCheckpointTest() {
super(JpaEntityCoverageCheck.ENABLED);
}
@Test
void testEmpty_startOfTime() {
assertThat(jpaTm().transact(SqlReplayCheckpoint::get)).isEqualTo(START_OF_TIME);
}
@Test
void testSuccess_writes() {
DateTime dateTime = DateTime.parse("2012-02-29T00:00:00Z");
jpaTm().transact(() -> SqlReplayCheckpoint.set(dateTime));
assertThat(jpaTm().transact(SqlReplayCheckpoint::get)).isEqualTo(dateTime);
}
@Test
void testSuccess_multipleWrites() {
DateTime firstTime = DateTime.parse("2012-02-29T00:00:00Z");
jpaTm().transact(() -> SqlReplayCheckpoint.set(firstTime));
DateTime secondTime = DateTime.parse("2013-02-28T00:00:00Z");
jpaTm().transact(() -> SqlReplayCheckpoint.set(secondTime));
assertThat(jpaTm().transact(SqlReplayCheckpoint::get)).isEqualTo(secondTime);
jpaTm()
.transact(
() ->
assertThat(
jpaTm()
.getEntityManager()
.createQuery("SELECT COUNT(*) FROM SqlReplayCheckpoint", Long.class)
.getSingleResult())
.isEqualTo(1L));
}
}

View file

@ -221,12 +221,6 @@ class DedupeRecurringBillingEventIdsCommandTest
}
}
private static void assertNotChangeInDatastore(ImmutableObject... entities) {
for (ImmutableObject entity : entities) {
assertThat(ofy().load().entity(entity).now()).isEqualTo(entity);
}
}
private static void assertNotChangeExceptUpdateTime(ImmutableObject... entities) {
for (ImmutableObject entity : entities) {
assertAboutImmutableObjects()