Add dual read for cursors (#473)

* Add dual read for cursors

* Fix loadAndCompareAll to batch load cursors

* fix javadocs
This commit is contained in:
sarahcaseybot 2020-02-19 16:10:19 -05:00 committed by GitHub
parent be395611ca
commit f53aa8d55e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 323 additions and 50 deletions

View file

@ -19,6 +19,8 @@ import static google.registry.model.common.Cursor.CursorType.BRDA;
import static google.registry.model.common.Cursor.CursorType.RDE_UPLOAD;
import static google.registry.model.common.Cursor.CursorType.RECURRING_BILLING;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.schema.cursor.Cursor.GLOBAL;
import static google.registry.schema.cursor.CursorDao.loadAndCompare;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
@ -27,19 +29,30 @@ import static org.junit.Assert.assertThrows;
import google.registry.model.EntityTestCase;
import google.registry.model.domain.DomainBase;
import google.registry.model.registry.Registry;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule;
import google.registry.schema.cursor.CursorDao;
import google.registry.testing.FakeClock;
import org.joda.time.DateTime;
import org.junit.Rule;
import org.junit.Test;
/** Unit tests for {@link Cursor}. */
public class CursorTest extends EntityTestCase {
private final FakeClock fakeClock = new FakeClock(DateTime.parse("2010-10-17TZ"));
@Rule
public final JpaIntegrationWithCoverageRule jpaRule =
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageRule();
@Test
public void testSuccess_persistScopedCursor() {
createTld("tld");
clock.advanceOneMilli();
final DateTime time = DateTime.parse("2012-07-12T03:30:00.000Z");
CursorDao.saveCursor(Cursor.create(RDE_UPLOAD, time, Registry.get("tld")), "tld");
Cursor cursor = Cursor.create(RDE_UPLOAD, time, Registry.get("tld"));
CursorDao.saveCursor(cursor, "tld");
assertThat(ofy().load().key(Cursor.createKey(BRDA, Registry.get("tld"))).now()).isNull();
assertThat(
ofy()
@ -48,23 +61,24 @@ public class CursorTest extends EntityTestCase {
.now()
.getCursorTime())
.isEqualTo(time);
loadAndCompare(cursor, "tld");
}
@Test
public void testSuccess_persistGlobalCursor() {
final DateTime time = DateTime.parse("2012-07-12T03:30:00.000Z");
CursorDao.saveCursor(
Cursor.createGlobal(RECURRING_BILLING, time), google.registry.schema.cursor.Cursor.GLOBAL);
CursorDao.saveCursor(Cursor.createGlobal(RECURRING_BILLING, time), GLOBAL);
assertThat(ofy().load().key(Cursor.createGlobalKey(RECURRING_BILLING)).now().getCursorTime())
.isEqualTo(time);
loadAndCompare(Cursor.createGlobal(RECURRING_BILLING, time), GLOBAL);
}
@Test
public void testIndexing() throws Exception {
final DateTime time = DateTime.parse("2012-07-12T03:30:00.000Z");
CursorDao.saveCursor(
Cursor.createGlobal(RECURRING_BILLING, time), google.registry.schema.cursor.Cursor.GLOBAL);
CursorDao.saveCursor(Cursor.createGlobal(RECURRING_BILLING, time), GLOBAL);
Cursor cursor = ofy().load().key(Cursor.createGlobalKey(RECURRING_BILLING)).now();
loadAndCompare(cursor, GLOBAL);
verifyIndexing(cursor);
}

View file

@ -259,4 +259,178 @@ public class CursorDaoTest {
assertThat(createdCursor3.getCursorTime()).isEqualTo(cursor3.getCursorTime());
assertThat(cursor3).isEqualTo(dataStoreCursor3);
}
@Test
public void loadAndCompare_worksSuccessfully() {
loggerToIntercept.addHandler(logHandler);
createTld("tld");
google.registry.model.common.Cursor cursor =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc(), Registry.get("tld"));
CursorDao.saveCursor(cursor, "tld");
CursorDao.loadAndCompare(cursor, "tld");
assertAboutLogs().that(logHandler).hasNoLogsAtLevel(Level.WARNING);
}
@Test
public void loadAndCompare_worksSuccessfullyGlobalCursor() {
loggerToIntercept.addHandler(logHandler);
google.registry.model.common.Cursor cursor =
google.registry.model.common.Cursor.createGlobal(
CursorType.RECURRING_BILLING, fakeClock.nowUtc());
CursorDao.saveCursor(cursor, Cursor.GLOBAL);
CursorDao.loadAndCompare(cursor, Cursor.GLOBAL);
assertAboutLogs().that(logHandler).hasNoLogsAtLevel(Level.WARNING);
}
@Test
public void loadAndCompare_worksSuccessfullyCursorNotInCloudSql() {
loggerToIntercept.addHandler(logHandler);
createTld("tld");
google.registry.model.common.Cursor cursor =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc(), Registry.get("tld"));
CursorDao.loadAndCompare(cursor, "tld");
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.WARNING,
"Cursor of type ICANN_UPLOAD_ACTIVITY with the scope tld was not found in Cloud SQL.");
}
@Test
public void loadAndCompare_worksSuccessfullyGlobalCursorNotInCloudSql() {
loggerToIntercept.addHandler(logHandler);
google.registry.model.common.Cursor cursor =
google.registry.model.common.Cursor.createGlobal(
CursorType.RECURRING_BILLING, fakeClock.nowUtc());
CursorDao.loadAndCompare(cursor, Cursor.GLOBAL);
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.WARNING,
"Cursor of type RECURRING_BILLING with the scope GLOBAL was not found in Cloud SQL.");
}
@Test
public void loadAndCompare_worksSuccessfullyCursorsNotEqual() {
loggerToIntercept.addHandler(logHandler);
createTld("tld");
google.registry.model.common.Cursor cursor =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc(), Registry.get("tld"));
CursorDao.saveCursor(cursor, "tld");
cursor =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc().minusDays(5), Registry.get("tld"));
CursorDao.loadAndCompare(cursor, "tld");
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.WARNING,
"This cursor of type ICANN_UPLOAD_ACTIVITY with the scope tld has a cursorTime of"
+ " 1969-12-27T00:00:00.000Z in Datastore and 1970-01-01T00:00:00.000Z in Cloud"
+ " SQL.");
}
@Test
public void loadAndCompareAll_worksSuccessfully() {
loggerToIntercept.addHandler(logHandler);
// Create Datastore cursors
createTlds("tld", "foo");
google.registry.model.common.Cursor cursor1 =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc(), Registry.get("tld"));
google.registry.model.common.Cursor cursor2 =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc(), Registry.get("foo"));
// Save cursors to Cloud SQL
ImmutableMap<google.registry.model.common.Cursor, String> cursors =
ImmutableMap.<google.registry.model.common.Cursor, String>builder()
.put(cursor1, "tld")
.put(cursor2, "foo")
.build();
CursorDao.saveCursors(cursors);
CursorDao.loadAndCompareAll(cursors, CursorType.ICANN_UPLOAD_ACTIVITY);
assertAboutLogs().that(logHandler).hasNoLogsAtLevel(Level.WARNING);
}
@Test
public void loadAndCompareAll_worksSuccessfullyMissingOne() {
loggerToIntercept.addHandler(logHandler);
// Create Datastore cursors
createTlds("tld", "foo", "lol");
google.registry.model.common.Cursor cursor1 =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc(), Registry.get("tld"));
google.registry.model.common.Cursor cursor2 =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc(), Registry.get("foo"));
// Save Cursors to Cloud SQL
ImmutableMap.Builder<google.registry.model.common.Cursor, String> cursors =
ImmutableMap.<google.registry.model.common.Cursor, String>builder()
.put(cursor1, "tld")
.put(cursor2, "foo");
CursorDao.saveCursors(cursors.build());
// Create a new Datastore cursor that is not saved to Cloud SQL
google.registry.model.common.Cursor cursor3 =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc().minusDays(4), Registry.get("lol"));
// Call loadAndCompareAll with all three Datastore cursors
CursorDao.loadAndCompareAll(
cursors.put(cursor3, "lol").build(), CursorType.ICANN_UPLOAD_ACTIVITY);
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.WARNING,
"Cursor of type ICANN_UPLOAD_ACTIVITY with the scope lol was not found in Cloud SQL.");
}
@Test
public void loadAndCompareAll_worksSuccessfullyOneWithWrongTime() {
loggerToIntercept.addHandler(logHandler);
// Create Datastore cursors
createTlds("tld", "foo");
google.registry.model.common.Cursor cursor1 =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc(), Registry.get("tld"));
google.registry.model.common.Cursor cursor2 =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc(), Registry.get("foo"));
// Save Cursors to Cloud SQL
CursorDao.saveCursors(ImmutableMap.of(cursor1, "tld", cursor2, "foo"));
// Change time of first Datastore cursor, but don't save new time to Cloud SQL
google.registry.model.common.Cursor cursor3 =
google.registry.model.common.Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, fakeClock.nowUtc().minusDays(4), Registry.get("tld"));
CursorDao.loadAndCompareAll(
ImmutableMap.of(cursor2, "foo", cursor3, "tld"), CursorType.ICANN_UPLOAD_ACTIVITY);
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.WARNING,
"This cursor of type ICANN_UPLOAD_ACTIVITY with the scope tld has a cursorTime of"
+ " 1969-12-28T00:00:00.000Z in Datastore and 1970-01-01T00:00:00.000Z in Cloud"
+ " SQL.");
}
@Test
public void loadAndCompareAll_worksSuccessfullyEmptyMap() {
loggerToIntercept.addHandler(logHandler);
CursorDao.loadAndCompareAll(ImmutableMap.of(), CursorType.ICANN_UPLOAD_ACTIVITY);
assertAboutLogs().that(logHandler).hasNoLogsAtLevel(Level.WARNING);
}
}

View file

@ -15,6 +15,7 @@
package google.registry.schema.integration;
import com.google.common.truth.Expect;
import google.registry.model.common.CursorTest;
import google.registry.model.domain.DomainBaseSqlTest;
import google.registry.model.registry.RegistryLockDaoTest;
import google.registry.persistence.transaction.JpaEntityCoverage;
@ -60,6 +61,7 @@ import org.junit.runners.Suite.SuiteClasses;
CreateRegistrarCommandTest.class,
CreateReservedListCommandTest.class,
CursorDaoTest.class,
CursorTest.class,
DomainLockUtilsTest.class,
LockDaoTest.class,
LockDomainCommandTest.class,