Unwrap the return value of loadAtPointInTime (#1205)

In SQL we do not need to wrap it in a Result. Unfortunately we cannot
overload a function based on its return value so we renamed the existing
one and created a new one with the old name that returns the resource
directly. Once we no longer have use of Datastore we can delete the now
renamed function that returns a Result<? extends EppResource>

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1205)
<!-- Reviewable:end -->
This commit is contained in:
Lai Jiang 2021-06-14 11:55:24 -04:00 committed by GitHub
parent df6513ef06
commit 3f01a90647
5 changed files with 87 additions and 83 deletions

View file

@ -29,8 +29,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import com.googlecode.objectify.Result;
import com.googlecode.objectify.util.ResultNow;
import google.registry.config.RegistryConfig; import google.registry.config.RegistryConfig;
import google.registry.model.EppResource.BuilderWithTransferData; import google.registry.model.EppResource.BuilderWithTransferData;
import google.registry.model.EppResource.ForeignKeyedEppResource; import google.registry.model.EppResource.ForeignKeyedEppResource;
@ -54,6 +52,7 @@ import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.Query; import javax.persistence.Query;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -299,43 +298,51 @@ public final class EppResourceUtils {
* <p>When using the SQL backend (post-Registry-3.0-migration) this restriction goes away and * <p>When using the SQL backend (post-Registry-3.0-migration) this restriction goes away and
* objects can be restored to any revision. * objects can be restored to any revision.
* *
* <p>TODO(b/177567432): Once Datastore is completely removed, remove the Result wrapping. * @return the resource at {@code timestamp} or {@code null} if resource is deleted or not yet
* * created
* @return an asynchronous operation returning resource at {@code timestamp} or {@code null} if
* resource is deleted or not yet created
*/ */
public static <T extends EppResource> Result<T> loadAtPointInTime( public static <T extends EppResource> T loadAtPointInTime(
final T resource, final DateTime timestamp) { final T resource, final DateTime timestamp) {
// If we're before the resource creation time, don't try to find a "most recent revision". // If we're before the resource creation time, don't try to find a "most recent revision".
if (timestamp.isBefore(resource.getCreationTime())) { if (timestamp.isBefore(resource.getCreationTime())) {
return new ResultNow<>(null); return null;
} }
// If the resource was not modified after the requested time, then use it as-is, otherwise find // If the resource was not modified after the requested time, then use it as-is, otherwise find
// the most recent revision asynchronously, and return an async result that wraps that revision // the most recent revision and project it forward to exactly the desired timestamp, or null if
// and returns it projected forward to exactly the desired timestamp, or null if the resource is // the resource is deleted at that timestamp.
// deleted at that timestamp. T loadedResource =
final Result<T> loadResult =
isAtOrAfter(timestamp, resource.getUpdateTimestamp().getTimestamp()) isAtOrAfter(timestamp, resource.getUpdateTimestamp().getTimestamp())
? new ResultNow<>(resource) ? resource
: loadMostRecentRevisionAtTime(resource, timestamp); : loadMostRecentRevisionAtTime(resource, timestamp);
return () -> { return (loadedResource == null)
T loadedResource = loadResult.now(); ? null
return (loadedResource == null) : (isActive(loadedResource, timestamp)
? null ? cloneProjectedAtTime(loadedResource, timestamp)
: (isActive(loadedResource, timestamp) : null);
? cloneProjectedAtTime(loadedResource, timestamp)
: null);
};
} }
/** /**
* Returns an asynchronous result holding the most recent revision of a given EppResource before * Rewinds an {@link EppResource} object to a given point in time.
* or at the provided timestamp, falling back to using the resource as-is if there are no *
* revisions. * <p>This method costs nothing if {@code resource} is already current. Otherwise it returns an
* async operation that performs a single fetch operation.
*
* @return an asynchronous operation returning resource at {@code timestamp} or {@code null} if
* resource is deleted or not yet created
* @see #loadAtPointInTime(EppResource, DateTime)
*/
public static <T extends EppResource> Supplier<T> loadAtPointInTimeAsync(
final T resource, final DateTime timestamp) {
return () -> loadAtPointInTime(resource, timestamp);
}
/**
* Returns the most recent revision of a given EppResource before or at the provided timestamp,
* falling back to using the resource as-is if there are no revisions.
* *
* @see #loadAtPointInTime(EppResource, DateTime) * @see #loadAtPointInTime(EppResource, DateTime)
*/ */
private static <T extends EppResource> Result<T> loadMostRecentRevisionAtTime( private static <T extends EppResource> T loadMostRecentRevisionAtTime(
final T resource, final DateTime timestamp) { final T resource, final DateTime timestamp) {
if (tm().isOfy()) { if (tm().isOfy()) {
return loadMostRecentRevisionAtTimeDatastore(resource, timestamp); return loadMostRecentRevisionAtTimeDatastore(resource, timestamp);
@ -345,46 +352,42 @@ public final class EppResourceUtils {
} }
/** /**
* Returns an asynchronous result holding the most recent Datastore revision of a given * Returns the most recent Datastore revision of a given EppResource before or at the provided
* EppResource before or at the provided timestamp using the EppResource revisions map, falling * timestamp using the EppResource revisions map, falling back to using the resource as-is if
* back to using the resource as-is if there are no revisions. * there are no revisions.
* *
* @see #loadAtPointInTime(EppResource, DateTime) * @see #loadAtPointInTimeAsync(EppResource, DateTime)
*/ */
private static <T extends EppResource> Result<T> loadMostRecentRevisionAtTimeDatastore( private static <T extends EppResource> T loadMostRecentRevisionAtTimeDatastore(
final T resource, final DateTime timestamp) { final T resource, final DateTime timestamp) {
final Key<T> resourceKey = Key.create(resource); final Key<T> resourceKey = Key.create(resource);
final Key<CommitLogManifest> revision = final Key<CommitLogManifest> revision =
findMostRecentDatastoreRevisionAtTime(resource, timestamp); findMostRecentDatastoreRevisionAtTime(resource, timestamp);
if (revision == null) { if (revision == null) {
logger.atSevere().log("No revision found for %s, falling back to resource.", resourceKey); logger.atSevere().log("No revision found for %s, falling back to resource.", resourceKey);
return new ResultNow<>(resource);
}
final Result<CommitLogMutation> mutationResult =
auditedOfy().load().key(CommitLogMutation.createKey(revision, resourceKey));
return () -> {
CommitLogMutation mutation = mutationResult.now();
if (mutation != null) {
return auditedOfy().load().fromEntity(mutation.getEntity());
}
logger.atSevere().log(
"Couldn't load mutation for revision at %s for %s, falling back to resource."
+ " Revision: %s",
timestamp, resourceKey, revision);
return resource; return resource;
}; }
final CommitLogMutation mutation =
auditedOfy().load().key(CommitLogMutation.createKey(revision, resourceKey)).now();
if (mutation != null) {
return auditedOfy().load().fromEntity(mutation.getEntity());
}
logger.atSevere().log(
"Couldn't load mutation for revision at %s for %s, falling back to resource."
+ " Revision: %s",
timestamp, resourceKey, revision);
return resource;
} }
/** /**
* Returns an asynchronous result holding the most recent SQL revision of a given EppResource * Returns the most recent SQL revision of a given EppResource before or at the provided timestamp
* before or at the provided timestamp using *History objects, falling back to using the resource * using *History objects, falling back to using the resource as-is if there are no revisions.
* as-is if there are no revisions.
* *
* @see #loadAtPointInTime(EppResource, DateTime) * @see #loadAtPointInTimeAsync(EppResource, DateTime)
*/ */
@SuppressWarnings("unchecked") private static <T extends EppResource> T loadMostRecentRevisionAtTimeSql(
private static <T extends EppResource> Result<T> loadMostRecentRevisionAtTimeSql(
T resource, DateTime timestamp) { T resource, DateTime timestamp) {
@SuppressWarnings("unchecked")
T resourceAtPointInTime = T resourceAtPointInTime =
(T) (T)
HistoryEntryDao.loadHistoryObjectsForResource( HistoryEntryDao.loadHistoryObjectsForResource(
@ -397,9 +400,9 @@ public final class EppResourceUtils {
logger.atSevere().log( logger.atSevere().log(
"Couldn't load resource at % for key %s, falling back to resource %s.", "Couldn't load resource at % for key %s, falling back to resource %s.",
timestamp, resource.createVKey(), resource); timestamp, resource.createVKey(), resource);
return new ResultNow<>(resource); return resource;
} }
return new ResultNow<>(resourceAtPointInTime); return resourceAtPointInTime;
} }
@Nullable @Nullable

View file

@ -17,6 +17,7 @@ package google.registry.rde;
import static com.google.common.base.Strings.nullToEmpty; import static com.google.common.base.Strings.nullToEmpty;
import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.model.EppResourceUtils.loadAtPointInTime; import static google.registry.model.EppResourceUtils.loadAtPointInTime;
import static google.registry.model.EppResourceUtils.loadAtPointInTimeAsync;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.appengine.tools.mapreduce.Mapper; import com.google.appengine.tools.mapreduce.Mapper;
@ -26,7 +27,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.googlecode.objectify.Result;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
@ -36,7 +36,9 @@ import google.registry.model.registrar.Registrar;
import google.registry.xml.ValidationMode; import google.registry.xml.ValidationMode;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** Mapper for {@link RdeStagingAction}. */ /** Mapper for {@link RdeStagingAction}. */
@ -123,8 +125,8 @@ public final class RdeStagingMapper extends Mapper<EppResource, PendingDeposit,
.collect(toImmutableSet()); .collect(toImmutableSet());
// Launch asynchronous fetches of point-in-time representations of resource. // Launch asynchronous fetches of point-in-time representations of resource.
ImmutableMap<DateTime, Result<EppResource>> resourceAtTimes = ImmutableMap<DateTime, Supplier<EppResource>> resourceAtTimes =
ImmutableMap.copyOf(Maps.asMap(dates, input -> loadAtPointInTime(resource, input))); ImmutableMap.copyOf(Maps.asMap(dates, input -> loadAtPointInTimeAsync(resource, input)));
// Convert resource to an XML fragment for each watermark/mode pair lazily and cache the result. // Convert resource to an XML fragment for each watermark/mode pair lazily and cache the result.
Fragmenter fragmenter = new Fragmenter(resourceAtTimes); Fragmenter fragmenter = new Fragmenter(resourceAtTimes);
@ -159,13 +161,13 @@ public final class RdeStagingMapper extends Mapper<EppResource, PendingDeposit,
/** Loading cache that turns a resource into XML for the various points in time and modes. */ /** Loading cache that turns a resource into XML for the various points in time and modes. */
private class Fragmenter { private class Fragmenter {
private final Map<WatermarkModePair, Optional<DepositFragment>> cache = new HashMap<>(); private final Map<WatermarkModePair, Optional<DepositFragment>> cache = new HashMap<>();
private final ImmutableMap<DateTime, Result<EppResource>> resourceAtTimes; private final ImmutableMap<DateTime, Supplier<EppResource>> resourceAtTimes;
long cacheHits = 0; long cacheHits = 0;
long resourcesNotFound = 0; long resourcesNotFound = 0;
long resourcesFound = 0; long resourcesFound = 0;
Fragmenter(ImmutableMap<DateTime, Result<EppResource>> resourceAtTimes) { Fragmenter(ImmutableMap<DateTime, Supplier<EppResource>> resourceAtTimes) {
this.resourceAtTimes = resourceAtTimes; this.resourceAtTimes = resourceAtTimes;
} }
@ -175,7 +177,7 @@ public final class RdeStagingMapper extends Mapper<EppResource, PendingDeposit,
cacheHits++; cacheHits++;
return result; return result;
} }
EppResource resource = resourceAtTimes.get(watermark).now(); EppResource resource = resourceAtTimes.get(watermark).get();
if (resource == null) { if (resource == null) {
result = Optional.empty(); result = Optional.empty();
cache.put(WatermarkModePair.create(watermark, RdeMode.FULL), result); cache.put(WatermarkModePair.create(watermark, RdeMode.FULL), result);
@ -202,8 +204,9 @@ public final class RdeStagingMapper extends Mapper<EppResource, PendingDeposit,
host, host,
// Note that loadAtPointInTime() does cloneProjectedAtTime(watermark) for // Note that loadAtPointInTime() does cloneProjectedAtTime(watermark) for
// us. // us.
loadAtPointInTime(tm().loadByKey(host.getSuperordinateDomain()), watermark) Objects.requireNonNull(
.now()) loadAtPointInTime(
tm().loadByKey(host.getSuperordinateDomain()), watermark)))
: marshaller.marshalExternalHost(host)); : marshaller.marshalExternalHost(host));
cache.put(WatermarkModePair.create(watermark, RdeMode.FULL), result); cache.put(WatermarkModePair.create(watermark, RdeMode.FULL), result);
cache.put(WatermarkModePair.create(watermark, RdeMode.THIN), result); cache.put(WatermarkModePair.create(watermark, RdeMode.THIN), result);

View file

@ -198,7 +198,7 @@ public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonA
private void mapDomain(DomainBase domain) { private void mapDomain(DomainBase domain) {
// Domains never change their tld, so we can check if it's from the wrong tld right away. // Domains never change their tld, so we can check if it's from the wrong tld right away.
if (tlds.contains(domain.getTld())) { if (tlds.contains(domain.getTld())) {
domain = loadAtPointInTime(domain, exportTime).now(); domain = loadAtPointInTime(domain, exportTime);
// A null means the domain was deleted (or not created) at this time. // A null means the domain was deleted (or not created) at this time.
if (domain != null && domain.shouldPublishToDns()) { if (domain != null && domain.shouldPublishToDns()) {
String stanza = domainStanza(domain, exportTime, dnsDefaultNsTtl, dnsDefaultDsTtl); String stanza = domainStanza(domain, exportTime, dnsDefaultNsTtl, dnsDefaultDsTtl);
@ -215,7 +215,7 @@ public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonA
ImmutableSet<String> subordinateHosts = domain.getSubordinateHosts(); ImmutableSet<String> subordinateHosts = domain.getSubordinateHosts();
if (!subordinateHosts.isEmpty()) { if (!subordinateHosts.isEmpty()) {
for (HostResource unprojectedHost : tm().loadByKeys(domain.getNameservers()).values()) { for (HostResource unprojectedHost : tm().loadByKeys(domain.getNameservers()).values()) {
HostResource host = loadAtPointInTime(unprojectedHost, exportTime).now(); HostResource host = loadAtPointInTime(unprojectedHost, exportTime);
// A null means the host was deleted (or not created) at this time. // A null means the host was deleted (or not created) at this time.
if ((host != null) && subordinateHosts.contains(host.getHostName())) { if ((host != null) && subordinateHosts.contains(host.getHostName())) {
String stanza = hostStanza(host, dnsDefaultATtl, domain.getTld()); String stanza = hostStanza(host, dnsDefaultATtl, domain.getTld());
@ -290,7 +290,7 @@ public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonA
domainLabel, domainLabel,
dnsDefaultNsTtl.getStandardSeconds(), dnsDefaultNsTtl.getStandardSeconds(),
// Load the nameservers at the export time in case they've been renamed or deleted. // Load the nameservers at the export time in case they've been renamed or deleted.
loadAtPointInTime(nameserver, exportTime).now().getHostName())); loadAtPointInTime(nameserver, exportTime).getHostName()));
} }
for (DelegationSignerData dsData : domain.getDsData()) { for (DelegationSignerData dsData : domain.getDsData()) {
result.append( result.append(

View file

@ -138,13 +138,13 @@ class EppPointInTimeTest {
// Creation time has millisecond granularity due to isActive() check. // Creation time has millisecond granularity due to isActive() check.
tm().clearSessionCache(); tm().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtCreate.minusMillis(1)).now()).isNull(); assertThat(loadAtPointInTime(latest, timeAtCreate.minusMillis(1))).isNull();
assertThat(loadAtPointInTime(latest, timeAtCreate).now()).isNotNull(); assertThat(loadAtPointInTime(latest, timeAtCreate)).isNotNull();
assertThat(loadAtPointInTime(latest, timeAtCreate.plusMillis(1)).now()).isNotNull(); assertThat(loadAtPointInTime(latest, timeAtCreate.plusMillis(1))).isNotNull();
tm().clearSessionCache(); tm().clearSessionCache();
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(loadAtPointInTime(latest, timeAtCreate.plusDays(1)).now()) .that(loadAtPointInTime(latest, timeAtCreate.plusDays(1)))
.hasFieldsEqualTo(domainAfterCreate); .hasFieldsEqualTo(domainAfterCreate);
tm().clearSessionCache(); tm().clearSessionCache();
@ -152,30 +152,30 @@ class EppPointInTimeTest {
// Both updates happened on the same day. Since the revisions field has day granularity in // Both updates happened on the same day. Since the revisions field has day granularity in
// Datastore, the key to the first update should have been overwritten by the second, and its // Datastore, the key to the first update should have been overwritten by the second, and its
// timestamp rolled forward. So we have to fall back to the last revision before midnight. // timestamp rolled forward. So we have to fall back to the last revision before midnight.
assertThat(loadAtPointInTime(latest, timeAtFirstUpdate).now()).isEqualTo(domainAfterCreate); assertThat(loadAtPointInTime(latest, timeAtFirstUpdate)).isEqualTo(domainAfterCreate);
} else { } else {
// In SQL, however, we are not limited by the day granularity, so when we request the object // In SQL, however, we are not limited by the day granularity, so when we request the object
// at timeAtFirstUpdate we should receive the object at that first update, even though the // at timeAtFirstUpdate we should receive the object at that first update, even though the
// second update occurred one millisecond later. // second update occurred one millisecond later.
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(loadAtPointInTime(latest, timeAtFirstUpdate).now()) .that(loadAtPointInTime(latest, timeAtFirstUpdate))
.hasFieldsEqualTo(domainAfterFirstUpdate); .hasFieldsEqualTo(domainAfterFirstUpdate);
} }
tm().clearSessionCache(); tm().clearSessionCache();
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(loadAtPointInTime(latest, timeAtSecondUpdate).now()) .that(loadAtPointInTime(latest, timeAtSecondUpdate))
.hasFieldsEqualTo(domainAfterSecondUpdate); .hasFieldsEqualTo(domainAfterSecondUpdate);
tm().clearSessionCache(); tm().clearSessionCache();
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(loadAtPointInTime(latest, timeAtSecondUpdate.plusDays(1)).now()) .that(loadAtPointInTime(latest, timeAtSecondUpdate.plusDays(1)))
.hasFieldsEqualTo(domainAfterSecondUpdate); .hasFieldsEqualTo(domainAfterSecondUpdate);
// Deletion time has millisecond granularity due to isActive() check. // Deletion time has millisecond granularity due to isActive() check.
tm().clearSessionCache(); tm().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtDelete.minusMillis(1)).now()).isNotNull(); assertThat(loadAtPointInTime(latest, timeAtDelete.minusMillis(1))).isNotNull();
assertThat(loadAtPointInTime(latest, timeAtDelete).now()).isNull(); assertThat(loadAtPointInTime(latest, timeAtDelete)).isNull();
assertThat(loadAtPointInTime(latest, timeAtDelete.plusMillis(1)).now()).isNull(); assertThat(loadAtPointInTime(latest, timeAtDelete.plusMillis(1))).isNull();
} }
} }

View file

@ -67,7 +67,7 @@ class EppResourceUtilsTest {
newHostResource("ns1.cat.tld").asBuilder() newHostResource("ns1.cat.tld").asBuilder()
.setCreationTimeForTest(clock.nowUtc()) .setCreationTimeForTest(clock.nowUtc())
.build()); .build());
assertThat(loadAtPointInTime(host, clock.nowUtc().minus(Duration.millis(1))).now()).isNull(); assertThat(loadAtPointInTime(host, clock.nowUtc().minus(Duration.millis(1)))).isNull();
} }
@TestOfyAndSql @TestOfyAndSql
@ -78,7 +78,7 @@ class EppResourceUtilsTest {
newHostResource("ns1.cat.tld").asBuilder() newHostResource("ns1.cat.tld").asBuilder()
.setCreationTimeForTest(START_OF_TIME) .setCreationTimeForTest(START_OF_TIME)
.build()); .build());
assertThat(loadAtPointInTime(host, clock.nowUtc()).now()).isEqualTo(host); assertThat(loadAtPointInTime(host, clock.nowUtc())).isEqualTo(host);
} }
@TestOfyOnly @TestOfyOnly
@ -99,8 +99,7 @@ class EppResourceUtilsTest {
.build()); .build());
// Load at the point in time just before the latest update; the floor entry of the revisions // Load at the point in time just before the latest update; the floor entry of the revisions
// map should point to the manifest for the first save, so we should get the old host. // map should point to the manifest for the first save, so we should get the old host.
assertThat(loadAtPointInTime(currentHost, clock.nowUtc().minusMillis(1)).now()) assertThat(loadAtPointInTime(currentHost, clock.nowUtc().minusMillis(1))).isEqualTo(oldHost);
.isEqualTo(oldHost);
} }
@TestOfyOnly @TestOfyOnly
@ -120,7 +119,7 @@ class EppResourceUtilsTest {
// Load at the point in time just before the latest update; the old host is not recoverable // Load at the point in time just before the latest update; the old host is not recoverable
// (revisions map link is broken, and guessing using the oldest revision map entry finds the // (revisions map link is broken, and guessing using the oldest revision map entry finds the
// same broken link), so just returns the current host. // same broken link), so just returns the current host.
assertThat(loadAtPointInTime(host, clock.nowUtc().minusMillis(1)).now()).isEqualTo(host); assertThat(loadAtPointInTime(host, clock.nowUtc().minusMillis(1))).isEqualTo(host);
} }
@TestOfyOnly @TestOfyOnly
@ -141,8 +140,7 @@ class EppResourceUtilsTest {
// Load at the point in time before the first update; there will be no floor entry for the // Load at the point in time before the first update; there will be no floor entry for the
// revisions map, so give up and return the oldest revision entry's mutation value (the old host // revisions map, so give up and return the oldest revision entry's mutation value (the old host
// data). // data).
assertThat(loadAtPointInTime(currentHost, clock.nowUtc().minusDays(2)).now()) assertThat(loadAtPointInTime(currentHost, clock.nowUtc().minusDays(2))).isEqualTo(oldHost);
.isEqualTo(oldHost);
} }
@TestOfyOnly @TestOfyOnly
@ -157,7 +155,7 @@ class EppResourceUtilsTest {
// Load at the point in time before the first save; there will be no floor entry for the // Load at the point in time before the first save; there will be no floor entry for the
// revisions map. Since the oldest revision entry is the only (i.e. current) revision, return // revisions map. Since the oldest revision entry is the only (i.e. current) revision, return
// the resource. // the resource.
assertThat(loadAtPointInTime(host, clock.nowUtc().minusMillis(1)).now()).isEqualTo(host); assertThat(loadAtPointInTime(host, clock.nowUtc().minusMillis(1))).isEqualTo(host);
} }
@TestOfyOnly @TestOfyOnly
@ -175,7 +173,7 @@ class EppResourceUtilsTest {
// Even though there is no revision, make a best effort guess to use the oldest revision. // Even though there is no revision, make a best effort guess to use the oldest revision.
assertThat( assertThat(
loadAtPointInTime(host, clock.nowUtc().minus(Duration.standardDays(32))) loadAtPointInTime(host, clock.nowUtc().minus(Duration.standardDays(32)))
.now()
.getUpdateTimestamp() .getUpdateTimestamp()
.getTimestamp()) .getTimestamp())
.isEqualTo(host.getRevisions().firstKey()); .isEqualTo(host.getRevisions().firstKey());