Remove ofy support from auto timestamp classes (#1708)

Also remove the use of ZonedDateTime, as joda DateTime can already be persisted to SQL with an existing converted.
This commit is contained in:
Lai Jiang
2022-07-26 09:51:35 -04:00
committed by GitHub
parent 12905c1c1f
commit 1ca80010c0
22 changed files with 100 additions and 512 deletions
@@ -28,16 +28,16 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.stream.Collectors;
import org.apache.avro.generic.GenericRecord;
import org.apache.beam.sdk.coders.AtomicCoder;
import org.apache.beam.sdk.coders.Coder;
import org.apache.beam.sdk.coders.StringUtf8Coder;
import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
* A POJO representing a single billable event, parsed from a {@code SchemaAndRecord}.
@@ -51,7 +51,7 @@ import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord;
public abstract class BillingEvent implements Serializable {
private static final DateTimeFormatter DATE_TIME_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss zzz");
DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss zzz");
/** The amount we multiply the price for sunrise creates. This is currently a 15% discount. */
private static final double SUNRISE_DISCOUNT_PRICE_MODIFIER = 0.85;
@@ -77,10 +77,10 @@ public abstract class BillingEvent implements Serializable {
abstract long id();
/** Returns the UTC DateTime this event becomes billable. */
abstract ZonedDateTime billingTime();
abstract DateTime billingTime();
/** Returns the UTC DateTime this event was generated. */
abstract ZonedDateTime eventTime();
abstract DateTime eventTime();
/** Returns the billed registrar's name. */
abstract String registrarId();
@@ -132,10 +132,8 @@ public abstract class BillingEvent implements Serializable {
// Objects, which contain a string representation of their underlying types.
Long.parseLong(extractField(record, "id")),
// Bigquery provides UNIX timestamps with microsecond precision.
Instant.ofEpochMilli(Long.parseLong(extractField(record, "billingTime")) / 1000)
.atZone(ZoneId.of("UTC")),
Instant.ofEpochMilli(Long.parseLong(extractField(record, "eventTime")) / 1000)
.atZone(ZoneId.of("UTC")),
new DateTime(Long.parseLong(extractField(record, "billingTime")) / 1000, DateTimeZone.UTC),
new DateTime(Long.parseLong(extractField(record, "eventTime")) / 1000, DateTimeZone.UTC),
extractField(record, "registrarId"),
extractField(record, "billingId"),
extractField(record, "poNumber"),
@@ -180,8 +178,8 @@ public abstract class BillingEvent implements Serializable {
@VisibleForTesting
static BillingEvent create(
long id,
ZonedDateTime billingTime,
ZonedDateTime eventTime,
DateTime billingTime,
DateTime eventTime,
String registrarId,
String billingId,
String poNumber,
@@ -231,8 +229,8 @@ public abstract class BillingEvent implements Serializable {
.join(
ImmutableList.of(
id(),
DATE_TIME_FORMATTER.format(billingTime()),
DATE_TIME_FORMATTER.format(eventTime()),
DATE_TIME_FORMATTER.print(billingTime()),
DATE_TIME_FORMATTER.print(eventTime()),
registrarId(),
billingId(),
poNumber(),
@@ -27,14 +27,12 @@ import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.reporting.billing.BillingModule;
import google.registry.util.DateTimeUtils;
import google.registry.util.DomainNameUtils;
import google.registry.util.SqlTemplate;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.Optional;
@@ -123,8 +121,8 @@ public class InvoicingPipeline implements Serializable {
return Optional.of(
BillingEvent.create(
oneTime.getId(),
DateTimeUtils.toZonedDateTime(oneTime.getBillingTime(), ZoneId.of("UTC")),
DateTimeUtils.toZonedDateTime(oneTime.getEventTime(), ZoneId.of("UTC")),
oneTime.getBillingTime(),
oneTime.getEventTime(),
registrar.getRegistrarId(),
registrar.getBillingAccountMap().get(currency),
registrar.getPoNumber().orElse(""),
@@ -14,6 +14,7 @@
package google.registry.model;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.util.PreconditionsUtils;
import javax.persistence.Access;
import javax.persistence.AccessType;
@@ -44,6 +45,7 @@ public abstract class BackupGroupRoot extends ImmutableObject implements UnsafeS
// require an unnecessary non-private setter method.
@Access(AccessType.FIELD)
@AttributeOverride(name = "lastUpdateTime", column = @Column(name = "updateTimestamp"))
@Ignore
UpdateAutoTimestamp updateTimestamp = UpdateAutoTimestamp.create(null);
/** Get the {@link UpdateAutoTimestamp} for this entity. */
@@ -10,73 +10,43 @@
// 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.
// limitations under the Licenseschema..
package google.registry.model;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.OnLoad;
import google.registry.model.translators.CreateAutoTimestampTranslatorFactory;
import google.registry.util.DateTimeUtils;
import java.time.ZonedDateTime;
import javax.annotation.Nullable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Transient;
import org.joda.time.DateTime;
/**
* A timestamp that auto-updates when first saved to Datastore.
*
* @see CreateAutoTimestampTranslatorFactory
*/
/** A timestamp that auto-updates when first saved to Datastore. */
@Embeddable
public class CreateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
@Transient DateTime timestamp;
@Column(nullable = false)
@Ignore
ZonedDateTime creationTime;
DateTime creationTime;
@PrePersist
@PreUpdate
void setTimestamp() {
if (creationTime == null) {
timestamp = jpaTm().getTransactionTime();
creationTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@OnLoad
void onLoad() {
if (timestamp != null) {
creationTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@PostLoad
void postLoad() {
if (creationTime != null) {
timestamp = DateTimeUtils.toJodaDateTime(creationTime);
creationTime = jpaTm().getTransactionTime();
}
}
/** Returns the timestamp. */
@Nullable
public DateTime getTimestamp() {
return timestamp;
return creationTime;
}
public static CreateAutoTimestamp create(@Nullable DateTime timestamp) {
public static CreateAutoTimestamp create(@Nullable DateTime creationTime) {
CreateAutoTimestamp instance = new CreateAutoTimestamp();
instance.timestamp = timestamp;
instance.creationTime = (timestamp == null) ? null : DateTimeUtils.toZonedDateTime(timestamp);
instance.creationTime = creationTime;
return instance;
}
}
@@ -32,6 +32,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import google.registry.config.RegistryConfig;
import google.registry.model.CacheUtils.AppEngineEnvironmentCacheLoader;
@@ -111,7 +112,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
* resource fields.
*/
@AttributeOverrides(@AttributeOverride(name = "creationTime", column = @Column))
@Index
@Ignore
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/**
@@ -59,8 +59,7 @@ public final class SchemaVersion {
* types (for classes), or else a list of all possible values (for enums).
*/
public static String getSchema() {
return getAllPersistedTypes()
.stream()
return getAllPersistedTypes().stream()
.filter(or(subtypeOf(Enum.class), subtypeOf(ImmutableObject.class)))
.map(ModelUtils::getSchema)
.collect(joining("\n"));
@@ -17,23 +17,15 @@ package google.registry.model;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.OnLoad;
import google.registry.util.DateTimeUtils;
import java.time.ZonedDateTime;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Transient;
import org.joda.time.DateTime;
/**
* A timestamp that auto-updates on each save to Datastore/Cloud SQL.
*/
/** A timestamp that auto-updates on each save to Datastore/Cloud SQL. */
@Embeddable
public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
@@ -42,11 +34,8 @@ public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerial
// during a replay).
private static final ThreadLocal<Boolean> autoUpdateEnabled = ThreadLocal.withInitial(() -> true);
@Transient DateTime timestamp;
@Ignore
@Column(nullable = false)
ZonedDateTime lastUpdateTime;
DateTime lastUpdateTime;
// Unfortunately, we cannot use the @UpdateTimestamp annotation on "lastUpdateTime" in this class
// because Hibernate does not allow it to be used on @Embeddable classes, see
@@ -55,34 +44,18 @@ public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerial
@PreUpdate
void setTimestamp() {
if (autoUpdateEnabled() || lastUpdateTime == null) {
timestamp = jpaTm().getTransactionTime();
lastUpdateTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@OnLoad
void onLoad() {
if (timestamp != null) {
lastUpdateTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@PostLoad
void postLoad() {
if (lastUpdateTime != null) {
timestamp = DateTimeUtils.toJodaDateTime(lastUpdateTime);
lastUpdateTime = jpaTm().getTransactionTime();
}
}
/** Returns the timestamp, or {@code START_OF_TIME} if it's null. */
public DateTime getTimestamp() {
return Optional.ofNullable(timestamp).orElse(START_OF_TIME);
return Optional.ofNullable(lastUpdateTime).orElse(START_OF_TIME);
}
public static UpdateAutoTimestamp create(@Nullable DateTime timestamp) {
UpdateAutoTimestamp instance = new UpdateAutoTimestamp();
instance.timestamp = timestamp;
instance.lastUpdateTime = timestamp == null ? null : DateTimeUtils.toZonedDateTime(timestamp);
instance.lastUpdateTime = timestamp;
return instance;
}
@@ -16,15 +16,12 @@ package google.registry.model.domain;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.toZonedDateTime;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.Buildable;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject;
import google.registry.model.UpdateAutoTimestamp;
import google.registry.util.DateTimeUtils;
import java.time.ZonedDateTime;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.persistence.AttributeOverride;
@@ -109,19 +106,19 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
private CreateAutoTimestamp lockRequestTime = CreateAutoTimestamp.create(null);
/** When the unlock is first requested. */
private ZonedDateTime unlockRequestTime;
private DateTime unlockRequestTime;
/**
* When the user has verified the lock. If this field is null, it means the lock has not been
* verified yet (and thus not been put into effect).
*/
private ZonedDateTime lockCompletionTime;
private DateTime lockCompletionTime;
/**
* When the user has verified the unlock of this lock. If this field is null, it means the unlock
* action has not been verified yet (and has not been put into effect).
*/
private ZonedDateTime unlockCompletionTime;
private DateTime unlockCompletionTime;
/** The user must provide the random verification code in order to complete the action. */
@Column(nullable = false)
@@ -168,19 +165,19 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
/** Returns the unlock request timestamp or null if an unlock has not been requested yet. */
public Optional<DateTime> getUnlockRequestTime() {
return Optional.ofNullable(unlockRequestTime).map(DateTimeUtils::toJodaDateTime);
return Optional.ofNullable(unlockRequestTime);
}
/** Returns the completion timestamp, or empty if this lock has not been completed yet. */
public Optional<DateTime> getLockCompletionTime() {
return Optional.ofNullable(lockCompletionTime).map(DateTimeUtils::toJodaDateTime);
return Optional.ofNullable(lockCompletionTime);
}
/**
* Returns the unlock completion timestamp, or empty if this unlock has not been completed yet.
*/
public Optional<DateTime> getUnlockCompletionTime() {
return Optional.ofNullable(unlockCompletionTime).map(DateTimeUtils::toJodaDateTime);
return Optional.ofNullable(unlockCompletionTime);
}
public String getVerificationCode() {
@@ -278,17 +275,17 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
}
public Builder setUnlockRequestTime(DateTime unlockRequestTime) {
getInstance().unlockRequestTime = toZonedDateTime(unlockRequestTime);
getInstance().unlockRequestTime = unlockRequestTime;
return this;
}
public Builder setLockCompletionTime(DateTime lockCompletionTime) {
getInstance().lockCompletionTime = toZonedDateTime(lockCompletionTime);
getInstance().lockCompletionTime = lockCompletionTime;
return this;
}
public Builder setUnlockCompletionTime(DateTime unlockCompletionTime) {
getInstance().unlockCompletionTime = toZonedDateTime(unlockCompletionTime);
getInstance().unlockCompletionTime = unlockCompletionTime;
return this;
}
@@ -33,6 +33,7 @@ import com.google.common.collect.Range;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.OnLoad;
import google.registry.flows.EppException;
@@ -138,7 +139,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
@Nullable @Index String domainName;
/** When this token was created. */
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
@Ignore CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** Allowed registrar client IDs for this token, or null if all registrars are allowed. */
@Column(name = "allowedRegistrarIds")
@@ -38,7 +38,6 @@ import google.registry.model.ImmutableObject;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.translators.BloomFilterOfStringTranslatorFactory;
import google.registry.model.translators.CidrAddressBlockTranslatorFactory;
import google.registry.model.translators.CreateAutoTimestampTranslatorFactory;
import google.registry.model.translators.CurrencyUnitTranslatorFactory;
import google.registry.model.translators.DurationTranslatorFactory;
import google.registry.model.translators.EppHistoryVKeyTranslatorFactory;
@@ -122,7 +121,6 @@ public class ObjectifyService {
ImmutableList.of(
new BloomFilterOfStringTranslatorFactory(),
new CidrAddressBlockTranslatorFactory(),
new CreateAutoTimestampTranslatorFactory(),
new CurrencyUnitTranslatorFactory(),
new DurationTranslatorFactory(),
new EppHistoryVKeyTranslatorFactory(),
@@ -58,6 +58,7 @@ import com.google.re2j.Pattern;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;
@@ -407,10 +408,10 @@ public class Registrar extends ImmutableObject
// Metadata.
/** The time when this registrar was created. */
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
@Ignore CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** An automatically managed last-saved timestamp. */
UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
@Ignore UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
/** The time that the certificate was last updated. */
DateTime lastCertificateUpdateTime;
@@ -1,58 +0,0 @@
// Copyright 2017 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.model.translators;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static org.joda.time.DateTimeZone.UTC;
import google.registry.model.CreateAutoTimestamp;
import java.util.Date;
import org.joda.time.DateTime;
/** Saves {@link CreateAutoTimestamp} as either its value, or the current time if it was null. */
public class CreateAutoTimestampTranslatorFactory
extends AbstractSimpleTranslatorFactory<CreateAutoTimestamp, Date> {
public CreateAutoTimestampTranslatorFactory() {
super(CreateAutoTimestamp.class);
}
@Override
SimpleTranslator<CreateAutoTimestamp, Date> createTranslator() {
return new SimpleTranslator<CreateAutoTimestamp, Date>() {
/**
* Load an existing timestamp. It can be assumed to be non-null since if the field is null in
* Datastore then Objectify will skip this translator and directly load a null.
*/
@Override
public CreateAutoTimestamp loadValue(Date datastoreValue) {
return CreateAutoTimestamp.create(new DateTime(datastoreValue, UTC));
}
/** Save a timestamp, setting it to the current time if it did not have a previous value. */
@Override
public Date saveValue(CreateAutoTimestamp pojoValue) {
// Note that we use the current transaction manager -- we need to do this under JPA when we
// serialize the entity from a Transaction object, but we need to use the JPA transaction
// manager in that case.
return (pojoValue.getTimestamp() == null
? tm().getTransactionTime()
: pojoValue.getTimestamp())
.toDate();
}
};
}
}
@@ -1,48 +0,0 @@
// Copyright 2019 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.persistence.converter;
import java.sql.Timestamp;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import javax.annotation.Nullable;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/**
* JPA converter to for storing/retrieving {@link ZonedDateTime} objects.
*
* <p>Hibernate provides a default converter for {@link ZonedDateTime}, but it converts timestamp to
* a non-normalized format, e.g., 2019-09-01T01:01:01Z will be converted to
* 2019-09-01T01:01:01Z[UTC]. This converter solves that problem by explicitly calling {@link
* ZoneId#normalized()} to normalize the zone id.
*/
@Converter(autoApply = true)
public class ZonedDateTimeConverter implements AttributeConverter<ZonedDateTime, Timestamp> {
@Override
@Nullable
public Timestamp convertToDatabaseColumn(@Nullable ZonedDateTime attribute) {
return attribute == null ? null : Timestamp.from(attribute.toInstant());
}
@Override
@Nullable
public ZonedDateTime convertToEntityAttribute(@Nullable Timestamp dbData) {
return dbData == null
? null
: ZonedDateTime.ofInstant(dbData.toInstant(), ZoneId.of("UTC").normalized());
}
}
@@ -96,7 +96,6 @@
<class>google.registry.persistence.converter.StringSetConverter</class>
<class>google.registry.persistence.converter.TldStateTransitionConverter</class>
<class>google.registry.persistence.converter.TransferServerApproveEntitySetConverter</class>
<class>google.registry.persistence.converter.ZonedDateTimeConverter</class>
<!-- Generated converters for VKey -->
<class>google.registry.model.billing.VKeyConverter_Cancellation</class>