Add caches to ClaimsListDao and ClaimsList (#1731)

We cache the ClaimsList Java object for six hours (we don't expect it to
change frequently, and the cron job to update it only runs every twelve
hours). Subsequent calls to ClaimsListDao::get will return the cached
value.

Within the ClaimsList Java object itself, we cache any labels that we
have retrieved. While we already have a form of a cache here in the
"labelsToKeys" map, that only handles situations where we've loaded the
entire map from the database. We want to have a non-guaranteed cache in
order to make repeated calls to getClaimKey fast.
This commit is contained in:
gbrodman
2022-08-05 17:29:59 -04:00
committed by GitHub
parent 9ff25f9a67
commit 632e3831e5
12 changed files with 154 additions and 18 deletions
@@ -41,13 +41,20 @@ import google.registry.flows.exceptions.TooManyResourceChecksException;
import google.registry.model.domain.Domain;
import google.registry.model.tld.Registry;
import google.registry.model.tld.Registry.TldState;
import google.registry.testing.TestCacheExtension;
import java.time.Duration;
import org.joda.money.Money;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link DomainClaimsCheckFlow}. */
public class DomainClaimsCheckFlowTest extends ResourceFlowTestCase<DomainClaimsCheckFlow, Domain> {
@RegisterExtension
public final TestCacheExtension testCacheExtension =
new TestCacheExtension.Builder().withClaimsListCache(Duration.ofHours(6)).build();
DomainClaimsCheckFlowTest() {
setEppInput("domain_check_claims.xml");
}
@@ -15,12 +15,16 @@
package google.registry.model.tmch;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableMap;
import com.google.common.truth.Truth8;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.TestCacheExtension;
import java.time.Duration;
import javax.persistence.PersistenceException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -37,6 +41,11 @@ public class ClaimsListDaoTest {
.withoutCannedData()
.buildIntegrationWithCoverageExtension();
// Set long persist times on the cache so it can be tested (cache times default to 0 in tests).
@RegisterExtension
public final TestCacheExtension testCacheExtension =
new TestCacheExtension.Builder().withClaimsListCache(Duration.ofHours(6)).build();
@Test
void save_insertsClaimsListSuccessfully() {
ClaimsList claimsList =
@@ -83,6 +92,40 @@ public class ClaimsListDaoTest {
assertClaimsListEquals(newClaimsList, ClaimsListDao.get());
}
@Test
void testDaoCaching_savesAndUpdates() {
assertThat(ClaimsListDao.CACHE.getIfPresent(ClaimsListDao.class)).isNull();
ClaimsList oldList =
ClaimsList.create(fakeClock.nowUtc(), ImmutableMap.of("label1", "key1", "label2", "key2"));
ClaimsListDao.save(oldList);
assertThat(ClaimsListDao.CACHE.getIfPresent(ClaimsListDao.class)).isEqualTo(oldList);
ClaimsList newList =
ClaimsList.create(fakeClock.nowUtc(), ImmutableMap.of("label3", "key3", "label4", "key4"));
ClaimsListDao.save(newList);
assertThat(ClaimsListDao.CACHE.getIfPresent(ClaimsListDao.class)).isEqualTo(newList);
}
@Test
void testEntryCaching_savesAndUpdates() {
ClaimsList claimsList =
ClaimsList.create(fakeClock.nowUtc(), ImmutableMap.of("label1", "key1", "label2", "key2"));
// Bypass the DAO to avoid the cache
jpaTm().transact(() -> jpaTm().insert(claimsList));
ClaimsList fromDatabase = ClaimsListDao.get();
// At first, we haven't loaded any entries
assertThat(fromDatabase.claimKeyCache.getIfPresent("label1")).isNull();
Truth8.assertThat(fromDatabase.getClaimKey("label1")).hasValue("key1");
// After retrieval, the key exists
Truth8.assertThat(fromDatabase.claimKeyCache.getIfPresent("label1")).hasValue("key1");
assertThat(fromDatabase.claimKeyCache.getIfPresent("label2")).isNull();
// Loading labels-to-keys should still work
assertThat(fromDatabase.getLabelsToKeys()).containsExactly("label1", "key1", "label2", "key2");
// We should also cache nonexistent values
assertThat(fromDatabase.claimKeyCache.getIfPresent("nonexistent")).isNull();
Truth8.assertThat(fromDatabase.getClaimKey("nonexistent")).isEmpty();
Truth8.assertThat(fromDatabase.claimKeyCache.getIfPresent("nonexistent")).isEmpty();
}
private void assertClaimsListEquals(ClaimsList left, ClaimsList right) {
assertThat(left.getRevisionId()).isEqualTo(right.getRevisionId());
assertThat(left.getTmdbGenerationTime()).isEqualTo(right.getTmdbGenerationTime());
@@ -19,6 +19,7 @@ import com.google.common.collect.Maps;
import google.registry.model.EppResource;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.tld.label.PremiumListDao;
import google.registry.model.tmch.ClaimsListDao;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;
@@ -75,6 +76,12 @@ public class TestCacheExtension implements BeforeEachCallback, AfterEachCallback
return this;
}
public Builder withClaimsListCache(Duration expiry) {
cacheHandlerMap.put(
"ClaimsListDao.CACHE", new TestCacheHandler(ClaimsListDao::setCacheForTest, expiry));
return this;
}
public TestCacheExtension build() {
return new TestCacheExtension(ImmutableList.copyOf(cacheHandlerMap.values()));
}
@@ -29,7 +29,9 @@ import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.reporting.HistoryEntry.Type;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.TestCacheExtension;
import google.registry.util.Clock;
import java.time.Duration;
import java.util.List;
import org.joda.money.Money;
import org.joda.time.DateTime;
@@ -44,6 +46,10 @@ class EppLifecycleToolsTest extends EppTestCase {
final AppEngineExtension appEngine =
AppEngineExtension.builder().withClock(clock).withCloudSql().withTaskQueue().build();
@RegisterExtension
public final TestCacheExtension testCacheExtension =
new TestCacheExtension.Builder().withClaimsListCache(Duration.ofHours(6)).build();
@BeforeEach
void beforeEach() {
createTlds("example", "tld");