mirror of
https://github.com/google/nomulus
synced 2026-06-09 16:33:02 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b69d51add1 | |||
| 60d3653b46 | |||
| 80eefc6498 | |||
| 74f9f5d478 | |||
| 81b3a2fc5b |
@@ -19,7 +19,7 @@ This document outlines foundational mandates, architectural patterns, and projec
|
||||
- **CRITICAL MISTAKES TO AVOID:**
|
||||
- NEVER use `toInstant(clock.nowUtc())` or `toInstant(fakeClock.nowUtc())`. Both `Clock` and `FakeClock` have a `now()` method that natively returns a `java.time.Instant`. You MUST use `clock.now()` or `fakeClock.now()` directly.
|
||||
- NEVER double-wrap conversions like `toInstant(toDateTime(...))` or `toDateTime(toInstant(...))`.
|
||||
- NEVER mark `Instant` parameters or local variables as `final` unnecessarily, as it clutters the codebase.
|
||||
- NEVER mark method parameters or local variables as `final` unnecessarily, as it clutters the codebase. For class fields and constants, use `final` where applicable (i.e. when the field is assigned once and never mutated) to enforce and communicate immutability.
|
||||
- When using test helpers like `assertThatCommand().atTime(...)` or `ForeignKeyUtils.loadResource(...)`, ALWAYS use the `Instant` overloads. DO NOT wrap `Instant` instances in `toDateTime(...)` just to pass them to deprecated overloads.
|
||||
- **UTC Timezones:** Do not use `ZoneId.of("UTC")`. Use a statically imported `UTC` from `ZoneOffset` instead (`import static java.time.ZoneOffset.UTC;`).
|
||||
- **Millisecond Precision:** Always truncate `Instant.now()` to milliseconds (using `.truncatedTo(ChronoUnit.MILLIS)`) to maintain consistency with Joda `DateTime` and the PostgreSQL schema (which enforces millisecond precision via JPA converters).
|
||||
@@ -49,7 +49,9 @@ This document outlines foundational mandates, architectural patterns, and projec
|
||||
- **Utility/Cache Methods:** Use `tm().reTransact(...)` for utility methods or Caffeine cache loaders that might be invoked from both transactional and non-transactional paths.
|
||||
- `reTransact` will join an existing transaction if one is present (acting as a no-op) or start a new one if not.
|
||||
- This is particularly useful for in-memory caches where the loader must be able to fetch data regardless of whether the caller is currently in a transaction.
|
||||
- **Transactional Time:** Ensure code that relies on `tm().getTransactionTime()` is executed within a transaction context.
|
||||
- **Test Helpers & Timestamps:** If a static test helper method (like in `DatabaseHelper`) needs the database transaction time but might be called from outside a transaction, using `tm().reTransact(tm()::getTxTime)` is acceptable. However, NEVER wrap it redundantly like `tm().transact(() -> tm().reTransact(tm()::getTxTime))`. If you are just setting an arbitrary timestamp in a test where the exact DB transaction time isn't strictly required, prefer `Instant.now()` or `clock.now()` to avoid creating unnecessary database transactions.
|
||||
- **Production Code:** In production code, if a flow fails because it is calling `getTxTime()` outside of a transaction, you must wrap the *caller* in a transaction instead of adding an unnecessary `reTransact()` around `getTxTime()`.
|
||||
- **Transactional Time:** Ensure code that relies on `tm().getTransactionTime()` (or `tm().getTxTime()`) is executed within a transaction context.
|
||||
|
||||
### 5. Testing Best Practices
|
||||
- **FakeClock and Sleeper:** Use `FakeClock` and `Sleeper` for any logic involving timeouts, delays, or expiration.
|
||||
@@ -77,6 +79,7 @@ Based on historical PR reviews, avoid the following common mistakes:
|
||||
- **Immutable Types:** Declare variables, fields, and return types explicitly as Guava immutable types (e.g., `ImmutableList<T>`, `ImmutableMap<K, V>`) instead of their generic interfaces (`List<T>`, `Map<K, V>`) to clearly communicate immutability contracts to callers. Use `toImmutableList()` and `toImmutableMap()` collectors in streams rather than manually accumulating into an `ArrayList` or `HashMap`.
|
||||
- **Constructors:** Do not perform heavy logic, I/O, or external API calls inside constructors. Initialization logic should be deferred or handled in a factory method or a dedicated startup routine.
|
||||
- **Exception Handling:** Do not catch generic `Exception` or `Throwable` if a more specific exception is expected. Never "log and re-throw" the same exception; either handle it entirely (and log), or throw it up the chain. For batch processes, catch exceptions at the individual item/chunk level so one failure doesn't abort the entire batch.
|
||||
- **Optional Assertions:** Prefer Truth's `.hasValue(...)` over `.isEqualTo(Optional.of(...))` for cleaner and more descriptive assertions on `Optional` types.
|
||||
- **Fail Fast:** Validate inputs and fail fast (using `Preconditions.checkArgument` or similar) at the highest level possible rather than passing invalid state (like `null`s) deeper into business logic.
|
||||
- **Magic Numbers:** Always document magic numbers or hardcoded limits (like `50.0` or `30`) with inline comments explaining the rationale.
|
||||
- **Null Safety and Optional:** Prefer using `Optional` for any variable that is expected to potentially be null. For any other variable that can be null but cannot use an `Optional` (e.g., function parameters or return types where `Optional` is not idiomatic), it MUST be annotated with `@Nullable`. Always use the `javax.annotation.Nullable` annotation.
|
||||
@@ -93,7 +96,7 @@ This document captures high-level architectural patterns, lessons learned from l
|
||||
- **Dependency Injection:** Dagger 2 is used extensively. If you see "cannot find symbol" errors for classes starting with `Dagger...`, the project is in a state where annotation processing failed. Fix compilation in core models first to restore generated code.
|
||||
- **Value Types:** AutoValue and "ImmutableObject" patterns are dominant. Most models follow a `Buildable` pattern with a nested `Builder`.
|
||||
- **Temporal Logic:** The project is migrating from Joda-Time to `java.time`.
|
||||
- Core boundaries: `DateTimeUtils.START_OF_TIME_INSTANT` (Unix Epoch) and `END_OF_TIME_INSTANT` (Long.MAX_VALUE / 1000).
|
||||
- Core boundaries: `DateTimeUtils.START_INSTANT` (Unix Epoch) and `DateTimeUtils.END_INSTANT` (Long.MAX_VALUE / 1000).
|
||||
- Year Arithmetic: Use `DateTimeUtils.plusYears()` and `DateTimeUtils.minusYears()` to handle February 29th logic correctly.
|
||||
|
||||
## Source Control
|
||||
@@ -119,6 +122,7 @@ Before finalizing any PR or declaring a task complete, you MUST perform a thorou
|
||||
5. **Diff Scope:** Are there any formatting-only changes in files that I did not functionally modify? If so, revert them. Does the total line count of the diff align with the approved scope (e.g., ~1,000 lines for migrations)?
|
||||
6. **Commit Message:** Does the commit message title fit within 50 characters? Does the body encapsulate the entirety of the changes across the diff cleanly and professionally?
|
||||
7. **Missing Tests & Coverage:** *Perform a structured check for any new methods or modified behavior.* Did I add a new utility method (like `plusMonths(Instant, int)`) or change core logic? If so, I MUST open the corresponding test file and write tests to cover the new functionality (including edge cases, negative values, and leap years) before considering the task complete. A code review is not thorough if it only checks for compilation. I must actively ensure every new branch of logic has a test.
|
||||
8. **Package Lock:** Did I include `console-webapp/package-lock.json` in my diff? If so, I MUST revert it (`git checkout console-webapp/package-lock.json`) unless I explicitly intended to modify NPM dependencies. This file is often modified by the build process and should not be committed accidentally.
|
||||
|
||||
Only after actively confirming these checks against your diff are you permitted to finalize the task.
|
||||
|
||||
@@ -148,7 +152,7 @@ This project treats Error Prone warnings as errors.
|
||||
- **Static Imports:** Methods like `toDateTime`, `toInstant`, `plusYears`, `plusMonths`, and `minusDays` from `DateTimeUtils` MUST be statically imported. Do NOT use them fully qualified (e.g., `DateTimeUtils.plusMonths(...)`).
|
||||
|
||||
- **Redundant Parses:** Never write `toDateTime(Instant.parse(...))` or `toInstant(DateTime.parse(...))`. If you need a `DateTime`, use `DateTime.parse(...)` directly. If you need an `Instant`, use `Instant.parse(...)` directly.
|
||||
- **cloneProjectedAtTime vs cloneProjectedAtInstant:** When converting tests and logic that use `clock.now()` to project resource state into the future or past, do not wrap the Java `Instant` in `toDateTime()` just to call `cloneProjectedAtTime()`. Instead, switch the method call to use the native `cloneProjectedAtInstant()` method which is available on all `EppResource` models.
|
||||
- **cloneProjectedAtTime vs cloneProjectedAtTime:** When converting tests and logic that use `clock.now()` to project resource state into the future or past, do not wrap the Java `Instant` in `toDateTime()` just to call `cloneProjectedAtTime()`. Instead, switch the method call to use the native `cloneProjectedAtTime()` method which is available on all `EppResource` models.
|
||||
- **Do not go in circles with the build:** If you see an `InlineMeSuggester` error, apply the suppression to **ALL** similar methods in that file and related files in one turn. Do not fix them one by one. Furthermore, do not run a global `./gradlew build` when a scoped `./gradlew :core:build` or `./gradlew :core:test` is faster and more appropriate. Run global builds only when doing final verification.
|
||||
- **Exception Conversion in Tests:** When migrating time types (e.g., from Joda `DateTime` to Java `Instant`), be extremely careful with tests that verify parsing failures (e.g., `assertThrows(IllegalArgumentException.class, ...)`). Joda's `DateTime.parse()` throws an `IllegalArgumentException` on failure, but `Instant.parse()` throws a `java.time.format.DateTimeParseException`. You must update the expected exception type in these tests to ensure they actually test the correct behavior, and verify the tests are not failing prematurely on the first line if it contains invalid data meant to be ignored.
|
||||
- Dagger/AutoValue corruption: If you modify a builder or a component incorrectly, Dagger will fail to generate code, leading to hundreds of "cannot find symbol" errors. If this happens, `git checkout` the last working state of the specific file and re-apply changes more surgically.
|
||||
@@ -156,20 +160,20 @@ This project treats Error Prone warnings as errors.
|
||||
|
||||
---
|
||||
|
||||
# GitHub and Pull Request Protocol
|
||||
## GitHub and Pull Request Protocol
|
||||
|
||||
This protocol defines the standard for interacting with GitHub repositories and processing Pull Request (PR) feedback.
|
||||
|
||||
## 1. Interaction via `gh` CLI
|
||||
### 1. Interaction via `gh` CLI
|
||||
- **Primary Tool:** ALWAYS use the `gh` CLI for all GitHub-related operations (listing PRs, viewing PR content, checking status, adding comments).
|
||||
- **Credential Safety:** Never expose tokens or credentials in shell commands.
|
||||
|
||||
## 2. Processing PR Feedback
|
||||
### 2. Processing PR Feedback
|
||||
- **Systematic Review:** When asked to address PR comments, first fetch all comments using `gh pr view <number> --json reviews,comments`.
|
||||
- **Minimal Scope Expansion:** Address comments surgically. If a fix requires changes beyond a few lines or expands the PR's original scope significantly, DO NOT implement it without explicit user approval. Instead, report the issue to the user.
|
||||
- **Verification:** After addressing feedback, run the full build (`./gradlew build`) and relevant tests to ensure no regressions were introduced.
|
||||
|
||||
## 3. PR Lifecycle Management
|
||||
### 3. PR Lifecycle Management
|
||||
- **One Commit Per PR:** Ensure all changes are squashed into a single, clean commit. Use `git commit --amend --no-edit` for follow-up fixes.
|
||||
- **Clean Workspace:** Always run `git status` and verify the repository state before declaring a task complete.
|
||||
- **Package Lock:** The Gradle build automatically modifies `console-webapp/package-lock.json` via the `npmInstallDeps` task. ALWAYS revert this file (`git checkout console-webapp/package-lock.json`) before staging changes or finalizing a commit unless you explicitly modified NPM dependencies.
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A clock that tells the current time in milliseconds or nanoseconds.
|
||||
@@ -32,10 +31,6 @@ import org.joda.time.DateTime;
|
||||
@ThreadSafe
|
||||
public interface Clock extends Serializable {
|
||||
|
||||
/** Returns current time in UTC timezone. */
|
||||
@Deprecated
|
||||
DateTime nowUtc();
|
||||
|
||||
/** Returns current Instant (which is always in UTC). */
|
||||
Instant now();
|
||||
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
package google.registry.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Ordering;
|
||||
import java.sql.Date;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
@@ -30,28 +31,13 @@ import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.ReadableDuration;
|
||||
|
||||
/** Utilities methods and constants related to Joda {@link DateTime} objects. */
|
||||
public abstract class DateTimeUtils {
|
||||
|
||||
/** The start of the epoch, in a convenient constant. */
|
||||
@Deprecated public static final DateTime START_OF_TIME = new DateTime(0, DateTimeZone.UTC);
|
||||
|
||||
/** The start of the UNIX epoch (which is defined in UTC), in a convenient constant. */
|
||||
public static final Instant START_INSTANT = Instant.ofEpochMilli(0);
|
||||
|
||||
/**
|
||||
* A date in the far future that we can treat as infinity.
|
||||
*
|
||||
* <p>This value is (2^63-1)/1000 rounded down. Postgres can store dates as 64 bit microseconds,
|
||||
* but Java uses milliseconds, so this is the largest representable date that will survive a
|
||||
* round-trip through the database.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final DateTime END_OF_TIME = new DateTime(Long.MAX_VALUE / 1000, DateTimeZone.UTC);
|
||||
|
||||
/**
|
||||
* An instant in the far future that we can treat as infinity.
|
||||
*
|
||||
@@ -71,11 +57,15 @@ public abstract class DateTimeUtils {
|
||||
*/
|
||||
private static final DateTimeFormatter ISO_8601_FORMATTER =
|
||||
new DateTimeFormatterBuilder()
|
||||
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.NORMAL)
|
||||
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.NOT_NEGATIVE)
|
||||
.appendPattern("-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||
.toFormatter()
|
||||
.withZone(ZoneOffset.UTC);
|
||||
|
||||
/** A formatter that produces lowercase, filename-safe and job-name-safe timestamps. */
|
||||
public static final DateTimeFormatter LOWERCASE_TIMESTAMP_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd't'HH-mm-ss'z'").withZone(ZoneOffset.UTC);
|
||||
|
||||
/** Formats an {@link Instant} to an ISO-8601 string. */
|
||||
public static String formatInstant(Instant instant) {
|
||||
return ISO_8601_FORMATTER.format(instant);
|
||||
@@ -98,53 +88,38 @@ public abstract class DateTimeUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the earliest of a number of given {@link DateTime} instances. */
|
||||
public static DateTime earliestOf(DateTime first, DateTime... rest) {
|
||||
return earliestDateTimeOf(Lists.asList(first, rest));
|
||||
}
|
||||
|
||||
/** Returns the earliest of a number of given {@link Instant} instances. */
|
||||
public static Instant earliestOf(Instant first, Instant... rest) {
|
||||
return earliestOf(Lists.asList(first, rest));
|
||||
}
|
||||
|
||||
/** Returns the earliest element in a {@link DateTime} iterable. */
|
||||
public static DateTime earliestDateTimeOf(Iterable<DateTime> dates) {
|
||||
checkArgument(!Iterables.isEmpty(dates));
|
||||
return Ordering.<DateTime>natural().min(dates);
|
||||
}
|
||||
|
||||
/** Returns the earliest element in a {@link Instant} iterable. */
|
||||
public static Instant earliestOf(Iterable<Instant> instants) {
|
||||
checkArgument(!Iterables.isEmpty(instants));
|
||||
return Ordering.<Instant>natural().min(instants);
|
||||
}
|
||||
|
||||
/** Returns the latest of a number of given {@link DateTime} instances. */
|
||||
public static DateTime latestOf(DateTime first, DateTime... rest) {
|
||||
return latestDateTimeOf(Lists.asList(first, rest));
|
||||
}
|
||||
|
||||
/** Returns the latest of a number of given {@link Instant} instances. */
|
||||
public static Instant latestOf(Instant first, Instant... rest) {
|
||||
return latestOf(Lists.asList(first, rest));
|
||||
}
|
||||
|
||||
/** Returns the latest element in a {@link DateTime} iterable. */
|
||||
public static DateTime latestDateTimeOf(Iterable<DateTime> dates) {
|
||||
checkArgument(!Iterables.isEmpty(dates));
|
||||
return Ordering.<DateTime>natural().max(dates);
|
||||
}
|
||||
|
||||
/** Returns the latest element in a {@link Instant} iterable. */
|
||||
public static Instant latestOf(Iterable<Instant> instants) {
|
||||
checkArgument(!Iterables.isEmpty(instants));
|
||||
return Ordering.<Instant>natural().max(instants);
|
||||
}
|
||||
|
||||
/** Returns whether the first {@link DateTime} is equal to or earlier than the second. */
|
||||
public static boolean isBeforeOrAt(DateTime timeToCheck, DateTime timeToCompareTo) {
|
||||
return !timeToCheck.isAfter(timeToCompareTo);
|
||||
/** Converts a Joda-Time Duration to a java.time.Duration. */
|
||||
@Nullable
|
||||
public static java.time.Duration toJavaDuration(@Nullable ReadableDuration duration) {
|
||||
return duration == null ? null : java.time.Duration.ofMillis(duration.getMillis());
|
||||
}
|
||||
|
||||
/** Converts a java.time.Duration to a Joda-Time Duration. */
|
||||
@Nullable
|
||||
public static org.joda.time.Duration toJodaDuration(@Nullable java.time.Duration duration) {
|
||||
return duration == null ? null : org.joda.time.Duration.millis(duration.toMillis());
|
||||
}
|
||||
|
||||
/** Returns whether the first {@link Instant} is equal to or earlier than the second. */
|
||||
@@ -152,25 +127,11 @@ public abstract class DateTimeUtils {
|
||||
return !timeToCheck.isAfter(timeToCompareTo);
|
||||
}
|
||||
|
||||
/** Returns whether the first {@link DateTime} is equal to or later than the second. */
|
||||
public static boolean isAtOrAfter(DateTime timeToCheck, DateTime timeToCompareTo) {
|
||||
return !timeToCheck.isBefore(timeToCompareTo);
|
||||
}
|
||||
|
||||
/** Returns whether the first {@link Instant} is equal to or later than the second. */
|
||||
public static boolean isAtOrAfter(Instant timeToCheck, Instant timeToCompareTo) {
|
||||
return !timeToCheck.isBefore(timeToCompareTo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds years to a date, in the {@code Duration} sense of semantic years. Use this instead of
|
||||
* {@link DateTime#plusYears} to ensure that we never end up on February 29.
|
||||
*/
|
||||
public static DateTime plusYears(DateTime now, int years) {
|
||||
checkArgument(years >= 0);
|
||||
return years == 0 ? now : now.plusYears(1).plusYears(years - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds years to a date, in the {@code Duration} sense of semantic years. Use this instead of
|
||||
* {@link java.time.ZonedDateTime#plusYears} to ensure that we never end up on February 29.
|
||||
@@ -194,15 +155,6 @@ public abstract class DateTimeUtils {
|
||||
return now.atZone(ZoneOffset.UTC).minusMonths(months).toInstant();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts years from a date, in the {@code Duration} sense of semantic years. Use this instead
|
||||
* of {@link DateTime#minusYears} to ensure that we never end up on February 29.
|
||||
*/
|
||||
public static DateTime minusYears(DateTime now, int years) {
|
||||
checkArgument(years >= 0);
|
||||
return years == 0 ? now : now.minusYears(1).minusYears(years - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts years from a date, in the {@code Duration} sense of semantic years. Use this instead
|
||||
* of {@link java.time.ZonedDateTime#minusYears} to ensure that we never end up on February 29.
|
||||
@@ -214,30 +166,9 @@ public abstract class DateTimeUtils {
|
||||
: now.atZone(ZoneOffset.UTC).minusYears(1).minusYears(years - 1).toInstant();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #plusYears(DateTime, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static DateTime leapSafeAddYears(DateTime now, int years) {
|
||||
return plusYears(now, years);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #minusYears(DateTime, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static DateTime leapSafeSubtractYears(DateTime now, int years) {
|
||||
return minusYears(now, years);
|
||||
}
|
||||
|
||||
public static Date toSqlDate(LocalDate localDate) {
|
||||
return new Date(localDate.toDateTimeAtStartOfDay().getMillis());
|
||||
}
|
||||
|
||||
public static LocalDate toLocalDate(Date date) {
|
||||
return new LocalDate(date.getTime(), DateTimeZone.UTC);
|
||||
/** Converts an Instant to a java.time.LocalDate in UTC. */
|
||||
public static LocalDate toLocalDate(Instant instant) {
|
||||
return instant.atZone(ZoneOffset.UTC).toLocalDate();
|
||||
}
|
||||
|
||||
/** Convert a joda {@link DateTime} to a java.time {@link Instant}, null-safe. */
|
||||
@@ -249,7 +180,7 @@ public abstract class DateTimeUtils {
|
||||
/** Convert a java.time {@link Instant} to a joda {@link DateTime}, null-safe. */
|
||||
@Nullable
|
||||
public static DateTime toDateTime(@Nullable Instant instant) {
|
||||
return (instant == null) ? null : new DateTime(instant.toEpochMilli(), DateTimeZone.UTC);
|
||||
return (instant == null) ? null : new DateTime(instant.toEpochMilli(), UTC);
|
||||
}
|
||||
|
||||
/** Convert a java.time {@link java.time.Instant} to a joda {@link org.joda.time.Instant}. */
|
||||
@@ -274,11 +205,19 @@ public abstract class DateTimeUtils {
|
||||
return instant.minus(minutes, ChronoUnit.MINUTES);
|
||||
}
|
||||
|
||||
public static Instant plusDays(Instant instant, int days) {
|
||||
public static Instant plusWeeks(Instant instant, int weeks) {
|
||||
return instant.atZone(ZoneOffset.UTC).plusWeeks(weeks).toInstant();
|
||||
}
|
||||
|
||||
public static Instant minusWeeks(Instant instant, int weeks) {
|
||||
return instant.atZone(ZoneOffset.UTC).minusWeeks(weeks).toInstant();
|
||||
}
|
||||
|
||||
public static Instant plusDays(Instant instant, long days) {
|
||||
return instant.atZone(ZoneOffset.UTC).plusDays(days).toInstant();
|
||||
}
|
||||
|
||||
public static Instant minusDays(Instant instant, int days) {
|
||||
public static Instant minusDays(Instant instant, long days) {
|
||||
return instant.atZone(ZoneOffset.UTC).minusDays(days).toInstant();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
package google.registry.util;
|
||||
|
||||
import java.time.Duration;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import org.joda.time.ReadableDuration;
|
||||
|
||||
/**
|
||||
* An object which accepts requests to put the current thread to sleep.
|
||||
@@ -30,7 +30,7 @@ public interface Sleeper {
|
||||
*
|
||||
* @throws InterruptedException if this thread was interrupted
|
||||
*/
|
||||
void sleep(ReadableDuration duration) throws InterruptedException;
|
||||
void sleep(Duration duration) throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Puts the current thread to sleep, ignoring interrupts.
|
||||
@@ -40,7 +40,7 @@ public interface Sleeper {
|
||||
*
|
||||
* @see com.google.common.util.concurrent.Uninterruptibles#sleepUninterruptibly
|
||||
*/
|
||||
void sleepUninterruptibly(ReadableDuration duration);
|
||||
void sleepUninterruptibly(Duration duration);
|
||||
|
||||
/**
|
||||
* Puts the current thread to interruptible sleep.
|
||||
@@ -48,7 +48,7 @@ public interface Sleeper {
|
||||
* <p>This is a convenience method for {@link #sleep} that properly converts an {@link
|
||||
* InterruptedException} to a {@link RuntimeException}.
|
||||
*/
|
||||
default void sleepInterruptibly(ReadableDuration duration) {
|
||||
default void sleepInterruptibly(Duration duration) {
|
||||
try {
|
||||
sleep(duration);
|
||||
} catch (InterruptedException e) {
|
||||
|
||||
@@ -14,13 +14,11 @@
|
||||
|
||||
package google.registry.util;
|
||||
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static java.time.temporal.ChronoUnit.MILLIS;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Clock implementation that proxies to the real system clock. */
|
||||
@ThreadSafe
|
||||
@@ -31,18 +29,12 @@ public class SystemClock implements Clock {
|
||||
@Inject
|
||||
public SystemClock() {}
|
||||
|
||||
/** Returns the current time. */
|
||||
@Override
|
||||
public DateTime nowUtc() {
|
||||
return DateTime.now(UTC);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant now() {
|
||||
// Truncate to milliseconds to match the precision of Joda DateTime and our database schema
|
||||
// (which uses millisecond precision via DateTimeConverter). This prevents subtle comparison
|
||||
// bugs where a high-precision Instant would be considered "after" a truncated database
|
||||
// timestamp.
|
||||
return Instant.now().truncatedTo(ChronoUnit.MILLIS);
|
||||
return Instant.now().truncatedTo(MILLIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import jakarta.inject.Inject;
|
||||
import java.io.Serializable;
|
||||
import java.time.Duration;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import org.joda.time.ReadableDuration;
|
||||
|
||||
/** Implementation of {@link Sleeper} for production use. */
|
||||
@ThreadSafe
|
||||
@@ -33,14 +32,14 @@ public final class SystemSleeper implements Sleeper, Serializable {
|
||||
public SystemSleeper() {}
|
||||
|
||||
@Override
|
||||
public void sleep(ReadableDuration duration) throws InterruptedException {
|
||||
checkArgument(duration.getMillis() >= 0);
|
||||
Thread.sleep(duration.getMillis());
|
||||
public void sleep(Duration duration) throws InterruptedException {
|
||||
checkArgument(!duration.isNegative(), "Duration must be non-negative");
|
||||
Thread.sleep(duration.toMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sleepUninterruptibly(ReadableDuration duration) {
|
||||
checkArgument(duration.getMillis() >= 0);
|
||||
Uninterruptibles.sleepUninterruptibly(Duration.ofMillis(duration.getMillis()));
|
||||
public void sleepUninterruptibly(Duration duration) {
|
||||
checkArgument(!duration.isNegative(), "Duration must be non-negative");
|
||||
Uninterruptibles.sleepUninterruptibly(duration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,16 +14,13 @@
|
||||
|
||||
package google.registry.testing;
|
||||
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.joda.time.Duration.millis;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
|
||||
import google.registry.util.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.joda.time.ReadableDuration;
|
||||
import org.joda.time.ReadableInstant;
|
||||
|
||||
@@ -39,12 +36,13 @@ public final class FakeClock implements Clock {
|
||||
|
||||
private volatile long autoIncrementStepMs;
|
||||
|
||||
/** Creates a FakeClock that starts at START_OF_TIME. */
|
||||
/** Creates a FakeClock that starts at START_INSTANT. */
|
||||
public FakeClock() {
|
||||
this(START_OF_TIME);
|
||||
this(START_INSTANT);
|
||||
}
|
||||
|
||||
/** Creates a FakeClock initialized to a specific time. */
|
||||
@Deprecated
|
||||
public FakeClock(ReadableInstant startTime) {
|
||||
setTo(startTime);
|
||||
}
|
||||
@@ -54,12 +52,6 @@ public final class FakeClock implements Clock {
|
||||
setTo(startTime);
|
||||
}
|
||||
|
||||
/** Returns the current time. */
|
||||
@Override
|
||||
public DateTime nowUtc() {
|
||||
return new DateTime(currentTimeMillis.addAndGet(autoIncrementStepMs), UTC);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant now() {
|
||||
return Instant.ofEpochMilli(currentTimeMillis.addAndGet(autoIncrementStepMs));
|
||||
@@ -74,22 +66,44 @@ public final class FakeClock implements Clock {
|
||||
* @param autoIncrementStep the new auto increment duration
|
||||
* @return this
|
||||
*/
|
||||
@Deprecated
|
||||
public FakeClock setAutoIncrementStep(ReadableDuration autoIncrementStep) {
|
||||
this.autoIncrementStepMs = autoIncrementStep.getMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the increment applied to the clock whenever it is queried. The increment is zero by
|
||||
* default: the clock is left unchanged when queried.
|
||||
*
|
||||
* <p>Passing a duration of zero to this method effectively unsets the auto increment mode.
|
||||
*
|
||||
* @param autoIncrementStep the new auto increment duration
|
||||
* @return this
|
||||
*/
|
||||
public FakeClock setAutoIncrementStep(Duration autoIncrementStep) {
|
||||
this.autoIncrementStepMs = autoIncrementStep.toMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Advances clock by one millisecond. */
|
||||
public void advanceOneMilli() {
|
||||
advanceBy(millis(1));
|
||||
advanceBy(Duration.ofMillis(1));
|
||||
}
|
||||
|
||||
/** Advances clock by some duration. */
|
||||
@Deprecated
|
||||
public void advanceBy(ReadableDuration duration) {
|
||||
currentTimeMillis.addAndGet(duration.getMillis());
|
||||
}
|
||||
|
||||
/** Advances clock by some duration. */
|
||||
public void advanceBy(Duration duration) {
|
||||
currentTimeMillis.addAndGet(duration.toMillis());
|
||||
}
|
||||
|
||||
/** Sets the time to the specified instant. */
|
||||
@Deprecated
|
||||
public void setTo(ReadableInstant time) {
|
||||
currentTimeMillis.set(time.getMillis());
|
||||
}
|
||||
@@ -101,7 +115,7 @@ public final class FakeClock implements Clock {
|
||||
|
||||
/** Invokes {@link #setAutoIncrementStep} with one millisecond-step. */
|
||||
public FakeClock setAutoIncrementByOneMilli() {
|
||||
return setAutoIncrementStep(Duration.millis(1));
|
||||
return setAutoIncrementStep(Duration.ofMillis(1));
|
||||
}
|
||||
|
||||
/** Disables the auto-increment mode. */
|
||||
|
||||
@@ -19,8 +19,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import google.registry.util.Sleeper;
|
||||
import java.io.Serializable;
|
||||
import java.time.Duration;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import org.joda.time.ReadableDuration;
|
||||
|
||||
/** Sleeper implementation for unit tests that advances {@link FakeClock} rather than sleep. */
|
||||
@ThreadSafe
|
||||
@@ -35,8 +35,8 @@ public final class FakeSleeper implements Sleeper, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sleep(ReadableDuration duration) throws InterruptedException {
|
||||
checkArgument(duration.getMillis() >= 0);
|
||||
public void sleep(Duration duration) throws InterruptedException {
|
||||
checkArgument(!duration.isNegative(), "Duration must be non-negative");
|
||||
if (Thread.interrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
@@ -44,8 +44,8 @@ public final class FakeSleeper implements Sleeper, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sleepUninterruptibly(ReadableDuration duration) {
|
||||
checkArgument(duration.getMillis() >= 0);
|
||||
public void sleepUninterruptibly(Duration duration) {
|
||||
checkArgument(!duration.isNegative(), "Duration must be non-negative");
|
||||
clock.advanceBy(duration);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-33
@@ -153,7 +153,15 @@ PRESUBMITS = {
|
||||
PresubmitCheck(
|
||||
r".*java\.util\.Date.*",
|
||||
"java",
|
||||
{"/node_modules/", "JpaTransactionManagerImpl.java", "DateTimeUtils.java"},
|
||||
{
|
||||
"/node_modules/",
|
||||
"JpaTransactionManagerImpl.java",
|
||||
"DateTimeUtils.java",
|
||||
"SelfSignedCaCertificate.java",
|
||||
"X509Utils.java",
|
||||
"TmchCertificateAuthority.java",
|
||||
"DelegatedCredentials.java"
|
||||
},
|
||||
):
|
||||
"Do not use java.util.Date. Use classes in java.time package instead.",
|
||||
PresubmitCheck(
|
||||
@@ -208,29 +216,29 @@ PRESUBMITS = {
|
||||
):
|
||||
"Do not double-wrap toInstant(toDateTime(...)).",
|
||||
PresubmitCheck(
|
||||
r".*toInstant\([^;]*[cC]lock\.nowUtc\(\).*",
|
||||
r".*toInstant\([^;]*[cC]lock\.now\(\).*",
|
||||
"java",
|
||||
{},
|
||||
):
|
||||
"Do not use toInstant(clock.nowUtc()). Use clock.now() instead.",
|
||||
"Do not use toInstant(clock.now()). Use clock.now() instead.",
|
||||
PresubmitCheck(
|
||||
r".*toDateTime\([^;]*[cC]lock\.now\(\).*",
|
||||
"java",
|
||||
{},
|
||||
):
|
||||
"Do not use toDateTime(clock.now()). Use clock.nowUtc() instead.",
|
||||
"Do not use toDateTime(clock.now()). Use clock.now() and Instant overloads instead.",
|
||||
PresubmitCheck(
|
||||
r".*toInstant\([^;]*tm\(\)\.getTransactionTime\(\).*",
|
||||
r".*toInstant\([^;]*tm\(\)\.getTxTime\(\).*",
|
||||
"java",
|
||||
{},
|
||||
):
|
||||
"Do not use toInstant(tm().getTransactionTime()). Use tm().getTxTime() instead.",
|
||||
"Do not use toInstant(tm().getTxTime()). Use tm().getTxTime() instead.",
|
||||
PresubmitCheck(
|
||||
r".*toDateTime\([^;]*tm\(\)\.getTxTime\(\).*",
|
||||
"java",
|
||||
{},
|
||||
):
|
||||
"Do not use toDateTime(tm().getTxTime()). Use tm().getTransactionTime() instead.",
|
||||
"Do not use toDateTime(tm().getTxTime()). Use tm().getTxTime() and Instant overloads instead.",
|
||||
PresubmitCheck(
|
||||
r".*\(\s*Instant\s*\)\s*(?:this\.)?(?:fakeClock|clock)\.now\(\s*\).*",
|
||||
"java",
|
||||
@@ -266,37 +274,13 @@ PRESUBMITS = {
|
||||
"java",
|
||||
{},
|
||||
):
|
||||
"Do not use cloneProjectedAtTime(toDateTime(...)). Use cloneProjectedAtInstant(...) instead.",
|
||||
"Do not use cloneProjectedAtTime(toDateTime(...)). Use cloneProjectedAtTime(...) instead.",
|
||||
PresubmitCheck(
|
||||
r".*ZoneId\.of\(\s*\"UTC\"\s*\).*",
|
||||
"java",
|
||||
{},
|
||||
):
|
||||
"Do not use ZoneId.of(\"UTC\"). Use java.time.ZoneOffset.UTC.",
|
||||
PresubmitCheck(
|
||||
r".*toDateTime\(\s*END_INSTANT\s*\).*",
|
||||
"java",
|
||||
{"DateTimeUtilsTest.java"},
|
||||
):
|
||||
"Do not wrap END_INSTANT in toDateTime. Use END_OF_TIME.",
|
||||
PresubmitCheck(
|
||||
r".*toInstant\(\s*END_OF_TIME\s*\).*",
|
||||
"java",
|
||||
{"DateTimeUtilsTest.java"},
|
||||
):
|
||||
"Do not wrap END_OF_TIME in toInstant. Use END_INSTANT.",
|
||||
PresubmitCheck(
|
||||
r".*toDateTime\(\s*START_INSTANT\s*\).*",
|
||||
"java",
|
||||
{"DateTimeUtilsTest.java"},
|
||||
):
|
||||
"Do not wrap START_INSTANT in toDateTime. Use START_OF_TIME.",
|
||||
PresubmitCheck(
|
||||
r".*toInstant\(\s*START_OF_TIME\s*\).*",
|
||||
"java",
|
||||
{"DateTimeUtilsTest.java"},
|
||||
):
|
||||
"Do not wrap START_OF_TIME in toInstant. Use START_INSTANT."
|
||||
"Do not use ZoneId.of(\"UTC\"). Use java.time.ZoneOffset.UTC."
|
||||
}
|
||||
|
||||
# Note that this regex only works for one kind of Flyway file. If we want to
|
||||
|
||||
Generated
+123
@@ -628,6 +628,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/build/node_modules/@types/node": {
|
||||
"version": "25.6.0",
|
||||
"resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/@types/node/-/node-25.6.0.tgz",
|
||||
"integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/build/node_modules/@vitejs/plugin-basic-ssl": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.0.tgz",
|
||||
@@ -641,6 +653,24 @@
|
||||
"vite": "^6.0.0 || ^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/build/node_modules/chokidar": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/chokidar/-/chokidar-5.0.0.tgz",
|
||||
"integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"readdirp": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/build/node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -664,6 +694,22 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/build/node_modules/readdirp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/readdirp/-/readdirp-5.0.0.tgz",
|
||||
"integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 20.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/build/node_modules/rxjs": {
|
||||
"version": "7.8.2",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
|
||||
@@ -697,6 +743,15 @@
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/build/node_modules/undici-types": {
|
||||
"version": "7.19.2",
|
||||
"resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/undici-types/-/undici-types-7.19.2.tgz",
|
||||
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@angular/build/node_modules/vite": {
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
|
||||
@@ -916,6 +971,24 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/cli/node_modules/chokidar": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/chokidar/-/chokidar-5.0.0.tgz",
|
||||
"integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"readdirp": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/cli/node_modules/cli-spinners": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz",
|
||||
@@ -1019,6 +1092,22 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/cli/node_modules/readdirp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/readdirp/-/readdirp-5.0.0.tgz",
|
||||
"integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 20.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/cli/node_modules/rxjs": {
|
||||
"version": "7.8.2",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
|
||||
@@ -4606,6 +4695,24 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@schematics/angular/node_modules/chokidar": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/chokidar/-/chokidar-5.0.0.tgz",
|
||||
"integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"readdirp": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@schematics/angular/node_modules/cli-spinners": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz",
|
||||
@@ -4709,6 +4816,22 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/@schematics/angular/node_modules/readdirp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://us-npm.pkg.dev/artifact-foundry-prod/ah-3p-staging-npm/readdirp/-/readdirp-5.0.0.tgz",
|
||||
"integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 20.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@schematics/angular/node_modules/rxjs": {
|
||||
"version": "7.8.2",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
|
||||
|
||||
@@ -579,6 +579,11 @@ if (environment == 'alpha') {
|
||||
mainClass: 'google.registry.beam.resave.ResaveAllEppResourcesPipeline',
|
||||
metaData: 'google/registry/beam/resave_all_epp_resources_pipeline_metadata.json'
|
||||
],
|
||||
smokeTest:
|
||||
[
|
||||
mainClass: 'google.registry.beam.common.SmokeTestPipeline',
|
||||
metaData: 'google/registry/beam/smoke_test_pipeline_metadata.json'
|
||||
],
|
||||
]
|
||||
project.tasks.create("stageBeamPipelines") {
|
||||
doLast {
|
||||
|
||||
@@ -26,8 +26,8 @@ import google.registry.model.EppResource;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action;
|
||||
import jakarta.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
/** Helper class to enqueue tasks for handling asynchronous operations in flows. */
|
||||
public final class AsyncTaskEnqueuer {
|
||||
@@ -41,7 +41,7 @@ public final class AsyncTaskEnqueuer {
|
||||
public static final String QUEUE_ASYNC_ACTIONS = "async-actions";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final Duration MAX_ASYNC_ETA = Duration.standardDays(30);
|
||||
private static final Duration MAX_ASYNC_ETA = Duration.ofDays(30);
|
||||
|
||||
private final CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@@ -58,12 +58,12 @@ public final class AsyncTaskEnqueuer {
|
||||
*/
|
||||
public void enqueueAsyncResave(
|
||||
VKey<? extends EppResource> entityKey,
|
||||
DateTime now,
|
||||
ImmutableSortedSet<DateTime> whenToResave) {
|
||||
DateTime firstResave = whenToResave.first();
|
||||
Instant now,
|
||||
ImmutableSortedSet<Instant> whenToResave) {
|
||||
Instant firstResave = whenToResave.first();
|
||||
checkArgument(isBeforeOrAt(now, firstResave), "Can't enqueue a resave to run in the past");
|
||||
Duration etaDuration = new Duration(now, firstResave);
|
||||
if (etaDuration.isLongerThan(MAX_ASYNC_ETA)) {
|
||||
Duration etaDuration = Duration.between(now, firstResave);
|
||||
if (etaDuration.compareTo(MAX_ASYNC_ETA) > 0) {
|
||||
logger.atInfo().log(
|
||||
"Ignoring async re-save of %s; %s is past the ETA threshold of %s.",
|
||||
entityKey, firstResave, MAX_ASYNC_ETA);
|
||||
|
||||
@@ -24,9 +24,9 @@ import static google.registry.request.RequestParameters.extractOptionalBooleanPa
|
||||
import static google.registry.request.RequestParameters.extractOptionalInstantParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalIntParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalParameter;
|
||||
import static google.registry.request.RequestParameters.extractRequiredDatetimeParameter;
|
||||
import static google.registry.request.RequestParameters.extractRequiredInstantParameter;
|
||||
import static google.registry.request.RequestParameters.extractRequiredParameter;
|
||||
import static google.registry.request.RequestParameters.extractSetOfDatetimeParameters;
|
||||
import static google.registry.request.RequestParameters.extractSetOfInstantParameters;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -44,7 +44,6 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Dagger module for injecting common settings for batch actions. */
|
||||
@Module
|
||||
@@ -98,14 +97,14 @@ public class BatchModule {
|
||||
|
||||
@Provides
|
||||
@Parameter(PARAM_REQUESTED_TIME)
|
||||
static DateTime provideRequestedTime(HttpServletRequest req) {
|
||||
return extractRequiredDatetimeParameter(req, PARAM_REQUESTED_TIME);
|
||||
static Instant provideRequestedTime(HttpServletRequest req) {
|
||||
return extractRequiredInstantParameter(req, PARAM_REQUESTED_TIME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(PARAM_RESAVE_TIMES)
|
||||
static ImmutableSet<DateTime> provideResaveTimes(HttpServletRequest req) {
|
||||
return extractSetOfDatetimeParameters(req, PARAM_RESAVE_TIMES);
|
||||
static ImmutableSet<Instant> provideResaveTimes(HttpServletRequest req) {
|
||||
return extractSetOfInstantParameters(req, PARAM_RESAVE_TIMES);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -42,10 +42,10 @@ import google.registry.request.auth.Auth;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* An action that transfers a set of domains from one registrar to another.
|
||||
@@ -158,7 +158,7 @@ public class BulkDomainTransferAction implements Runnable {
|
||||
return null;
|
||||
};
|
||||
|
||||
if (!lockHandler.executeWithLocks(runner, null, Duration.standardHours(1), LOCK_NAME)) {
|
||||
if (!lockHandler.executeWithLocks(runner, null, Duration.ofHours(1), LOCK_NAME)) {
|
||||
// Send a 200-series status code to prevent this conflicting action from retrying.
|
||||
response.setStatus(SC_NO_CONTENT);
|
||||
response.setPayload("Could not acquire lock; already running?");
|
||||
|
||||
@@ -52,11 +52,11 @@ import jakarta.inject.Inject;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.function.Supplier;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Utilities for dealing with Cloud Tasks. */
|
||||
public class CloudTasksUtils implements Serializable {
|
||||
@@ -251,7 +251,7 @@ public class CloudTasksUtils implements Serializable {
|
||||
method,
|
||||
service,
|
||||
params,
|
||||
Duration.millis(random.nextInt((int) SECONDS.toMillis(jitterSeconds.get()))));
|
||||
Duration.ofMillis(random.nextInt((int) SECONDS.toMillis(jitterSeconds.get()))));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,12 +302,12 @@ public class CloudTasksUtils implements Serializable {
|
||||
Action.Service service,
|
||||
Multimap<String, String> params,
|
||||
Duration delay) {
|
||||
if (delay.isEqual(Duration.ZERO)) {
|
||||
if (delay.isZero()) {
|
||||
return createTask(path, method, service, params);
|
||||
}
|
||||
checkArgument(delay.isLongerThan(Duration.ZERO), "Negative duration is not supported.");
|
||||
checkArgument(!delay.isNegative(), "Negative duration is not supported.");
|
||||
return Task.newBuilder(createTask(path, method, service, params))
|
||||
.setScheduleTime(Timestamps.fromMillis(clock.nowUtc().plus(delay).getMillis()))
|
||||
.setScheduleTime(Timestamps.fromMillis(clock.now().plus(delay).toEpochMilli()))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -42,11 +42,11 @@ import google.registry.request.auth.Auth;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* An action that deletes all non-renewing domains whose expiration dates have now passed.
|
||||
@@ -116,7 +116,7 @@ public class DeleteExpiredDomainsAction implements Runnable {
|
||||
return null;
|
||||
};
|
||||
|
||||
if (!lockHandler.executeWithLocks(runner, null, Duration.standardHours(1), LOCK_NAME)) {
|
||||
if (!lockHandler.executeWithLocks(runner, null, Duration.ofHours(1), LOCK_NAME)) {
|
||||
// Send a 200-series status code to prevent this conflicting action from retrying.
|
||||
response.setStatus(SC_NO_CONTENT);
|
||||
response.setPayload("Could not acquire lock; already running?");
|
||||
|
||||
@@ -115,7 +115,7 @@ public class DeleteLoadTestDataAction implements Runnable {
|
||||
VKey<Host> hostVKey = host.createVKey();
|
||||
// We can remove hosts from linked domains, so we should do so then delete the hosts
|
||||
ImmutableSet<VKey<Domain>> linkedDomains =
|
||||
EppResourceUtils.getLinkedDomainKeys(hostVKey, clock.nowUtc(), null);
|
||||
EppResourceUtils.getLinkedDomainKeys(hostVKey, clock.now(), null);
|
||||
tm().loadByKeys(linkedDomains)
|
||||
.values()
|
||||
.forEach(
|
||||
|
||||
@@ -27,7 +27,6 @@ import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.request.RequestParameters.PARAM_BATCH_SIZE;
|
||||
import static google.registry.request.RequestParameters.PARAM_DRY_RUN;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLDS;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
import static google.registry.util.RegistryEnvironment.PRODUCTION;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
@@ -48,11 +47,11 @@ import google.registry.util.Clock;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Deletes all prober {@link Domain}s and their subordinate history entries, poll messages, and
|
||||
@@ -74,7 +73,7 @@ public class DeleteProberDataAction implements Runnable {
|
||||
* <p>In practice, the prober's connection will time out well before this duration. This includes
|
||||
* a decent buffer.
|
||||
*/
|
||||
private static final Duration DOMAIN_USED_DURATION = Duration.standardHours(1);
|
||||
private static final Duration DOMAIN_USED_DURATION = Duration.ofHours(1);
|
||||
|
||||
/**
|
||||
* The minimum amount of time we want a domain to be "soft deleted".
|
||||
@@ -82,7 +81,7 @@ public class DeleteProberDataAction implements Runnable {
|
||||
* <p>The domain has to remain soft deleted for at least enough time for the DNS task to run and
|
||||
* remove it from DNS itself. This is probably on the order of minutes.
|
||||
*/
|
||||
private static final Duration SOFT_DELETE_DELAY = Duration.standardHours(1);
|
||||
private static final Duration SOFT_DELETE_DELAY = Duration.ofHours(1);
|
||||
|
||||
// Domains to delete must:
|
||||
// 1. Be in one of the prober TLDs
|
||||
@@ -150,7 +149,7 @@ public class DeleteProberDataAction implements Runnable {
|
||||
AtomicInteger softDeletedDomains = new AtomicInteger();
|
||||
AtomicInteger hardDeletedDomains = new AtomicInteger();
|
||||
AtomicReference<ImmutableList<Domain>> domainsBatch = new AtomicReference<>();
|
||||
DateTime startTime = clock.nowUtc();
|
||||
Instant startTime = clock.now();
|
||||
do {
|
||||
tm().transact(
|
||||
TRANSACTION_REPEATABLE_READ,
|
||||
@@ -169,7 +168,7 @@ public class DeleteProberDataAction implements Runnable {
|
||||
hardDeletedDomains.get(), batchSize);
|
||||
|
||||
// Automatically kill the job if it is running for over 20 hours
|
||||
} while (clock.nowUtc().isBefore(startTime.plusHours(20))
|
||||
} while (clock.now().isBefore(startTime.plus(Duration.ofHours(20)))
|
||||
&& domainsBatch.get().size() == batchSize);
|
||||
logger.atInfo().log(
|
||||
"%s %d domains.",
|
||||
@@ -183,15 +182,14 @@ public class DeleteProberDataAction implements Runnable {
|
||||
ImmutableSet<String> deletableTlds,
|
||||
AtomicInteger softDeletedDomains,
|
||||
AtomicInteger hardDeletedDomains,
|
||||
DateTime now) {
|
||||
Instant now) {
|
||||
TypedQuery<Domain> query =
|
||||
tm().query(DOMAIN_QUERY_STRING, Domain.class)
|
||||
.setParameter("tlds", deletableTlds)
|
||||
.setParameter(
|
||||
"creationTimeCutoff",
|
||||
CreateAutoTimestamp.create(toInstant(now.minus(DOMAIN_USED_DURATION))))
|
||||
.setParameter("nowMinusSoftDeleteDelay", toInstant(now.minus(SOFT_DELETE_DELAY)))
|
||||
.setParameter("now", toInstant(now));
|
||||
"creationTimeCutoff", CreateAutoTimestamp.create(now.minus(DOMAIN_USED_DURATION)))
|
||||
.setParameter("nowMinusSoftDeleteDelay", now.minus(SOFT_DELETE_DELAY))
|
||||
.setParameter("now", now);
|
||||
ImmutableList<Domain> domainList =
|
||||
query.setMaxResults(batchSize).getResultStream().collect(toImmutableList());
|
||||
ImmutableList.Builder<String> domainRepoIdsToHardDelete = new ImmutableList.Builder<>();
|
||||
@@ -218,7 +216,7 @@ public class DeleteProberDataAction implements Runnable {
|
||||
// successfully soft-delete the domain (thus leaving its DNS entry published). We soft-delete
|
||||
// it now so that the DNS entry can be handled. The domain will then be hard-deleted the next
|
||||
// time the job is run.
|
||||
if (EppResourceUtils.isActive(domain, tm().getTransactionTime())) {
|
||||
if (EppResourceUtils.isActive(domain, tm().getTxTime())) {
|
||||
if (isDryRun) {
|
||||
logger.atInfo().log(
|
||||
"Would soft-delete the active domain: %s (%s).",
|
||||
|
||||
@@ -111,7 +111,7 @@ public class ExpandBillingRecurrencesAction implements Runnable {
|
||||
() ->
|
||||
tm().loadByKeyIfPresent(Cursor.createGlobalVKey(RECURRING_BILLING))
|
||||
.orElse(Cursor.createGlobal(RECURRING_BILLING, START_INSTANT))
|
||||
.getCursorTimeInstant()));
|
||||
.getCursorTime()));
|
||||
checkArgument(
|
||||
startTime.isBefore(endTime),
|
||||
"Start time (%s) must be before end time (%s)",
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
@@ -36,13 +37,12 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Task that re-locks a previously-Registry-Locked domain after a predetermined period of time. */
|
||||
@Action(
|
||||
@@ -59,8 +59,8 @@ public class RelockDomainAction implements Runnable {
|
||||
|
||||
static final int ATTEMPTS_BEFORE_SLOWDOWN = 36; // every ten minutes for six hours then every hour
|
||||
static final int FAILURES_BEFORE_EMAIL = 2; // email after three failures, one half hour
|
||||
private static final Duration TEN_MINUTES = Duration.standardMinutes(10);
|
||||
private static final Duration ONE_HOUR = Duration.standardHours(1);
|
||||
private static final Duration TEN_MINUTES = Duration.ofMinutes(10);
|
||||
private static final Duration ONE_HOUR = Duration.ofHours(1);
|
||||
|
||||
private static final String RELOCK_SUCCESS_EMAIL_TEMPLATE =
|
||||
"""
|
||||
@@ -188,7 +188,7 @@ public class RelockDomainAction implements Runnable {
|
||||
"Domain %s has a pending delete.",
|
||||
domainName);
|
||||
checkArgument(
|
||||
!DateTimeUtils.isAtOrAfter(tm().getTxTime(), domain.getDeletionTime()),
|
||||
!isAtOrAfter(tm().getTxTime(), domain.getDeletionTime()),
|
||||
"Domain %s has been deleted.",
|
||||
domainName);
|
||||
checkArgument(
|
||||
|
||||
@@ -30,8 +30,8 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An action that re-saves a given entity, typically after a certain amount of time has passed.
|
||||
@@ -50,16 +50,16 @@ public class ResaveEntityAction implements Runnable {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final String resourceKey;
|
||||
private final DateTime requestedTime;
|
||||
private final ImmutableSortedSet<DateTime> resaveTimes;
|
||||
private final Instant requestedTime;
|
||||
private final ImmutableSortedSet<Instant> resaveTimes;
|
||||
private final AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||
private final Response response;
|
||||
|
||||
@Inject
|
||||
ResaveEntityAction(
|
||||
@Parameter(PARAM_RESOURCE_KEY) String resourceKey,
|
||||
@Parameter(PARAM_REQUESTED_TIME) DateTime requestedTime,
|
||||
@Parameter(PARAM_RESAVE_TIMES) ImmutableSet<DateTime> resaveTimes,
|
||||
@Parameter(PARAM_REQUESTED_TIME) Instant requestedTime,
|
||||
@Parameter(PARAM_RESAVE_TIMES) ImmutableSet<Instant> resaveTimes,
|
||||
AsyncTaskEnqueuer asyncTaskEnqueuer,
|
||||
Response response) {
|
||||
this.resourceKey = resourceKey;
|
||||
|
||||
+23
-25
@@ -16,9 +16,8 @@ package google.registry.batch;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.toDateTime;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static java.time.ZoneOffset.UTC;
|
||||
import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
|
||||
@@ -43,11 +42,10 @@ import google.registry.util.EmailMessage;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
/** An action that sends notification emails to registrars whose certificates are expiring soon. */
|
||||
@Action(
|
||||
@@ -57,6 +55,7 @@ import org.joda.time.format.DateTimeFormatter;
|
||||
public class SendExpiringCertificateNotificationEmailAction implements Runnable {
|
||||
|
||||
public static final String PATH = "/_dr/task/sendExpiringCertificateNotificationEmail";
|
||||
|
||||
/**
|
||||
* Used as an offset when storing the last notification email sent date.
|
||||
*
|
||||
@@ -65,10 +64,11 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
* next day at 2am, the date difference will be less than a day, which will lead to the date
|
||||
* difference between two successive email sent date being the expected email interval days + 1;
|
||||
*/
|
||||
protected static final Duration UPDATE_TIME_OFFSET = Duration.standardMinutes(10);
|
||||
protected static final Duration UPDATE_TIME_OFFSET = Duration.ofMinutes(10);
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd");
|
||||
private static final DateTimeFormatter DATE_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(UTC);
|
||||
|
||||
private final CertificateChecker certificateChecker;
|
||||
private final String expirationWarningEmailBodyText;
|
||||
@@ -130,11 +130,11 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
registrar,
|
||||
registrar.getClientCertificate().isPresent()
|
||||
&& certificateChecker.shouldReceiveExpiringNotification(
|
||||
toDateTime(registrar.getLastExpiringCertNotificationSentDate()),
|
||||
registrar.getLastExpiringCertNotificationSentDate(),
|
||||
registrar.getClientCertificate().get()),
|
||||
registrar.getFailoverClientCertificate().isPresent()
|
||||
&& certificateChecker.shouldReceiveExpiringNotification(
|
||||
toDateTime(registrar.getLastExpiringFailoverCertNotificationSentDate()),
|
||||
registrar.getLastExpiringFailoverCertNotificationSentDate(),
|
||||
registrar.getFailoverClientCertificate().get())))
|
||||
.filter(
|
||||
registrarInfo ->
|
||||
@@ -149,7 +149,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
@VisibleForTesting
|
||||
boolean sendNotificationEmail(
|
||||
Registrar registrar,
|
||||
DateTime lastExpiringCertNotificationSentDate,
|
||||
Instant lastExpiringCertNotificationSentDate,
|
||||
CertificateType certificateType,
|
||||
Optional<String> certificate) {
|
||||
if (certificate.isEmpty()
|
||||
@@ -160,8 +160,8 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
try {
|
||||
ImmutableSet<InternetAddress> recipients = getEmailAddresses(registrar, Type.TECH);
|
||||
ImmutableSet<InternetAddress> ccs = getEmailAddresses(registrar, Type.ADMIN);
|
||||
DateTime expirationDate =
|
||||
new DateTime(certificateChecker.getCertificate(certificate.get()).getNotAfter());
|
||||
Instant expirationDate =
|
||||
certificateChecker.getCertificate(certificate.get()).getNotAfter().toInstant();
|
||||
logger.atInfo().log(
|
||||
" %s SSL certificate of registrar '%s' will expire on %s.",
|
||||
certificateType.getDisplayName(), registrar.getRegistrarName(), expirationDate);
|
||||
@@ -190,9 +190,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
* for applicable certificate.
|
||||
*/
|
||||
updateLastNotificationSentDate(
|
||||
registrar,
|
||||
clock.nowUtc().minusMinutes((int) UPDATE_TIME_OFFSET.getStandardMinutes()),
|
||||
certificateType);
|
||||
registrar, clock.now().minus(UPDATE_TIME_OFFSET), certificateType);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
@@ -205,29 +203,29 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
/** Updates the last notification sent date in database. */
|
||||
@VisibleForTesting
|
||||
void updateLastNotificationSentDate(
|
||||
Registrar registrar, DateTime now, CertificateType certificateType) {
|
||||
Registrar registrar, Instant now, CertificateType certificateType) {
|
||||
try {
|
||||
tm().transact(
|
||||
() -> {
|
||||
Registrar.Builder newRegistrar = tm().loadByEntity(registrar).asBuilder();
|
||||
switch (certificateType) {
|
||||
case PRIMARY -> {
|
||||
newRegistrar.setLastExpiringCertNotificationSentDate(toInstant(now));
|
||||
newRegistrar.setLastExpiringCertNotificationSentDate(now);
|
||||
tm().put(newRegistrar.build());
|
||||
logger.atInfo().log(
|
||||
"Updated last notification email sent date to %s for %s certificate of "
|
||||
+ "registrar %s.",
|
||||
DATE_FORMATTER.print(now),
|
||||
DATE_FORMATTER.format(now),
|
||||
certificateType.getDisplayName(),
|
||||
registrar.getRegistrarName());
|
||||
}
|
||||
case FAILOVER -> {
|
||||
newRegistrar.setLastExpiringFailoverCertNotificationSentDate(toInstant(now));
|
||||
newRegistrar.setLastExpiringFailoverCertNotificationSentDate(now);
|
||||
tm().put(newRegistrar.build());
|
||||
logger.atInfo().log(
|
||||
"Updated last notification email sent date to %s for %s certificate of "
|
||||
+ "registrar %s.",
|
||||
DATE_FORMATTER.print(now),
|
||||
DATE_FORMATTER.format(now),
|
||||
certificateType.getDisplayName(),
|
||||
registrar.getRegistrarName());
|
||||
}
|
||||
@@ -257,7 +255,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
if (registrarInfo.isCertExpiring()
|
||||
&& sendNotificationEmail(
|
||||
registrar,
|
||||
toDateTime(registrar.getLastExpiringCertNotificationSentDate()),
|
||||
registrar.getLastExpiringCertNotificationSentDate(),
|
||||
CertificateType.PRIMARY,
|
||||
registrar.getClientCertificate())) {
|
||||
numEmailsSent++;
|
||||
@@ -265,7 +263,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
if (registrarInfo.isFailOverCertExpiring()
|
||||
&& sendNotificationEmail(
|
||||
registrar,
|
||||
toDateTime(registrar.getLastExpiringFailoverCertNotificationSentDate()),
|
||||
registrar.getLastExpiringFailoverCertNotificationSentDate(),
|
||||
CertificateType.FAILOVER,
|
||||
registrar.getFailoverClientCertificate())) {
|
||||
numEmailsSent++;
|
||||
@@ -300,7 +298,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
@VisibleForTesting
|
||||
@SuppressWarnings("lgtm[java/dereferenced-value-may-be-null]")
|
||||
String getEmailBody(
|
||||
String registrarName, CertificateType type, DateTime expirationDate, String registrarId) {
|
||||
String registrarName, CertificateType type, Instant expirationDate, String registrarId) {
|
||||
checkArgumentNotNull(expirationDate, "Expiration date cannot be null");
|
||||
checkArgumentNotNull(type, "Certificate type cannot be null");
|
||||
checkArgumentNotNull(registrarId, "Registrar Id cannot be null");
|
||||
@@ -308,7 +306,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
expirationWarningEmailBodyText,
|
||||
registrarName,
|
||||
type.getDisplayName(),
|
||||
DATE_FORMATTER.print(expirationDate),
|
||||
DATE_FORMATTER.format(expirationDate),
|
||||
registrarId);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
// Copyright 2026 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.batch;
|
||||
|
||||
import static google.registry.model.common.Cursor.CursorType.REMOTE_CACHE_DOMAIN_SYNC;
|
||||
import static google.registry.model.common.Cursor.CursorType.REMOTE_CACHE_HOST_SYNC;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.cache.SimplifiedJedisClient;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = SyncRemoteCacheAction.PATH,
|
||||
method = POST,
|
||||
auth = Auth.AUTH_ADMIN)
|
||||
public class SyncRemoteCacheAction implements Runnable {
|
||||
|
||||
public static final String PATH = "/_dr/task/syncRemoteCache";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final String LOCK_NAME = "syncRemoteCacheAction";
|
||||
private static final int BATCH_SIZE = 10000;
|
||||
|
||||
private final LockHandler lockHandler;
|
||||
private final Response response;
|
||||
private final Optional<SimplifiedJedisClient> jedisClient;
|
||||
|
||||
@Inject
|
||||
public SyncRemoteCacheAction(
|
||||
LockHandler lockHandler, Response response, Optional<SimplifiedJedisClient> jedisClient) {
|
||||
this.lockHandler = lockHandler;
|
||||
this.response = response;
|
||||
this.jedisClient = jedisClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
if (jedisClient.isEmpty()) {
|
||||
response.setStatus(SC_NO_CONTENT);
|
||||
response.setPayload("No Jedis/Valkey configuration found");
|
||||
return;
|
||||
}
|
||||
Callable<Void> runner =
|
||||
() -> {
|
||||
try {
|
||||
runLocked();
|
||||
response.setStatus(SC_OK);
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log("Errored out during execution.");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(String.format("Errored out with cause: %s", e));
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
if (!lockHandler.executeWithLocks(runner, null, Duration.ofHours(1), LOCK_NAME)) {
|
||||
// Send a 200-series status code to prevent this conflicting action from retrying.
|
||||
response.setStatus(SC_NO_CONTENT);
|
||||
response.setPayload("Could not acquire lock; already running?");
|
||||
}
|
||||
}
|
||||
|
||||
private void runLocked() {
|
||||
// Note: the transaction ordering means that cursors in our database are updated only if all the
|
||||
// operations for the entity type succeeded. There is no downside to processing the same objects
|
||||
// multiple times if, for some reason, saving the cursor to the DB fails.
|
||||
int syncedDomains = tm().transact(this::syncDomains);
|
||||
int syncedHosts = tm().transact(this::syncHosts);
|
||||
String message = String.format("Synced %d domains and %d hosts.", syncedDomains, syncedHosts);
|
||||
logger.atInfo().log(message);
|
||||
response.setPayload(message);
|
||||
}
|
||||
|
||||
private int syncDomains() {
|
||||
Instant domainCursorTime = getPreviousCursorTime(REMOTE_CACHE_DOMAIN_SYNC);
|
||||
ImmutableSet<String> realTlds = Tlds.getTldsOfType(Tld.TldType.REAL);
|
||||
List<Domain> domains =
|
||||
tm().query(
|
||||
"FROM Domain WHERE updateTimestamp.lastUpdateTime > :cursorTime AND tld IN"
|
||||
+ " :realTlds ORDER BY updateTimestamp ASC",
|
||||
Domain.class)
|
||||
.setParameter("cursorTime", domainCursorTime)
|
||||
.setParameter("realTlds", realTlds)
|
||||
.setMaxResults(BATCH_SIZE)
|
||||
.getResultList();
|
||||
if (domains.isEmpty()) {
|
||||
logger.atInfo().log("No domains to process");
|
||||
return 0;
|
||||
}
|
||||
logger.atInfo().log("Processing %d domains", domains.size());
|
||||
processResources(Domain.class, domains, Domain::getDomainName);
|
||||
setNewCursorTime(domains, REMOTE_CACHE_DOMAIN_SYNC);
|
||||
return domains.size();
|
||||
}
|
||||
|
||||
private int syncHosts() {
|
||||
Instant hostCursorTime = getPreviousCursorTime(REMOTE_CACHE_HOST_SYNC);
|
||||
List<Host> hosts =
|
||||
tm().query(
|
||||
"FROM Host WHERE updateTimestamp.lastUpdateTime > :cursorTime ORDER BY"
|
||||
+ " updateTimestamp ASC",
|
||||
Host.class)
|
||||
.setParameter("cursorTime", hostCursorTime)
|
||||
.setMaxResults(BATCH_SIZE)
|
||||
.getResultList();
|
||||
if (hosts.isEmpty()) {
|
||||
logger.atInfo().log("No hosts to process");
|
||||
return 0;
|
||||
}
|
||||
logger.atInfo().log("Processing %d hosts", hosts.size());
|
||||
processResources(Host.class, hosts, Host::getRepoId);
|
||||
setNewCursorTime(hosts, REMOTE_CACHE_HOST_SYNC);
|
||||
return hosts.size();
|
||||
}
|
||||
|
||||
private <T extends EppResource> void processResources(
|
||||
Class<T> clazz, List<T> resources, Function<T, String> getKeyFunction) {
|
||||
ImmutableList.Builder<String> toDeleteBuilder = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<SimplifiedJedisClient.JedisResource<T>> toSaveBuilder =
|
||||
new ImmutableList.Builder<>();
|
||||
|
||||
for (T resource : resources) {
|
||||
String key = getKeyFunction.apply(resource);
|
||||
if (resource.getDeletionTime().isAfter(tm().getTxTime())) {
|
||||
toSaveBuilder.add(new SimplifiedJedisClient.JedisResource<>(key, resource));
|
||||
} else {
|
||||
toDeleteBuilder.add(key);
|
||||
}
|
||||
}
|
||||
ImmutableList<String> toDelete = toDeleteBuilder.build();
|
||||
ImmutableList<SimplifiedJedisClient.JedisResource<T>> toSave = toSaveBuilder.build();
|
||||
|
||||
jedisClient.get().deleteAll(clazz, toDelete);
|
||||
logger.atInfo().log("Invalidated %d from the remote cache", toDelete.size());
|
||||
jedisClient.get().setAll(toSave);
|
||||
logger.atInfo().log("Set %d in the remote cache", toSave.size());
|
||||
}
|
||||
|
||||
private Instant getPreviousCursorTime(Cursor.CursorType cursorType) {
|
||||
return tm().loadByKeyIfPresent(Cursor.createGlobalVKey(cursorType))
|
||||
.map(Cursor::getCursorTime)
|
||||
.orElse(START_INSTANT);
|
||||
}
|
||||
|
||||
private void setNewCursorTime(
|
||||
List<? extends EppResource> resources, Cursor.CursorType cursorType) {
|
||||
Instant lastUpdateTime = Iterables.getLast(resources).getUpdateTimestamp().getTimestamp();
|
||||
tm().put(Cursor.createGlobal(cursorType, lastUpdateTime));
|
||||
logger.atInfo().log("Set new %s cursor time to %s", cursorType, lastUpdateTime);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.beam;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.util.DateTimeUtils.LOWERCASE_TIMESTAMP_FORMATTER;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -61,7 +62,7 @@ public class BeamUtils {
|
||||
// with a letter and ending with a letter or number. So we replace the "T" and "Z" in ISO 8601
|
||||
// with lowercase letters.
|
||||
String jobName =
|
||||
String.format("%s-%s", prefix, clock.nowUtc().toString("yyyy-MM-dd't'HH-mm-ss'z'"));
|
||||
String.format("%s-%s", prefix, LOWERCASE_TIMESTAMP_FORMATTER.format(clock.now()));
|
||||
checkArgument(
|
||||
Pattern.compile("^[a-z][-a-z0-9]*[a-z0-9]*").matcher(jobName).matches(),
|
||||
"The job name %s is illegal, it consists of only characters [-a-z0-9], "
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package google.registry.beam.billing;
|
||||
|
||||
import static google.registry.util.DateTimeUtils.toLocalDate;
|
||||
import static java.time.ZoneOffset.UTC;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -22,8 +24,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.beam.sdk.coders.AtomicCoder;
|
||||
@@ -70,7 +70,7 @@ public record BillingEvent(
|
||||
String flags) {
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss 'UTC'").withZone(ZoneOffset.UTC);
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss 'UTC'").withZone(UTC);
|
||||
|
||||
private static final Pattern SYNTHETIC_REGEX = Pattern.compile("SYNTHETIC", Pattern.LITERAL);
|
||||
|
||||
@@ -164,14 +164,10 @@ public record BillingEvent(
|
||||
/** Returns the grouping key for this {@code BillingEvent}, to generate the overall invoice. */
|
||||
InvoiceGroupingKey getInvoiceGroupingKey() {
|
||||
return new InvoiceGroupingKey(
|
||||
ZonedDateTime.ofInstant(billingTime(), ZoneOffset.UTC)
|
||||
.toLocalDate()
|
||||
.withDayOfMonth(1)
|
||||
.toString(),
|
||||
toLocalDate(billingTime()).withDayOfMonth(1).toString(),
|
||||
years() == 0
|
||||
? ""
|
||||
: ZonedDateTime.ofInstant(billingTime(), ZoneOffset.UTC)
|
||||
.toLocalDate()
|
||||
: toLocalDate(billingTime())
|
||||
.withDayOfMonth(1)
|
||||
.plusYears(years())
|
||||
.minusDays(1)
|
||||
|
||||
+2
-2
@@ -311,7 +311,7 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
|
||||
for (Instant eventTime : eventTimesToExpand) {
|
||||
recurrenceLastExpansionTime = latestOf(recurrenceLastExpansionTime, eventTime);
|
||||
oneTimesToExpandCounter.inc();
|
||||
Instant billingTime = eventTime.plusMillis(tld.getAutoRenewGracePeriodLength().getMillis());
|
||||
Instant billingTime = eventTime.plus(tld.getAutoRenewGracePeriodLength());
|
||||
// Note that the DomainHistory is created as of transaction time, as opposed to event time.
|
||||
// This might be counterintuitive because other DomainHistories are created at the time
|
||||
// mutation events occur, such as in DomainDeleteFlow or DomainRenewFlow. Therefore, it is
|
||||
@@ -445,7 +445,7 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
|
||||
Cursor.createGlobalVKey(RECURRING_BILLING))
|
||||
.orElse(
|
||||
Cursor.createGlobal(RECURRING_BILLING, START_INSTANT))
|
||||
.getCursorTimeInstant();
|
||||
.getCursorTime();
|
||||
if (!currentCursorTime.equals(startTime)) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2026 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.beam.common;
|
||||
|
||||
import static com.google.common.base.Verify.verify;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import java.io.Serializable;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
import org.apache.beam.sdk.PipelineResult;
|
||||
import org.apache.beam.sdk.coders.StringUtf8Coder;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
import org.apache.beam.sdk.transforms.Count;
|
||||
import org.apache.beam.sdk.transforms.DoFn;
|
||||
import org.apache.beam.sdk.transforms.ParDo;
|
||||
|
||||
/**
|
||||
* For smoke test in the build/deployment process.
|
||||
*
|
||||
* <p>There two coverage gaps in unit tests for BEAM pipelines:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The compatibility of the JVM and SDK in the pipeline image
|
||||
* <li>The JPA setup, which is performed by the {@link RegistryPipelineWorkerInitializer}
|
||||
* </ul>
|
||||
*
|
||||
* <p>This classes defines a pipeline that performs one quick database query. The pipeline is
|
||||
* expected to complete quickly, and the build or deployment process may launch it on GCP and wait
|
||||
* for its completion to be certain that all aspects are tested for Nomulus pipelines.
|
||||
*/
|
||||
public class SmokeTestPipeline implements Serializable {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
public static void main(String[] args) {
|
||||
PipelineOptionsFactory.register(RegistryPipelineOptions.class);
|
||||
RegistryPipelineOptions options =
|
||||
PipelineOptionsFactory.fromArgs(args).withValidation().as(RegistryPipelineOptions.class);
|
||||
runPipeline(options);
|
||||
}
|
||||
|
||||
static PipelineResult runPipeline(RegistryPipelineOptions options) {
|
||||
Pipeline pipeline = Pipeline.create(options);
|
||||
pipeline
|
||||
.apply(
|
||||
"Read Tlds",
|
||||
RegistryJpaIO.read(() -> CriteriaQueryBuilder.create(Tld.class).build(), Tld::getTldStr)
|
||||
.withCoder(StringUtf8Coder.of()))
|
||||
.apply("Count Tlds", Count.globally())
|
||||
.apply(
|
||||
"Verify Count",
|
||||
ParDo.of(
|
||||
new DoFn<Long, Void>() {
|
||||
@DoFn.ProcessElement
|
||||
public void processElement(@Element Long count) {
|
||||
logger.atInfo().log("Tld count: %s", count);
|
||||
verify(count > 0, "Expecting 1 or more, got %s.", count);
|
||||
}
|
||||
}));
|
||||
|
||||
return pipeline.run();
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,8 @@ import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.security.Security;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.apache.beam.sdk.options.PipelineOptions;
|
||||
import org.apache.beam.sdk.transforms.DoFn;
|
||||
@@ -66,8 +68,6 @@ import org.apache.beam.sdk.values.PCollection;
|
||||
import org.apache.beam.sdk.values.PDone;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
public class RdeIO {
|
||||
|
||||
@@ -163,7 +163,7 @@ public class RdeIO {
|
||||
// Determine some basic things about the deposit.
|
||||
final RdeMode mode = key.mode();
|
||||
final String tld = key.tld();
|
||||
final DateTime watermark = key.watermark();
|
||||
final Instant watermark = key.watermark();
|
||||
final int revision =
|
||||
Optional.ofNullable(key.revision())
|
||||
.orElseGet(() -> RdeRevision.getNextRevision(tld, watermark, mode));
|
||||
@@ -259,7 +259,7 @@ public class RdeIO {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final long serialVersionUID = 5822176227753327224L;
|
||||
private static final Duration ENQUEUE_DELAY = Duration.standardMinutes(1);
|
||||
private static final Duration ENQUEUE_DELAY = Duration.ofMinutes(1);
|
||||
|
||||
private final CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@@ -276,9 +276,9 @@ public class RdeIO {
|
||||
Tld tld = Tld.get(key.tld());
|
||||
Optional<Cursor> cursor =
|
||||
tm().loadByKeyIfPresent(Cursor.createScopedVKey(key.cursor(), tld));
|
||||
DateTime position = getCursorTimeOrStartOfTime(cursor);
|
||||
Instant position = getCursorTimeOrStartOfTime(cursor);
|
||||
checkState(key.interval() != null, "Interval must be present");
|
||||
DateTime newPosition = key.watermark().plus(key.interval());
|
||||
Instant newPosition = key.watermark().plus(key.interval());
|
||||
if (!position.isBefore(newPosition)) {
|
||||
logger.atWarning().log("Cursor has already been rolled forward.");
|
||||
return;
|
||||
|
||||
@@ -27,7 +27,6 @@ import static google.registry.beam.rde.RdePipeline.TupleTags.REVISION_ID;
|
||||
import static google.registry.beam.rde.RdePipeline.TupleTags.SUPERORDINATE_DOMAINS;
|
||||
import static google.registry.model.reporting.HistoryEntryDao.RESOURCE_TYPES_TO_HISTORY_TYPES;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
import static google.registry.util.SafeSerializationUtils.safeDeserializeCollection;
|
||||
import static google.registry.util.SafeSerializationUtils.serializeCollection;
|
||||
import static google.registry.util.SerializeUtils.decodeBase64;
|
||||
@@ -72,6 +71,7 @@ import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
import org.apache.beam.sdk.PipelineResult;
|
||||
import org.apache.beam.sdk.coders.KvCoder;
|
||||
@@ -97,7 +97,6 @@ import org.apache.beam.sdk.values.PCollectionTuple;
|
||||
import org.apache.beam.sdk.values.TupleTag;
|
||||
import org.apache.beam.sdk.values.TupleTagList;
|
||||
import org.apache.beam.sdk.values.TypeDescriptor;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Definition of a Dataflow Flex template, which generates RDE/BRDA deposits.
|
||||
@@ -171,7 +170,7 @@ public class RdePipeline implements Serializable {
|
||||
private final transient RdePipelineOptions options;
|
||||
private final ValidationMode mode;
|
||||
private final ImmutableSet<PendingDeposit> pendingDeposits;
|
||||
private final DateTime watermark;
|
||||
private final Instant watermark;
|
||||
private final String rdeBucket;
|
||||
private final byte[] stagingKeyBytes;
|
||||
private final GcsUtils gcsUtils;
|
||||
@@ -190,7 +189,7 @@ public class RdePipeline implements Serializable {
|
||||
this.options = options;
|
||||
this.mode = ValidationMode.valueOf(options.getValidationMode());
|
||||
this.pendingDeposits = decodePendingDeposits(options.getPendings());
|
||||
ImmutableSet<DateTime> potentialWatermarks =
|
||||
ImmutableSet<Instant> potentialWatermarks =
|
||||
pendingDeposits.stream()
|
||||
.map(PendingDeposit::watermark)
|
||||
.distinct()
|
||||
@@ -324,7 +323,7 @@ public class RdePipeline implements Serializable {
|
||||
? "AND resource.tld IN " + "(SELECT id FROM Tld WHERE tldType = 'REAL')"
|
||||
: ""))
|
||||
.replace("%entity%", historyClass.getSimpleName()),
|
||||
ImmutableMap.of("watermark", toInstant(watermark)),
|
||||
ImmutableMap.of("watermark", watermark),
|
||||
Object[].class,
|
||||
row -> KV.of((String) row[0], (long) row[1]))
|
||||
.withCoder(KvCoder.of(StringUtf8Coder.of(), VarLongCoder.of())));
|
||||
@@ -365,7 +364,7 @@ public class RdePipeline implements Serializable {
|
||||
tm().loadByKey(
|
||||
VKey.create(historyEntryClazz, new HistoryEntryId(repoId, revisionId))))
|
||||
.getResourceAtPointInTime()
|
||||
.map(resource -> resource.cloneProjectedAtTime(toInstant(watermark)))
|
||||
.map(resource -> resource.cloneProjectedAtTime(watermark))
|
||||
.get();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.beam.resave;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.END_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.integers;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -33,6 +32,7 @@ import google.registry.model.host.Host;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
import org.apache.beam.sdk.PipelineResult;
|
||||
import org.apache.beam.sdk.coders.StringUtf8Coder;
|
||||
@@ -43,13 +43,12 @@ import org.apache.beam.sdk.transforms.ParDo;
|
||||
import org.apache.beam.sdk.transforms.WithKeys;
|
||||
import org.apache.beam.sdk.util.ShardedKey;
|
||||
import org.apache.beam.sdk.values.KV;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A Dataflow Flex pipeline that resaves changed EPP resources in SQL.
|
||||
*
|
||||
* <p>Due to the way that Hibernate works, if an entity is unchanged by {@link
|
||||
* EppResource#cloneProjectedAtTime(DateTime)} it will not actually be re-persisted to the database.
|
||||
* EppResource#cloneProjectedAtTime(Instant)} it will not actually be re-persisted to the database.
|
||||
* Thus, the only actual changes occur when objects are changed by projecting them to now, such as
|
||||
* when a pending transfer is resolved.
|
||||
*/
|
||||
@@ -103,7 +102,7 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
* transfers, grace periods).
|
||||
*
|
||||
* <p>The logic of what might have changed is paraphrased from {@link
|
||||
* DomainBase#cloneProjectedAtTime(DateTime)}.
|
||||
* DomainBase#cloneProjectedAtTime(Instant)}.
|
||||
*/
|
||||
private void fastResaveDomains(Pipeline pipeline) {
|
||||
Read<String, String> repoIdRead =
|
||||
@@ -158,14 +157,14 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
public void processElement(@Element KV<ShardedKey<Integer>, Iterable<String>> element) {
|
||||
tm().transact(
|
||||
() -> {
|
||||
DateTime now = tm().getTransactionTime();
|
||||
Instant now = tm().getTxTime();
|
||||
ImmutableList<VKey<? extends EppResource>> keys =
|
||||
Streams.stream(element.getValue())
|
||||
.map(repoId -> VKey.create(clazz, repoId))
|
||||
.collect(toImmutableList());
|
||||
ImmutableList<EppResource> mappedResources =
|
||||
tm().loadByKeys(keys).values().stream()
|
||||
.map(r -> r.cloneProjectedAtTime(toInstant(now)))
|
||||
.map(r -> r.cloneProjectedAtTime(now))
|
||||
.collect(toImmutableList());
|
||||
tm().putAll(mappedResources);
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.beam.spec11;
|
||||
|
||||
import static google.registry.util.DateTimeUtils.toJodaInstant;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
|
||||
@@ -22,7 +23,6 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.CharStreams;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import google.registry.util.Retrier;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -132,9 +132,7 @@ public class SafeBrowsingTransforms {
|
||||
if (!domainNameInfoBuffer.isEmpty()) {
|
||||
ImmutableSet<KV<DomainNameInfo, ThreatMatch>> results = evaluateAndFlush();
|
||||
results.forEach(
|
||||
(kv) ->
|
||||
context.output(
|
||||
kv, DateTimeUtils.toJodaInstant(clock.now()), GlobalWindow.INSTANCE));
|
||||
(kv) -> context.output(kv, toJodaInstant(clock.now()), GlobalWindow.INSTANCE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ import google.registry.util.Retrier;
|
||||
import google.registry.util.UtilsModule;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
import org.apache.beam.sdk.PipelineResult;
|
||||
import org.apache.beam.sdk.coders.KvCoder;
|
||||
@@ -50,9 +52,6 @@ import org.apache.beam.sdk.values.KV;
|
||||
import org.apache.beam.sdk.values.PCollection;
|
||||
import org.apache.beam.sdk.values.TypeDescriptor;
|
||||
import org.apache.beam.sdk.values.TypeDescriptors;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.YearMonth;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -76,7 +75,7 @@ public class Spec11Pipeline implements Serializable {
|
||||
* @see google.registry.reporting.spec11.Spec11EmailUtils
|
||||
*/
|
||||
public static String getSpec11ReportFilePath(LocalDate localDate) {
|
||||
YearMonth yearMonth = new YearMonth(localDate);
|
||||
YearMonth yearMonth = YearMonth.from(localDate);
|
||||
return String.format("icann/spec11/%s/SPEC11_MONTHLY_REPORT_%s", yearMonth, localDate);
|
||||
}
|
||||
|
||||
@@ -155,7 +154,7 @@ public class Spec11Pipeline implements Serializable {
|
||||
|
||||
static void saveToSql(
|
||||
PCollection<KV<DomainNameInfo, ThreatMatch>> threatMatches, Spec11PipelineOptions options) {
|
||||
LocalDate date = LocalDate.parse(options.getDate(), ISODateTimeFormat.date());
|
||||
LocalDate date = LocalDate.parse(options.getDate());
|
||||
String transformId = "Spec11 Threat Matches";
|
||||
threatMatches
|
||||
.apply(
|
||||
|
||||
@@ -64,19 +64,19 @@ import google.registry.util.SqlTemplate;
|
||||
import google.registry.util.SystemSleeper;
|
||||
import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Class encapsulating parameters and state for accessing the Bigquery API. */
|
||||
public class BigqueryConnection implements AutoCloseable {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final Duration MIN_POLL_INTERVAL = Duration.millis(500);
|
||||
private static final Duration MIN_POLL_INTERVAL = Duration.ofMillis(500);
|
||||
|
||||
@NonFinalForTesting
|
||||
private static Sleeper sleeper = new SystemSleeper();
|
||||
@@ -88,7 +88,7 @@ public class BigqueryConnection implements AutoCloseable {
|
||||
private static final String TEMP_DATASET_NAME = "__temp__";
|
||||
|
||||
/** Default time to live for temporary tables. */
|
||||
private static final Duration TEMP_TABLE_TTL = Duration.standardHours(24);
|
||||
private static final Duration TEMP_TABLE_TTL = Duration.ofHours(24);
|
||||
|
||||
/** Bigquery client instance wrapped by this class. */
|
||||
private final Bigquery bigquery;
|
||||
@@ -109,7 +109,7 @@ public class BigqueryConnection implements AutoCloseable {
|
||||
private boolean overwrite = false;
|
||||
|
||||
/** Duration to wait between polls for job status. */
|
||||
private Duration pollInterval = Duration.millis(1000);
|
||||
private Duration pollInterval = Duration.ofSeconds(1);
|
||||
|
||||
BigqueryConnection(Bigquery bigquery, Clock clock) {
|
||||
this.bigquery = bigquery;
|
||||
@@ -146,9 +146,9 @@ public class BigqueryConnection implements AutoCloseable {
|
||||
|
||||
public Builder setPollInterval(Duration pollInterval) {
|
||||
checkArgument(
|
||||
!pollInterval.isShorterThan(MIN_POLL_INTERVAL),
|
||||
pollInterval.compareTo(MIN_POLL_INTERVAL) >= 0,
|
||||
"poll interval must be at least %s ms",
|
||||
MIN_POLL_INTERVAL.getMillis());
|
||||
MIN_POLL_INTERVAL.toMillis());
|
||||
instance.pollInterval = pollInterval;
|
||||
return this;
|
||||
}
|
||||
@@ -225,7 +225,7 @@ public class BigqueryConnection implements AutoCloseable {
|
||||
}
|
||||
|
||||
public Builder timeToLive(Duration duration) {
|
||||
this.table.setExpirationTime(clock.nowUtc().plus(duration).getMillis());
|
||||
this.table.setExpirationTime(clock.now().plus(duration).toEpochMilli());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,16 @@
|
||||
|
||||
package google.registry.bigquery;
|
||||
|
||||
import static java.time.ZoneOffset.UTC;
|
||||
import static java.time.temporal.ChronoUnit.MICROS;
|
||||
|
||||
import com.google.api.services.bigquery.model.JobReference;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.SignStyle;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.joda.time.format.DateTimeFormatterBuilder;
|
||||
import org.joda.time.format.DateTimeParser;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
|
||||
/** Utilities related to Bigquery. */
|
||||
public class BigqueryUtils {
|
||||
@@ -67,47 +70,56 @@ public class BigqueryUtils {
|
||||
*
|
||||
* <p>The general format definition is "YYYY-MM-DD HH:MM:SS.SSS[ ZZ]", where the fractional
|
||||
* seconds portion can have 0-6 decimal places (although we restrict it to 0-3 here since Joda
|
||||
* DateTime only supports up to millisecond precision) and the zone if not specified defaults to
|
||||
* Instant only supports up to millisecond precision) and the zone if not specified defaults to
|
||||
* UTC.
|
||||
*
|
||||
* <p>Although we expect a zone specification of "UTC" when parsing, we don't emit it when
|
||||
* printing because in some cases BigQuery does not allow any time zone specification (instead it
|
||||
* assumes UTC for whatever input you provide) for input timestamp strings (see b/16380363).
|
||||
*
|
||||
* @see <a href="https://cloud.google.com/bigquery/data-types#timestamp-type">
|
||||
* BigQuery Data Types - TIMESTAMP</a>
|
||||
* @see <a href="https://cloud.google.com/bigquery/data-types#timestamp-type">BigQuery Data Types
|
||||
* - TIMESTAMP</a>
|
||||
*/
|
||||
public static final DateTimeFormatter BIGQUERY_TIMESTAMP_FORMAT = new DateTimeFormatterBuilder()
|
||||
.append(ISODateTimeFormat.date())
|
||||
.appendLiteral(' ')
|
||||
.append(
|
||||
// For printing, always print out the milliseconds.
|
||||
ISODateTimeFormat.hourMinuteSecondMillis().getPrinter(),
|
||||
// For parsing, we need a series of parsers to correctly handle the milliseconds.
|
||||
new DateTimeParser[] {
|
||||
// Try to parse the time with milliseconds first, which requires at least one
|
||||
// fractional second digit, and if that fails try to parse without milliseconds.
|
||||
ISODateTimeFormat.hourMinuteSecondMillis().getParser(),
|
||||
ISODateTimeFormat.hourMinuteSecond().getParser()})
|
||||
// Print UTC as the empty string since BigQuery's TIMESTAMP() function does not accept any
|
||||
// time zone specification, but require "UTC" on parsing. Since we force this formatter to
|
||||
// always use UTC below, the other arguments do not matter. If b/16380363 ever gets resolved
|
||||
// this could be simplified to appendLiteral(" UTC").
|
||||
.appendTimeZoneOffset("", " UTC", false, 1, 1)
|
||||
.toFormatter()
|
||||
.withZoneUTC();
|
||||
private static final DateTimeFormatter BIGQUERY_TIMESTAMP_PARSER =
|
||||
new DateTimeFormatterBuilder()
|
||||
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.NOT_NEGATIVE)
|
||||
.appendLiteral('-')
|
||||
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
|
||||
.appendLiteral('-')
|
||||
.appendValue(ChronoField.DAY_OF_MONTH, 2)
|
||||
.appendLiteral(' ')
|
||||
.appendPattern("HH:mm:ss")
|
||||
.optionalStart()
|
||||
.appendFraction(ChronoField.MILLI_OF_SECOND, 0, 3, true)
|
||||
.optionalEnd()
|
||||
.appendLiteral(" UTC")
|
||||
.toFormatter()
|
||||
.withZone(UTC);
|
||||
|
||||
private static final DateTimeFormatter BIGQUERY_TIMESTAMP_PRINTER =
|
||||
new DateTimeFormatterBuilder()
|
||||
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.NOT_NEGATIVE)
|
||||
.appendLiteral('-')
|
||||
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
|
||||
.appendLiteral('-')
|
||||
.appendValue(ChronoField.DAY_OF_MONTH, 2)
|
||||
.appendLiteral(' ')
|
||||
.appendPattern("HH:mm:ss")
|
||||
.appendFraction(ChronoField.MILLI_OF_SECOND, 3, 3, true)
|
||||
.toFormatter()
|
||||
.withZone(UTC);
|
||||
|
||||
/**
|
||||
* Returns the human-readable string version of the given DateTime, suitable for conversion
|
||||
* within BigQuery from a string literal into a BigQuery timestamp type.
|
||||
* Returns the human-readable string version of the given Instant, suitable for conversion within
|
||||
* BigQuery from a string literal into a BigQuery timestamp type.
|
||||
*/
|
||||
public static String toBigqueryTimestampString(DateTime dateTime) {
|
||||
return BIGQUERY_TIMESTAMP_FORMAT.print(dateTime);
|
||||
public static String toBigqueryTimestampString(Instant dateTime) {
|
||||
return BIGQUERY_TIMESTAMP_PRINTER.format(dateTime);
|
||||
}
|
||||
|
||||
/** Returns the DateTime for a given human-readable string-formatted BigQuery timestamp. */
|
||||
public static DateTime fromBigqueryTimestampString(String timestampString) {
|
||||
return BIGQUERY_TIMESTAMP_FORMAT.parseDateTime(timestampString);
|
||||
/** Returns the Instant for a given human-readable string-formatted BigQuery timestamp. */
|
||||
public static Instant fromBigqueryTimestampString(String timestampString) {
|
||||
return BIGQUERY_TIMESTAMP_PARSER.parse(timestampString, Instant::from);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,15 +135,16 @@ public class BigqueryUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link DateTime} into a numeric string that BigQuery understands as a timestamp:
|
||||
* the decimal number of seconds since the epoch, precise up to microseconds.
|
||||
* Converts a time into a numeric string that BigQuery understands as a timestamp: the decimal
|
||||
* number of seconds since the epoch, precise up to microseconds.
|
||||
*
|
||||
* <p>Note that since {@code DateTime} only stores milliseconds, the last 3 digits will be zero.
|
||||
* <p>Note that while {@code Instant} supports nanosecond precision, BigQuery only supports
|
||||
* microsecond precision, so the sub-microsecond precision is truncated.
|
||||
*
|
||||
* @see <a href="https://developers.google.com/bigquery/timestamp">Data Types</a>
|
||||
*/
|
||||
public static String toBigqueryTimestamp(DateTime dateTime) {
|
||||
return toBigqueryTimestamp(dateTime.getMillis(), TimeUnit.MILLISECONDS);
|
||||
public static String toBigqueryTimestamp(Instant dateTime) {
|
||||
return toBigqueryTimestamp(MICROS.between(Instant.EPOCH, dateTime), TimeUnit.MICROSECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,7 +116,7 @@ public class BsaDownloadAction implements Runnable {
|
||||
|
||||
Void runWithinLock() {
|
||||
// Cannot enroll new TLDs after download starts. This may change if b/309175410 is fixed.
|
||||
if (!Tlds.hasActiveBsaEnrollment(clock.nowUtc())) {
|
||||
if (!Tlds.hasActiveBsaEnrollment(clock.now())) {
|
||||
logger.atInfo().log("No TLDs enrolled with BSA. Quitting.");
|
||||
return null;
|
||||
}
|
||||
@@ -179,9 +179,7 @@ public class BsaDownloadAction implements Runnable {
|
||||
gcsClient.writeUnblockableDomains(
|
||||
schedule.jobName(),
|
||||
batches
|
||||
.map(
|
||||
batch ->
|
||||
applyLabelDiff(batch, lazyIdnChecker.get(), schedule, clock.nowUtc()))
|
||||
.map(batch -> applyLabelDiff(batch, lazyIdnChecker.get(), schedule, clock.now()))
|
||||
.flatMap(ImmutableList::stream));
|
||||
}
|
||||
schedule.updateJobStage(DownloadStage.REPORT_START_OF_ORDER_PROCESSING);
|
||||
|
||||
@@ -17,8 +17,8 @@ package google.registry.bsa;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.Callable;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Helper for guarding all BSA related work with a common lock. */
|
||||
public class BsaLock {
|
||||
|
||||
@@ -67,7 +67,7 @@ public class BsaRefreshAction implements Runnable {
|
||||
GcsClient gcsClient,
|
||||
BsaReportSender bsaReportSender,
|
||||
@Config("bsaTxnBatchSize") int transactionBatchSize,
|
||||
@Config("domainCreateTxnCommitTimeLag") org.joda.time.Duration domainCreateTxnCommitTimeLag,
|
||||
@Config("domainCreateTxnCommitTimeLag") Duration domainCreateTxnCommitTimeLag,
|
||||
BsaEmailSender emailSender,
|
||||
BsaLock bsaLock,
|
||||
Clock clock,
|
||||
@@ -76,7 +76,7 @@ public class BsaRefreshAction implements Runnable {
|
||||
this.gcsClient = gcsClient;
|
||||
this.bsaReportSender = bsaReportSender;
|
||||
this.transactionBatchSize = transactionBatchSize;
|
||||
this.domainCreateTxnCommitTimeLag = Duration.ofMillis(domainCreateTxnCommitTimeLag.getMillis());
|
||||
this.domainCreateTxnCommitTimeLag = domainCreateTxnCommitTimeLag;
|
||||
this.emailSender = emailSender;
|
||||
this.bsaLock = bsaLock;
|
||||
this.clock = clock;
|
||||
|
||||
@@ -88,14 +88,14 @@ public class BsaValidateAction implements Runnable {
|
||||
IdnChecker idnChecker,
|
||||
BsaEmailSender emailSender,
|
||||
@Config("bsaTxnBatchSize") int transactionBatchSize,
|
||||
@Config("bsaValidationMaxStaleness") org.joda.time.Duration maxStaleness,
|
||||
@Config("bsaValidationMaxStaleness") Duration maxStaleness,
|
||||
Clock clock,
|
||||
Response response) {
|
||||
this.gcsClient = gcsClient;
|
||||
this.idnChecker = idnChecker;
|
||||
this.emailSender = emailSender;
|
||||
this.transactionBatchSize = transactionBatchSize;
|
||||
this.maxStaleness = Duration.ofMillis(maxStaleness.getMillis());
|
||||
this.maxStaleness = maxStaleness;
|
||||
this.clock = clock;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import google.registry.tldconfig.idn.IdnLabelValidator;
|
||||
import google.registry.tldconfig.idn.IdnTableEnum;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* Checks labels' validity wrt Idns in TLDs enrolled with BSA.
|
||||
@@ -45,7 +45,7 @@ public class IdnChecker {
|
||||
|
||||
@Inject
|
||||
IdnChecker(Clock clock) {
|
||||
this.idnToTlds = getIdnToTldMap(clock.nowUtc());
|
||||
this.idnToTlds = getIdnToTldMap(clock.now());
|
||||
allTlds = idnToTlds.values().stream().flatMap(ImmutableSet::stream).collect(toImmutableSet());
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public class IdnChecker {
|
||||
return Sets.difference(allTlds, getSupportingTlds(idnTables)).immutableCopy();
|
||||
}
|
||||
|
||||
private static ImmutableMap<IdnTableEnum, ImmutableSet<Tld>> getIdnToTldMap(DateTime now) {
|
||||
private static ImmutableMap<IdnTableEnum, ImmutableSet<Tld>> getIdnToTldMap(Instant now) {
|
||||
var idnToTldMap = new ImmutableMultimap.Builder<IdnTableEnum, Tld>();
|
||||
Tlds.getTldEntitiesOfType(TldType.REAL).stream()
|
||||
.filter(tld -> isEnrolledWithBsa(tld, now))
|
||||
|
||||
@@ -20,7 +20,6 @@ import static google.registry.bsa.BsaStringUtils.DOMAIN_JOINER;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isReserved;
|
||||
import static google.registry.model.tld.Tlds.findTldForName;
|
||||
import static google.registry.model.tld.label.ReservedList.loadReservedLists;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
@@ -35,7 +34,6 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Utility for looking up reserved domain names.
|
||||
@@ -47,15 +45,6 @@ public final class ReservedDomainsUtils {
|
||||
|
||||
private ReservedDomainsUtils() {}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getAllReservedNames(Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static Stream<String> getAllReservedNames(DateTime now) {
|
||||
return getAllReservedNames(toInstant(now));
|
||||
}
|
||||
|
||||
public static Stream<String> getAllReservedNames(Instant now) {
|
||||
return Tlds.getTldEntitiesOfType(TldType.REAL).stream()
|
||||
.filter(tld -> Tld.isEnrolledWithBsa(tld, now))
|
||||
@@ -74,15 +63,6 @@ public final class ReservedDomainsUtils {
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getAllReservedDomainsInTld(Tld, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
static ImmutableSet<String> getAllReservedDomainsInTld(Tld tld, DateTime now) {
|
||||
return getAllReservedDomainsInTld(tld, toInstant(now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if {@code domain} is a reserved name that can be registered right now (e.g.,
|
||||
* during sunrise or with allocation token), therefore unblockable.
|
||||
@@ -95,13 +75,4 @@ public final class ReservedDomainsUtils {
|
||||
InternetDomainName.from(domain),
|
||||
Objects.equals(tld.getTldState(now), TldState.START_DATE_SUNRISE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #isReservedDomain(String, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static boolean isReservedDomain(String domain, DateTime now) {
|
||||
return isReservedDomain(domain, toInstant(now));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,12 @@ import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import org.joda.time.Duration;
|
||||
import org.joda.time.Instant;
|
||||
|
||||
/**
|
||||
* A credential for accessing the BSA API.
|
||||
@@ -104,7 +104,7 @@ public class BsaCredential {
|
||||
}
|
||||
|
||||
private void ensureAuthTokenValid() throws IOException, GeneralSecurityException {
|
||||
Instant now = Instant.ofEpochMilli(clock.nowUtc().getMillis());
|
||||
Instant now = clock.now();
|
||||
if (authToken != null && lastRefreshTime.plus(authTokenExpiry).isAfter(now)) {
|
||||
logger.atInfo().log("AuthToken still valid, reusing.");
|
||||
return;
|
||||
|
||||
@@ -20,7 +20,6 @@ import static google.registry.bsa.DownloadStage.DONE;
|
||||
import static google.registry.bsa.DownloadStage.NOP;
|
||||
import static google.registry.bsa.persistence.RefreshScheduler.fetchMostRecentRefresh;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.joda.time.Duration.standardSeconds;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -28,9 +27,9 @@ import google.registry.bsa.persistence.DownloadSchedule.CompletedJob;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Assigns work for each cron invocation of the BSA Download job.
|
||||
@@ -57,7 +56,7 @@ import org.joda.time.Duration;
|
||||
public final class DownloadScheduler {
|
||||
|
||||
/** Allows a new download to proceed if the cron job fires a little early due to NTP drift. */
|
||||
private static final Duration CRON_JITTER = standardSeconds(5);
|
||||
private static final Duration CRON_JITTER = Duration.ofSeconds(5);
|
||||
|
||||
private final Duration downloadInterval;
|
||||
private final Duration maxNopInterval;
|
||||
@@ -117,8 +116,8 @@ public final class DownloadScheduler {
|
||||
private boolean isTimeAgain(BsaDownload mostRecent, Duration interval) {
|
||||
return mostRecent
|
||||
.getCreationTime()
|
||||
.plusMillis(interval.getMillis())
|
||||
.minusMillis(CRON_JITTER.getMillis())
|
||||
.plusMillis(interval.toMillis())
|
||||
.minusMillis(CRON_JITTER.toMillis())
|
||||
.isBefore(clock.now());
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ import google.registry.bsa.api.UnblockableDomain.Reason;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.tld.Tld;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Applies the BSA label diffs from the latest BSA download. */
|
||||
public final class LabelDiffUpdates {
|
||||
@@ -60,7 +60,7 @@ public final class LabelDiffUpdates {
|
||||
ImmutableList<BlockLabel> labels,
|
||||
IdnChecker idnChecker,
|
||||
DownloadSchedule schedule,
|
||||
DateTime now) {
|
||||
Instant now) {
|
||||
ImmutableList.Builder<UnblockableDomain> nonBlockedDomains = new ImmutableList.Builder<>();
|
||||
ImmutableMap<LabelType, ImmutableList<BlockLabel>> labelsByType =
|
||||
ImmutableMap.copyOf(
|
||||
@@ -134,7 +134,7 @@ public final class LabelDiffUpdates {
|
||||
}
|
||||
|
||||
static ImmutableList<UnblockableDomain> tallyUnblockableDomainsForNewLabels(
|
||||
ImmutableList<BlockLabel> labels, IdnChecker idnChecker, DateTime now) {
|
||||
ImmutableList<BlockLabel> labels, IdnChecker idnChecker, Instant now) {
|
||||
ImmutableList.Builder<UnblockableDomain> nonBlockedDomains = new ImmutableList.Builder<>();
|
||||
|
||||
for (BlockLabel label : labels) {
|
||||
|
||||
+16
-12
@@ -85,25 +85,29 @@ public final class CacheModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public static DomainCache provideDomainCache(Optional<UnifiedJedis> jedis, Clock clock) {
|
||||
if (jedis.isEmpty()) {
|
||||
return domainName ->
|
||||
ForeignKeyUtils.loadResourceByCache(Domain.class, domainName, clock.now());
|
||||
}
|
||||
SimplifiedJedisClient<Domain> jedisClient =
|
||||
SimplifiedJedisClient.create(Domain.class, jedis.get());
|
||||
return new MultilayerDomainCache(jedisClient, clock);
|
||||
public static Optional<SimplifiedJedisClient> provideJedisClient(Optional<UnifiedJedis> jedis) {
|
||||
return jedis.map(SimplifiedJedisClient::new);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public static HostCache provideHostCache(Optional<UnifiedJedis> jedis) {
|
||||
if (jedis.isEmpty()) {
|
||||
public static DomainCache provideDomainCache(
|
||||
Optional<SimplifiedJedisClient> domainJedisClient, Clock clock) {
|
||||
if (domainJedisClient.isEmpty()) {
|
||||
return domainName ->
|
||||
ForeignKeyUtils.loadResourceByCache(Domain.class, domainName, clock.now());
|
||||
}
|
||||
return new MultilayerDomainCache(domainJedisClient.get(), clock);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public static HostCache provideHostCache(Optional<SimplifiedJedisClient> hostJedisClient) {
|
||||
if (hostJedisClient.isEmpty()) {
|
||||
return repoId ->
|
||||
Optional.ofNullable(EppResource.loadByCache(VKey.create(Host.class, repoId)));
|
||||
}
|
||||
SimplifiedJedisClient<Host> jedisClient = SimplifiedJedisClient.create(Host.class, jedis.get());
|
||||
return new MultilayerHostCache(jedisClient);
|
||||
return new MultilayerHostCache(hostJedisClient.get());
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -32,14 +32,14 @@ public class MultilayerDomainCache extends MultilayerEppResourceCache<Domain>
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
public MultilayerDomainCache(SimplifiedJedisClient<Domain> jedisClient, Clock clock) {
|
||||
public MultilayerDomainCache(SimplifiedJedisClient jedisClient, Clock clock) {
|
||||
super(jedisClient);
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Domain> loadByDomainName(String domainName) {
|
||||
return loadFromCaches(domainName);
|
||||
return loadFromCaches(Domain.class, domainName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,11 +56,6 @@ public class MultilayerDomainCache extends MultilayerEppResourceCache<Domain>
|
||||
.map(domain -> domain.cloneProjectedAtTime(now));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJedisPrefix() {
|
||||
return "d_";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldPersistToRemoteCache(Domain domain) {
|
||||
return Tld.get(domain.getTld()).getTldType().equals(Tld.TldType.REAL);
|
||||
|
||||
@@ -35,21 +35,19 @@ public abstract class MultilayerEppResourceCache<V extends EppResource> {
|
||||
.maximumSize(RegistryConfig.getEppResourceMaxCachedEntries())
|
||||
.build();
|
||||
|
||||
private final SimplifiedJedisClient<V> jedisClient;
|
||||
private final SimplifiedJedisClient jedisClient;
|
||||
|
||||
protected MultilayerEppResourceCache(SimplifiedJedisClient<V> jedisClient) {
|
||||
protected MultilayerEppResourceCache(SimplifiedJedisClient jedisClient) {
|
||||
this.jedisClient = jedisClient;
|
||||
}
|
||||
|
||||
protected abstract Optional<V> loadFromDatabase(String key);
|
||||
|
||||
protected abstract String getJedisPrefix();
|
||||
|
||||
protected boolean shouldPersistToRemoteCache(V value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Optional<V> loadFromCaches(String key) {
|
||||
protected Optional<V> loadFromCaches(Class<V> clazz, String key) {
|
||||
// hopefully the resource is in the local cache
|
||||
Optional<V> possibleValue = Optional.ofNullable(localCache.getIfPresent(key));
|
||||
if (possibleValue.isPresent()) {
|
||||
@@ -57,8 +55,7 @@ public abstract class MultilayerEppResourceCache<V extends EppResource> {
|
||||
}
|
||||
|
||||
// if not, try the remote cache
|
||||
String jedisKey = getJedisPrefix() + key;
|
||||
possibleValue = jedisClient.get(jedisKey);
|
||||
possibleValue = jedisClient.get(clazz, key);
|
||||
if (possibleValue.isPresent()) {
|
||||
localCache.put(key, possibleValue.get());
|
||||
return possibleValue;
|
||||
@@ -70,7 +67,7 @@ public abstract class MultilayerEppResourceCache<V extends EppResource> {
|
||||
v -> {
|
||||
// Optional has no direct "peek" functionality to fill the caches
|
||||
if (shouldPersistToRemoteCache(v)) {
|
||||
jedisClient.set(new SimplifiedJedisClient.JedisResource<>(jedisKey, v));
|
||||
jedisClient.set(new SimplifiedJedisClient.JedisResource<>(key, v));
|
||||
}
|
||||
localCache.put(key, v);
|
||||
return v;
|
||||
|
||||
@@ -27,13 +27,13 @@ import java.util.Optional;
|
||||
*/
|
||||
public class MultilayerHostCache extends MultilayerEppResourceCache<Host> implements HostCache {
|
||||
|
||||
public MultilayerHostCache(SimplifiedJedisClient<Host> jedisClient) {
|
||||
public MultilayerHostCache(SimplifiedJedisClient jedisClient) {
|
||||
super(jedisClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Host> loadByRepoId(String repoId) {
|
||||
return loadFromCaches(repoId);
|
||||
return loadFromCaches(Host.class, repoId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -41,9 +41,4 @@ public class MultilayerHostCache extends MultilayerEppResourceCache<Host> implem
|
||||
return replicaTm()
|
||||
.transact(() -> replicaTm().loadByKeyIfPresent(VKey.create(Host.class, repoId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJedisPrefix() {
|
||||
return "h_";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,17 @@
|
||||
|
||||
package google.registry.cache;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import io.protostuff.LinkedBuffer;
|
||||
import io.protostuff.ProtostuffIOUtil;
|
||||
import io.protostuff.Schema;
|
||||
@@ -38,54 +43,61 @@ import redis.clients.jedis.params.SetParams;
|
||||
* <p>{@link UnifiedJedis} pairs key-value types, so we need the key to be serialized to a byte
|
||||
* array as well.
|
||||
*/
|
||||
public class SimplifiedJedisClient<V extends EppResource> {
|
||||
public class SimplifiedJedisClient {
|
||||
|
||||
public record JedisResource<V extends EppResource>(String key, V value) {}
|
||||
|
||||
private static final ImmutableMap<Class<? extends EppResource>, String> TYPE_PREFIXES =
|
||||
ImmutableMap.of(
|
||||
Domain.class, "d_",
|
||||
Host.class, "h_");
|
||||
|
||||
private static final ImmutableMap<Class<? extends EppResource>, Schema<? extends EppResource>>
|
||||
VALUE_SCHEMAS =
|
||||
ImmutableMap.of(
|
||||
Domain.class, RuntimeSchema.getSchema(Domain.class),
|
||||
Host.class, RuntimeSchema.getSchema(Host.class));
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final int BATCH_SIZE = 500;
|
||||
|
||||
private final Schema<V> valueSchema;
|
||||
private final UnifiedJedis jedis;
|
||||
|
||||
public static <V extends EppResource> SimplifiedJedisClient<V> create(
|
||||
Class<V> valueClass, UnifiedJedis jedis) {
|
||||
Schema<V> valueSchema = RuntimeSchema.getSchema(valueClass);
|
||||
return new SimplifiedJedisClient<>(valueSchema, jedis);
|
||||
}
|
||||
|
||||
private SimplifiedJedisClient(Schema<V> valueSchema, UnifiedJedis jedis) {
|
||||
this.valueSchema = valueSchema;
|
||||
SimplifiedJedisClient(UnifiedJedis jedis) {
|
||||
this.jedis = jedis;
|
||||
}
|
||||
|
||||
/** Gets the value from the remote cache. Returns null if it does not exist. */
|
||||
public Optional<V> get(String key) {
|
||||
public <V extends EppResource> Optional<V> get(Class<V> clazz, String key) {
|
||||
checkNotNull(key, "Key cannot be null");
|
||||
byte[] data = jedis.get(key.getBytes(StandardCharsets.UTF_8));
|
||||
return Optional.ofNullable(data).map(this::deserialize);
|
||||
byte[] data = jedis.get(convertKey(clazz, key));
|
||||
return Optional.ofNullable(data).map(d -> deserialize(clazz, d));
|
||||
}
|
||||
|
||||
/** Sets the value in the remote cache. */
|
||||
public void set(JedisResource<V> resource) {
|
||||
public <V extends EppResource> void set(JedisResource<V> resource) {
|
||||
checkNotNull(resource.key, "Key cannot be null");
|
||||
checkNotNull(resource.value, "Value cannot be null");
|
||||
jedis.set(
|
||||
resource.key.getBytes(StandardCharsets.UTF_8),
|
||||
convertKey(resource.value.getClass(), resource.key),
|
||||
serialize(resource.value),
|
||||
new SetParams().pxAt(resource.value.getDeletionTime().toEpochMilli()));
|
||||
}
|
||||
|
||||
/** Sets multiple values in the remote cache using a Jedis {@link AbstractPipeline}. */
|
||||
public void setAll(ImmutableCollection<JedisResource<V>> resources) {
|
||||
public <V extends EppResource> void setAll(ImmutableCollection<JedisResource<V>> resources) {
|
||||
logger.atInfo().log("Processing %d resources", resources.size());
|
||||
for (Iterable<JedisResource<V>> batch : Iterables.partition(resources, BATCH_SIZE)) {
|
||||
AbstractPipeline pipeline = jedis.pipelined();
|
||||
batch.forEach(
|
||||
resource ->
|
||||
pipeline.set(
|
||||
resource.key.getBytes(StandardCharsets.UTF_8),
|
||||
serialize(resource.value),
|
||||
new SetParams().pxAt(resource.value.getDeletionTime().toEpochMilli())));
|
||||
pipeline.sync();
|
||||
try (AbstractPipeline pipeline = jedis.pipelined()) {
|
||||
batch.forEach(
|
||||
resource ->
|
||||
pipeline.set(
|
||||
convertKey(resource.value.getClass(), resource.key),
|
||||
serialize(resource.value),
|
||||
new SetParams().pxAt(resource.value.getDeletionTime().toEpochMilli())));
|
||||
pipeline.sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,18 +113,18 @@ public class SimplifiedJedisClient<V extends EppResource> {
|
||||
* <p>This could also be accomplished by using {@link #setAll(ImmutableCollection)} with
|
||||
* expiration times that are in the past, but this is clearer.
|
||||
*/
|
||||
public void deleteAll(ImmutableCollection<String> keys) {
|
||||
public void deleteAll(Class<?> valueType, ImmutableCollection<String> keys) {
|
||||
// we use a reasonably small batch size to avoid overwhelming the network
|
||||
for (Iterable<String> batch : Iterables.partition(keys, BATCH_SIZE)) {
|
||||
byte[][] keysToUnlink =
|
||||
Streams.stream(batch)
|
||||
.map(key -> key.getBytes(StandardCharsets.UTF_8))
|
||||
.toArray(byte[][]::new);
|
||||
Streams.stream(batch).map(key -> convertKey(valueType, key)).toArray(byte[][]::new);
|
||||
jedis.unlink(keysToUnlink);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] serialize(V value) {
|
||||
private <V extends EppResource> byte[] serialize(V value) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Schema<V> valueSchema = (Schema<V>) getValueSchema(value.getClass());
|
||||
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
|
||||
try {
|
||||
return ProtostuffIOUtil.toByteArray(value, valueSchema, buffer);
|
||||
@@ -121,10 +133,22 @@ public class SimplifiedJedisClient<V extends EppResource> {
|
||||
}
|
||||
}
|
||||
|
||||
private V deserialize(byte[] data) {
|
||||
private <V extends EppResource> V deserialize(Class<V> clazz, byte[] data) {
|
||||
// We use protobufs because other deserializers don't play nicely with immutable collections
|
||||
Schema<V> valueSchema = getValueSchema(clazz);
|
||||
V value = valueSchema.newMessage();
|
||||
ProtostuffIOUtil.mergeFrom(data, value, valueSchema);
|
||||
return value;
|
||||
}
|
||||
|
||||
private byte[] convertKey(Class<?> clazz, String key) {
|
||||
checkArgument(TYPE_PREFIXES.containsKey(clazz), "Unknown class type %s", clazz);
|
||||
return (TYPE_PREFIXES.get(clazz) + key).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <V extends EppResource> Schema<V> getValueSchema(Class<V> clazz) {
|
||||
checkArgument(VALUE_SCHEMAS.containsKey(clazz), "Unknown class type %s", clazz);
|
||||
return (Schema<V>) VALUE_SCHEMAS.get(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
@@ -154,7 +155,7 @@ public class DelegatedCredentials extends GoogleCredentials {
|
||||
@Override
|
||||
public AccessToken refreshAccessToken() throws IOException {
|
||||
JsonFactory jsonFactory = JSON_FACTORY;
|
||||
long currentTime = clock.nowUtc().getMillis();
|
||||
long currentTime = clock.now().toEpochMilli();
|
||||
String assertion = createAssertion(jsonFactory, currentTime);
|
||||
|
||||
GenericData tokenRequest = new GenericData();
|
||||
@@ -195,7 +196,7 @@ public class DelegatedCredentials extends GoogleCredentials {
|
||||
GenericData responseData = response.parseAs(GenericData.class);
|
||||
String accessToken = validateString(responseData, "access_token", PARSE_ERROR_PREFIX);
|
||||
int expiresInSeconds = validateInt32(responseData, "expires_in", PARSE_ERROR_PREFIX);
|
||||
return new AccessToken(accessToken, clock.nowUtc().plusSeconds(expiresInSeconds).toDate());
|
||||
return new AccessToken(accessToken, Date.from(clock.now().plusSeconds(expiresInSeconds)));
|
||||
}
|
||||
|
||||
String createAssertion(JsonFactory jsonFactory, long currentTime) throws IOException {
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.base.Suppliers.memoize;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
|
||||
import static google.registry.config.ConfigUtils.makeUrl;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import static java.util.Comparator.naturalOrder;
|
||||
@@ -50,14 +50,14 @@ import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeConstants;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Central clearing-house for all configuration.
|
||||
@@ -282,7 +282,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("brdaInterval")
|
||||
public static Duration provideBrdaInterval() {
|
||||
return Duration.standardDays(7);
|
||||
return Duration.ofDays(7);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,7 +316,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("publishDnsUpdatesLockDuration")
|
||||
public static Duration providePublishDnsUpdatesLockDuration() {
|
||||
return Duration.standardMinutes(3);
|
||||
return Duration.ofMinutes(3);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -344,7 +344,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("readDnsRefreshRequestsActionRuntime")
|
||||
public static Duration provideReadDnsRefreshRequestsRuntime() {
|
||||
return Duration.standardSeconds(45);
|
||||
return Duration.ofSeconds(45);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,7 +355,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("dnsDefaultATtl")
|
||||
public static Duration provideDnsDefaultATtl() {
|
||||
return Duration.standardHours(1);
|
||||
return Duration.ofHours(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -366,7 +366,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("dnsDefaultNsTtl")
|
||||
public static Duration provideDnsDefaultNsTtl() {
|
||||
return Duration.standardHours(3);
|
||||
return Duration.ofHours(3);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,7 +377,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("dnsDefaultDsTtl")
|
||||
public static Duration provideDnsDefaultDsTtl() {
|
||||
return Duration.standardHours(1);
|
||||
return Duration.ofHours(1);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -758,7 +758,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("rdeInterval")
|
||||
public static Duration provideRdeInterval() {
|
||||
return Duration.standardDays(1);
|
||||
return Duration.ofDays(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -769,7 +769,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("rdeReportLockTimeout")
|
||||
public static Duration provideRdeReportLockTimeout() {
|
||||
return Duration.standardMinutes(1);
|
||||
return Duration.ofMinutes(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -793,7 +793,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("rdeUploadLockTimeout")
|
||||
public static Duration provideRdeUploadLockTimeout() {
|
||||
return Duration.standardMinutes(30);
|
||||
return Duration.ofMinutes(30);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -806,7 +806,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("rdeUploadSftpCooldown")
|
||||
public static Duration provideRdeUploadSftpCooldown() {
|
||||
return Duration.standardHours(2);
|
||||
return Duration.ofHours(2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -842,7 +842,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("sheetLockTimeout")
|
||||
public static Duration provideSheetLockTimeout() {
|
||||
return Duration.standardHours(1);
|
||||
return Duration.ofHours(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -866,7 +866,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("emailThrottleDuration")
|
||||
public static Duration provideEmailThrottleSeconds(RegistryConfigSettings config) {
|
||||
return Duration.standardSeconds(config.misc.emailThrottleSeconds);
|
||||
return Duration.ofSeconds(config.misc.emailThrottleSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -947,7 +947,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("sshTimeout")
|
||||
public static Duration provideSshTimeout() {
|
||||
return Duration.standardSeconds(30);
|
||||
return Duration.ofSeconds(30);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -958,7 +958,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("transactionCooldown")
|
||||
public static Duration provideTransactionCooldown() {
|
||||
return Duration.standardMinutes(5);
|
||||
return Duration.ofMinutes(5);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1034,7 +1034,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("metricsWriteInterval")
|
||||
public static Duration provideMetricsWriteInterval(RegistryConfigSettings config) {
|
||||
return Duration.standardSeconds(config.monitoring.writeIntervalSeconds);
|
||||
return Duration.ofSeconds(config.monitoring.writeIntervalSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1148,8 +1148,8 @@ public final class RegistryConfig {
|
||||
|
||||
@Provides
|
||||
@Config("tokenRefreshDelay")
|
||||
public static java.time.Duration provideTokenRefreshDelay(RegistryConfigSettings config) {
|
||||
return java.time.Duration.ofSeconds(config.credentialOAuth.tokenRefreshDelaySeconds);
|
||||
public static Duration provideTokenRefreshDelay(RegistryConfigSettings config) {
|
||||
return Duration.ofSeconds(config.credentialOAuth.tokenRefreshDelaySeconds);
|
||||
}
|
||||
|
||||
/** OAuth client ID used by the nomulus tool. */
|
||||
@@ -1186,28 +1186,28 @@ public final class RegistryConfig {
|
||||
|
||||
@Provides
|
||||
@Config("maxValidityDaysSchedule")
|
||||
public static ImmutableSortedMap<DateTime, Integer> provideValidityDaysMap(
|
||||
public static ImmutableSortedMap<Instant, Integer> provideValidityDaysMap(
|
||||
RegistryConfigSettings config) {
|
||||
return config.sslCertificateValidation.maxValidityDaysSchedule.entrySet().stream()
|
||||
.collect(
|
||||
toImmutableSortedMap(
|
||||
naturalOrder(),
|
||||
e ->
|
||||
"START_OF_TIME".equals(e.getKey())
|
||||
? START_OF_TIME
|
||||
: DateTime.parse(e.getKey()),
|
||||
"START_INSTANT".equals(e.getKey())
|
||||
? START_INSTANT
|
||||
: Instant.parse(e.getKey()),
|
||||
Entry::getValue));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("expirationWarningDays")
|
||||
public static int provideExpirationWarningDays(RegistryConfigSettings config) {
|
||||
public static long provideExpirationWarningDays(RegistryConfigSettings config) {
|
||||
return config.sslCertificateValidation.expirationWarningDays;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("expirationWarningIntervalDays")
|
||||
public static int provideExpirationWarningIntervalDays(RegistryConfigSettings config) {
|
||||
public static long provideExpirationWarningIntervalDays(RegistryConfigSettings config) {
|
||||
return config.sslCertificateValidation.expirationWarningIntervalDays;
|
||||
}
|
||||
|
||||
@@ -1328,14 +1328,14 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("bsaLockLeaseExpiry")
|
||||
public static Duration provideBsaLockLeaseExpiry(RegistryConfigSettings config) {
|
||||
return Duration.standardMinutes(config.bsa.bsaLockLeaseExpiryMinutes);
|
||||
return Duration.ofMinutes(config.bsa.bsaLockLeaseExpiryMinutes);
|
||||
}
|
||||
|
||||
/** Returns the desired interval between successive BSA downloads. */
|
||||
@Provides
|
||||
@Config("bsaDownloadInterval")
|
||||
public static Duration provideBsaDownloadInterval(RegistryConfigSettings config) {
|
||||
return Duration.standardMinutes(config.bsa.bsaDownloadIntervalMinutes);
|
||||
return Duration.ofMinutes(config.bsa.bsaDownloadIntervalMinutes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1345,7 +1345,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("bsaMaxNopInterval")
|
||||
public static Duration provideBsaMaxNopInterval(RegistryConfigSettings config) {
|
||||
return Duration.standardHours(config.bsa.bsaMaxNopIntervalHours);
|
||||
return Duration.ofHours(config.bsa.bsaMaxNopIntervalHours);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -1357,13 +1357,13 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("domainCreateTxnCommitTimeLag")
|
||||
public static Duration provideDomainCreateTxnCommitTimeLag(RegistryConfigSettings config) {
|
||||
return Duration.standardSeconds(config.bsa.domainCreateTxnCommitTimeLagSeconds);
|
||||
return Duration.ofSeconds(config.bsa.domainCreateTxnCommitTimeLagSeconds);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("bsaValidationMaxStaleness")
|
||||
public static Duration provideBsaValidationMaxStaleness(RegistryConfigSettings config) {
|
||||
return Duration.standardSeconds(config.bsa.bsaValidationMaxStalenessSeconds);
|
||||
return Duration.ofSeconds(config.bsa.bsaValidationMaxStalenessSeconds);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -1375,7 +1375,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("bsaAuthTokenExpiry")
|
||||
public static Duration provideBsaAuthTokenExpiry(RegistryConfigSettings config) {
|
||||
return Duration.standardSeconds(config.bsa.authTokenExpirySeconds);
|
||||
return Duration.ofSeconds(config.bsa.authTokenExpirySeconds);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -1489,7 +1489,7 @@ public final class RegistryConfig {
|
||||
* @see google.registry.tools.server.GenerateZoneFilesAction
|
||||
*/
|
||||
public static Duration getDatabaseRetention() {
|
||||
return Duration.standardDays(30);
|
||||
return Duration.ofDays(30);
|
||||
}
|
||||
|
||||
public static boolean areServersLocal() {
|
||||
@@ -1505,8 +1505,8 @@ public final class RegistryConfig {
|
||||
}
|
||||
|
||||
/** Returns the amount of time a singleton should be cached, before expiring. */
|
||||
public static java.time.Duration getSingletonCacheRefreshDuration() {
|
||||
return java.time.Duration.ofSeconds(CONFIG_SETTINGS.get().caching.singletonCacheRefreshSeconds);
|
||||
public static Duration getSingletonCacheRefreshDuration() {
|
||||
return Duration.ofSeconds(CONFIG_SETTINGS.get().caching.singletonCacheRefreshSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1515,13 +1515,13 @@ public final class RegistryConfig {
|
||||
* @see google.registry.model.tld.label.ReservedList
|
||||
* @see google.registry.model.tld.label.PremiumList
|
||||
*/
|
||||
public static java.time.Duration getDomainLabelListCacheDuration() {
|
||||
return java.time.Duration.ofSeconds(CONFIG_SETTINGS.get().caching.domainLabelCachingSeconds);
|
||||
public static Duration getDomainLabelListCacheDuration() {
|
||||
return Duration.ofSeconds(CONFIG_SETTINGS.get().caching.domainLabelCachingSeconds);
|
||||
}
|
||||
|
||||
/** Returns the amount of time a singleton should be cached in persist mode, before expiring. */
|
||||
public static java.time.Duration getSingletonCachePersistDuration() {
|
||||
return java.time.Duration.ofSeconds(CONFIG_SETTINGS.get().caching.singletonCachePersistSeconds);
|
||||
public static Duration getSingletonCachePersistDuration() {
|
||||
return Duration.ofSeconds(CONFIG_SETTINGS.get().caching.singletonCachePersistSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1543,8 +1543,8 @@ public final class RegistryConfig {
|
||||
/**
|
||||
* Returns the amount of time an EPP resource or key should be cached in memory before expiring.
|
||||
*/
|
||||
public static java.time.Duration getEppResourceCachingDuration() {
|
||||
return java.time.Duration.ofSeconds(CONFIG_SETTINGS.get().caching.eppResourceCachingSeconds);
|
||||
public static Duration getEppResourceCachingDuration() {
|
||||
return Duration.ofSeconds(CONFIG_SETTINGS.get().caching.eppResourceCachingSeconds);
|
||||
}
|
||||
|
||||
/** Returns the maximum number of EPP resources and keys to keep in in-memory cache. */
|
||||
@@ -1553,8 +1553,8 @@ public final class RegistryConfig {
|
||||
}
|
||||
|
||||
/** Returns the amount of time that a particular claims list should be cached. */
|
||||
public static java.time.Duration getClaimsListCacheDuration() {
|
||||
return java.time.Duration.ofSeconds(CONFIG_SETTINGS.get().caching.claimsListCachingSeconds);
|
||||
public static Duration getClaimsListCacheDuration() {
|
||||
return Duration.ofSeconds(CONFIG_SETTINGS.get().caching.claimsListCachingSeconds);
|
||||
}
|
||||
|
||||
/** Returns the email address that outgoing emails from the app are sent from. */
|
||||
|
||||
@@ -214,8 +214,8 @@ public class RegistryConfigSettings {
|
||||
/** Configuration for the certificate checker. */
|
||||
public static class SslCertificateValidation {
|
||||
public Map<String, Integer> maxValidityDaysSchedule;
|
||||
public int expirationWarningDays;
|
||||
public int expirationWarningIntervalDays;
|
||||
public long expirationWarningDays;
|
||||
public long expirationWarningIntervalDays;
|
||||
public int minimumRsaKeyLength;
|
||||
public Set<String> allowedEcdsaCurves;
|
||||
public String expirationWarningEmailBodyText;
|
||||
|
||||
@@ -476,7 +476,7 @@ sslCertificateValidation:
|
||||
# The entry key is the date closest before the date the certificate was issued
|
||||
# and the entry value is the applicable maximum validity days for that certificate.
|
||||
maxValidityDaysSchedule:
|
||||
"START_OF_TIME": 825
|
||||
"START_INSTANT": 825
|
||||
"2020-09-01T00:00:00Z": 398
|
||||
# The number of days before a certificate expires that indicates the
|
||||
# certificate is nearing expiration and warnings should be sent.
|
||||
|
||||
@@ -26,7 +26,7 @@ import com.google.monitoring.metrics.LabelDescriptor;
|
||||
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.inject.Inject;
|
||||
import org.joda.time.Duration;
|
||||
import java.time.Duration;
|
||||
|
||||
/** DNS instrumentation. */
|
||||
// TODO(b/67947699):Once load testing is done, revisit these to rename them and delete the ones that
|
||||
@@ -239,15 +239,15 @@ public class DnsMetrics {
|
||||
int batchSize = numberOfDomains + numberOfHosts;
|
||||
|
||||
processingTimePerCommitDist.record(
|
||||
processingDuration.getMillis(), tld, status.name(), dnsWriter);
|
||||
processingDuration.toMillis(), tld, status.name(), dnsWriter);
|
||||
processingTimePerItemDist.record(
|
||||
processingDuration.getMillis(), batchSize, tld, status.name(), dnsWriter);
|
||||
processingDuration.toMillis(), batchSize, tld, status.name(), dnsWriter);
|
||||
|
||||
if (batchSize > 0) {
|
||||
normalizedProcessingTimePerCommitDist.record(
|
||||
processingDuration.getMillis() / (double) batchSize, tld, status.name(), dnsWriter);
|
||||
processingDuration.toMillis() / (double) batchSize, tld, status.name(), dnsWriter);
|
||||
normalizedProcessingTimePerItemDist.record(
|
||||
processingDuration.getMillis() / (double) batchSize,
|
||||
processingDuration.toMillis() / (double) batchSize,
|
||||
batchSize,
|
||||
tld,
|
||||
status.name(),
|
||||
@@ -267,7 +267,7 @@ public class DnsMetrics {
|
||||
Duration timeSinceUpdateRequest,
|
||||
Duration timeSinceActionEnqueued) {
|
||||
updateRequestLatency.record(
|
||||
timeSinceUpdateRequest.getMillis(), numberOfItems, tld, status.name(), dnsWriter);
|
||||
publishQueueDelay.record(timeSinceActionEnqueued.getMillis(), tld, status.name(), dnsWriter);
|
||||
timeSinceUpdateRequest.toMillis(), numberOfItems, tld, status.name(), dnsWriter);
|
||||
publishQueueDelay.record(timeSinceActionEnqueued.toMillis(), tld, status.name(), dnsWriter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ import google.registry.dns.writer.DnsWriterZone;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.RequestParameters;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Dagger module for the dns package. */
|
||||
@Module
|
||||
@@ -68,15 +68,15 @@ public abstract class DnsModule {
|
||||
|
||||
@Provides
|
||||
@Parameter(PARAM_PUBLISH_TASK_ENQUEUED)
|
||||
static DateTime provideCreateTime(HttpServletRequest req) {
|
||||
return DateTime.parse(extractRequiredParameter(req, PARAM_PUBLISH_TASK_ENQUEUED));
|
||||
static Instant provideCreateTime(HttpServletRequest req) {
|
||||
return Instant.parse(extractRequiredParameter(req, PARAM_PUBLISH_TASK_ENQUEUED));
|
||||
}
|
||||
|
||||
// TODO: Retire the old header after DNS pull queue migration.
|
||||
@Provides
|
||||
@Parameter(PARAM_REFRESH_REQUEST_TIME)
|
||||
static DateTime provideItemsCreateTime(HttpServletRequest req) {
|
||||
return DateTime.parse(
|
||||
static Instant provideItemsCreateTime(HttpServletRequest req) {
|
||||
return Instant.parse(
|
||||
extractOptionalParameter(req, "itemsCreated")
|
||||
.orElse(extractRequiredParameter(req, PARAM_REFRESH_REQUEST_TIME)));
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@ import com.google.common.net.InternetDomainName;
|
||||
import google.registry.model.common.DnsRefreshRequest;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Utility class to handle DNS refresh requests. */
|
||||
public final class DnsUtils {
|
||||
@@ -42,13 +42,13 @@ public final class DnsUtils {
|
||||
// Throws an IllegalArgumentException if the name is not under a managed TLD -- we only update
|
||||
// DNS for names that are under our management.
|
||||
String tld = Tlds.findTldForNameOrThrow(InternetDomainName.from(name)).toString();
|
||||
tm().insert(new DnsRefreshRequest(type, name, tld, tm().getTransactionTime().plus(delay)));
|
||||
tm().insert(new DnsRefreshRequest(type, name, tld, tm().getTxTime().plus(delay)));
|
||||
}
|
||||
|
||||
private static void requestDnsRefresh(
|
||||
ImmutableCollection<String> names, TargetType type, Duration delay) {
|
||||
tm().assertInTransaction();
|
||||
DateTime requestTime = tm().getTransactionTime().plus(delay);
|
||||
Instant requestTime = tm().getTxTime().plus(delay);
|
||||
tm().insertAll(
|
||||
names.stream()
|
||||
.map(
|
||||
@@ -103,7 +103,7 @@ public final class DnsUtils {
|
||||
return tm().transact(
|
||||
TRANSACTION_REPEATABLE_READ,
|
||||
() -> {
|
||||
DateTime transactionTime = tm().getTransactionTime();
|
||||
Instant transactionTime = tm().getTxTime();
|
||||
ImmutableList<DnsRefreshRequest> requests =
|
||||
tm().query(
|
||||
"FROM DnsRefreshRequest WHERE tld = :tld "
|
||||
@@ -153,7 +153,7 @@ public final class DnsUtils {
|
||||
dnsAPlusAaaaTtl = tld.getDnsAPlusAaaaTtl().get();
|
||||
}
|
||||
}
|
||||
return dnsAPlusAaaaTtl.getStandardSeconds();
|
||||
return dnsAPlusAaaaTtl.toSeconds();
|
||||
}
|
||||
|
||||
/** The possible values of the {@code DNS_TARGET_TYPE_PARAM} parameter. */
|
||||
|
||||
@@ -63,11 +63,11 @@ import google.registry.util.DomainNameUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Task that sends domain and host updates to the DNS server. */
|
||||
@Action(
|
||||
@@ -100,8 +100,8 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
*/
|
||||
private final String dnsWriter;
|
||||
|
||||
private final DateTime enqueuedTime;
|
||||
private final DateTime itemsCreateTime;
|
||||
private final Instant enqueuedTime;
|
||||
private final Instant itemsCreateTime;
|
||||
private final int lockIndex;
|
||||
private final int numPublishLocks;
|
||||
private final Set<String> domains;
|
||||
@@ -121,8 +121,8 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
@Inject
|
||||
public PublishDnsUpdatesAction(
|
||||
@Parameter(PARAM_DNS_WRITER) String dnsWriter,
|
||||
@Parameter(PARAM_PUBLISH_TASK_ENQUEUED) DateTime enqueuedTime,
|
||||
@Parameter(PARAM_REFRESH_REQUEST_TIME) DateTime itemsCreateTime,
|
||||
@Parameter(PARAM_PUBLISH_TASK_ENQUEUED) Instant enqueuedTime,
|
||||
@Parameter(PARAM_REFRESH_REQUEST_TIME) Instant itemsCreateTime,
|
||||
@Parameter(PARAM_LOCK_INDEX) int lockIndex,
|
||||
@Parameter(PARAM_NUM_PUBLISH_LOCKS) int numPublishLocks,
|
||||
@Parameter(PARAM_DOMAINS) Set<String> domains,
|
||||
@@ -167,15 +167,15 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
}
|
||||
|
||||
private void recordActionResult(ActionStatus status) {
|
||||
DateTime now = clock.nowUtc();
|
||||
Instant now = clock.now();
|
||||
|
||||
dnsMetrics.recordActionResult(
|
||||
tld,
|
||||
dnsWriter,
|
||||
status,
|
||||
nullToEmpty(domains).size() + nullToEmpty(hosts).size(),
|
||||
new Duration(itemsCreateTime, now),
|
||||
new Duration(enqueuedTime, now));
|
||||
Duration.between(itemsCreateTime, now),
|
||||
Duration.between(enqueuedTime, now));
|
||||
logger.atInfo().log(
|
||||
"publishDnsWriter latency statistics: TLD: %s, dnsWriter: %s, actionStatus: %s, "
|
||||
+ "numItems: %d, timeSinceCreation: %s, timeInQueue: %s.",
|
||||
@@ -183,8 +183,8 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
dnsWriter,
|
||||
status,
|
||||
nullToEmpty(domains).size() + nullToEmpty(hosts).size(),
|
||||
new Duration(itemsCreateTime, now),
|
||||
new Duration(enqueuedTime, now));
|
||||
Duration.between(itemsCreateTime, now),
|
||||
Duration.between(enqueuedTime, now));
|
||||
}
|
||||
|
||||
/** Runs the task. */
|
||||
@@ -237,7 +237,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
.ifPresent(
|
||||
dn -> {
|
||||
Optional<Domain> domain =
|
||||
ForeignKeyUtils.loadResource(Domain.class, dn, clock.nowUtc());
|
||||
ForeignKeyUtils.loadResource(Domain.class, dn, clock.now());
|
||||
if (domain.isPresent()) {
|
||||
notifyWithEmailAboutDnsUpdateFailure(
|
||||
domain.get().getCurrentSponsorRegistrarId(), dn, false);
|
||||
@@ -250,8 +250,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
.findFirst()
|
||||
.ifPresent(
|
||||
hn -> {
|
||||
Optional<Host> host =
|
||||
ForeignKeyUtils.loadResource(Host.class, hn, clock.nowUtc());
|
||||
Optional<Host> host = ForeignKeyUtils.loadResource(Host.class, hn, clock.now());
|
||||
if (host.isPresent()) {
|
||||
notifyWithEmailAboutDnsUpdateFailure(
|
||||
host.get().getPersistedCurrentSponsorRegistrarId(), hn, true);
|
||||
@@ -336,7 +335,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
.put(PARAM_DNS_WRITER, dnsWriter)
|
||||
.put(PARAM_LOCK_INDEX, Integer.toString(lockIndex))
|
||||
.put(PARAM_NUM_PUBLISH_LOCKS, Integer.toString(numPublishLocks))
|
||||
.put(PARAM_PUBLISH_TASK_ENQUEUED, clock.nowUtc().toString())
|
||||
.put(PARAM_PUBLISH_TASK_ENQUEUED, clock.now().toString())
|
||||
.put(PARAM_REFRESH_REQUEST_TIME, itemsCreateTime.toString())
|
||||
.put(PARAM_DOMAINS, Joiner.on(",").join(domains))
|
||||
.put(PARAM_HOSTS, Joiner.on(",").join(hosts))
|
||||
@@ -374,7 +373,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
|
||||
/** Steps through the domain and host refreshes contained in the parameters and processes them. */
|
||||
private void processBatch() {
|
||||
DateTime timeAtStart = clock.nowUtc();
|
||||
Instant timeAtStart = clock.now();
|
||||
|
||||
DnsWriter writer = dnsWriterProxy.getByClassNameForTld(dnsWriter, tld);
|
||||
|
||||
@@ -426,7 +425,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
actionStatus = ActionStatus.SUCCESS;
|
||||
} finally {
|
||||
recordActionResult(actionStatus);
|
||||
Duration duration = new Duration(timeAtStart, clock.nowUtc());
|
||||
Duration duration = Duration.between(timeAtStart, clock.now());
|
||||
dnsMetrics.recordCommit(
|
||||
tld, dnsWriter, commitStatus, duration, domainsPublished, hostsPublished);
|
||||
logger.atInfo().log(
|
||||
|
||||
@@ -28,7 +28,7 @@ import static google.registry.dns.DnsUtils.deleteRequests;
|
||||
import static google.registry.dns.DnsUtils.readAndUpdateRequestsWithLatestProcessTime;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.END_INSTANT;
|
||||
import static google.registry.util.DomainNameUtils.getSecondLevelDomain;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
@@ -49,10 +49,10 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Action for fanning out DNS refresh tasks by TLD, using data taken from {@link DnsRefreshRequest}
|
||||
@@ -104,11 +104,11 @@ public final class ReadDnsRefreshRequestsAction implements Runnable {
|
||||
logger.atInfo().log("The queue updated is paused for TLD: %s.", tld);
|
||||
return;
|
||||
}
|
||||
DateTime requestedEndTime = clock.nowUtc().plus(requestedMaximumDuration);
|
||||
Instant requestedEndTime = clock.now().plus(requestedMaximumDuration);
|
||||
// See getLockIndex(), requests are evenly distributed to [1, numDnsPublishLocks], so each
|
||||
// bucket would be roughly the size of tldUpdateBatchSize.
|
||||
int processBatchSize = tldUpdateBatchSize * Tld.get(tld).getNumDnsPublishLocks();
|
||||
while (requestedEndTime.isAfter(clock.nowUtc())) {
|
||||
while (requestedEndTime.isAfter(clock.now())) {
|
||||
ImmutableList<DnsRefreshRequest> requests =
|
||||
readAndUpdateRequestsWithLatestProcessTime(
|
||||
tld, requestedMaximumDuration, processBatchSize);
|
||||
@@ -165,7 +165,7 @@ public final class ReadDnsRefreshRequestsAction implements Runnable {
|
||||
void enqueueUpdates(int lockIndex, int numPublishLocks, Collection<DnsRefreshRequest> requests) {
|
||||
ImmutableList.Builder<String> domainsBuilder = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<String> hostsBuilder = new ImmutableList.Builder<>();
|
||||
DateTime earliestRequestTime = END_OF_TIME;
|
||||
Instant earliestRequestTime = END_INSTANT;
|
||||
for (DnsRefreshRequest request : requests) {
|
||||
if (request.getRequestTime().isBefore(earliestRequestTime)) {
|
||||
earliestRequestTime = request.getRequestTime();
|
||||
@@ -189,7 +189,7 @@ public final class ReadDnsRefreshRequestsAction implements Runnable {
|
||||
.put(PARAM_DNS_WRITER, dnsWriter)
|
||||
.put(PARAM_LOCK_INDEX, Integer.toString(lockIndex))
|
||||
.put(PARAM_NUM_PUBLISH_LOCKS, Integer.toString(numPublishLocks))
|
||||
.put(PARAM_PUBLISH_TASK_ENQUEUED, clock.nowUtc().toString())
|
||||
.put(PARAM_PUBLISH_TASK_ENQUEUED, clock.now().toString())
|
||||
.put(PARAM_REFRESH_REQUEST_TIME, earliestRequestTime.toString())
|
||||
.put(PARAM_DOMAINS, Joiner.on(',').join(domains))
|
||||
.put(PARAM_HOSTS, Joiner.on(',').join(hosts))
|
||||
|
||||
@@ -78,7 +78,7 @@ public final class RefreshDnsAction implements Runnable {
|
||||
|
||||
private <T extends EppResource & ForeignKeyedEppResource>
|
||||
T loadAndVerifyExistence(Class<T> clazz, String foreignKey) {
|
||||
return ForeignKeyUtils.loadResource(clazz, foreignKey, clock.nowUtc())
|
||||
return ForeignKeyUtils.loadResource(clazz, foreignKey, clock.now())
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new NotFoundException(
|
||||
|
||||
@@ -30,7 +30,7 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import java.time.Instant;
|
||||
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
@@ -56,7 +56,7 @@ public class RefreshDnsOnHostRenameAction implements Runnable {
|
||||
public void run() {
|
||||
tm().transact(
|
||||
() -> {
|
||||
DateTime now = tm().getTransactionTime();
|
||||
Instant now = tm().getTxTime();
|
||||
Host host = tm().loadByKeyIfPresent(hostKey).orElse(null);
|
||||
boolean hostValid = true;
|
||||
String failureMessage = null;
|
||||
|
||||
@@ -51,6 +51,7 @@ import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -59,7 +60,6 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* {@link DnsWriter} implementation that talks to Google Cloud DNS.
|
||||
@@ -123,8 +123,7 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
String absoluteDomainName = getAbsoluteHostName(domainName);
|
||||
|
||||
// Load the target domain. Note that it can be absent if this domain was just deleted.
|
||||
Optional<Domain> domain =
|
||||
ForeignKeyUtils.loadResource(Domain.class, domainName, clock.nowUtc());
|
||||
Optional<Domain> domain = ForeignKeyUtils.loadResource(Domain.class, domainName, clock.now());
|
||||
|
||||
// Return early if no DNS records should be published.
|
||||
// desiredRecordsBuilder is populated with an empty set to indicate that all existing records
|
||||
@@ -148,7 +147,11 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
domainRecords.add(
|
||||
new ResourceRecordSet()
|
||||
.setName(absoluteDomainName)
|
||||
.setTtl((int) tld.getDnsDsTtl().orElse(defaultDsTtl).getStandardSeconds())
|
||||
.setTtl(
|
||||
(int)
|
||||
tld.getDnsDsTtl()
|
||||
.orElse(defaultDsTtl)
|
||||
.toSeconds())
|
||||
.setType("DS")
|
||||
.setKind("dns#resourceRecordSet")
|
||||
.setRrdatas(ImmutableList.copyOf(dsRrData)));
|
||||
@@ -171,7 +174,11 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
domainRecords.add(
|
||||
new ResourceRecordSet()
|
||||
.setName(absoluteDomainName)
|
||||
.setTtl((int) tld.getDnsNsTtl().orElse(defaultNsTtl).getStandardSeconds())
|
||||
.setTtl(
|
||||
(int)
|
||||
tld.getDnsNsTtl()
|
||||
.orElse(defaultNsTtl)
|
||||
.toSeconds())
|
||||
.setType("NS")
|
||||
.setKind("dns#resourceRecordSet")
|
||||
.setRrdatas(ImmutableList.copyOf(nsRrData)));
|
||||
@@ -190,7 +197,7 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
// Load the target host. Note that it can be absent if this host was just deleted.
|
||||
// desiredRecords is populated with an empty set to indicate that all existing records
|
||||
// should be deleted.
|
||||
Optional<Host> host = ForeignKeyUtils.loadResource(Host.class, hostName, clock.nowUtc());
|
||||
Optional<Host> host = ForeignKeyUtils.loadResource(Host.class, hostName, clock.now());
|
||||
|
||||
// Return early if the host is deleted.
|
||||
if (host.isEmpty()) {
|
||||
|
||||
@@ -28,8 +28,8 @@ import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Duration;
|
||||
import javax.net.SocketFactory;
|
||||
import org.joda.time.Duration;
|
||||
import org.xbill.DNS.Message;
|
||||
import org.xbill.DNS.Opcode;
|
||||
|
||||
@@ -76,7 +76,7 @@ public class DnsMessageTransport {
|
||||
@Config("dnsUpdateTimeout") Duration updateTimeout) {
|
||||
this.factory = factory;
|
||||
this.updateHost = updateHost;
|
||||
this.updateTimeout = Ints.checkedCast(updateTimeout.getMillis());
|
||||
this.updateTimeout = Ints.checkedCast(updateTimeout.toMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.dns.writer.dnsupdate;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import org.joda.time.Duration;
|
||||
import java.time.Duration;
|
||||
|
||||
/** Dagger module that provides DNS configuration settings. */
|
||||
@Module
|
||||
@@ -39,6 +39,6 @@ public class DnsUpdateConfigModule {
|
||||
@Provides
|
||||
@Config("dnsUpdateTimeout")
|
||||
public static Duration provideDnsUpdateTimeout() {
|
||||
return Duration.standardSeconds(30);
|
||||
return Duration.ofSeconds(30);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.Duration;
|
||||
import org.xbill.DNS.AAAARecord;
|
||||
import org.xbill.DNS.ARecord;
|
||||
import org.xbill.DNS.DClass;
|
||||
@@ -130,7 +130,7 @@ public class DnsUpdateWriter extends BaseDnsWriter {
|
||||
*/
|
||||
private void publishDomain(String domainName, String requestingHostName) {
|
||||
Optional<Domain> domainOptional =
|
||||
ForeignKeyUtils.loadResource(Domain.class, domainName, clock.nowUtc());
|
||||
ForeignKeyUtils.loadResource(Domain.class, domainName, clock.now());
|
||||
update.delete(toAbsoluteName(domainName), Type.ANY);
|
||||
// If the domain is now deleted, then don't update DNS for it.
|
||||
if (domainOptional.isPresent()) {
|
||||
@@ -194,7 +194,9 @@ public class DnsUpdateWriter extends BaseDnsWriter {
|
||||
new DSRecord(
|
||||
toAbsoluteName(domain.getDomainName()),
|
||||
DClass.IN,
|
||||
tld.getDnsDsTtl().orElse(dnsDefaultDsTtl).getStandardSeconds(),
|
||||
tld.getDnsDsTtl()
|
||||
.orElse(dnsDefaultDsTtl)
|
||||
.toSeconds(),
|
||||
signerData.getKeyTag(),
|
||||
signerData.getAlgorithm(),
|
||||
signerData.getDigestType(),
|
||||
@@ -219,7 +221,7 @@ public class DnsUpdateWriter extends BaseDnsWriter {
|
||||
private void addInBailiwickNameServerSet(Domain domain, Update update) {
|
||||
for (String hostName :
|
||||
intersection(domain.loadNameserverHostNames(), domain.getSubordinateHosts())) {
|
||||
Optional<Host> host = ForeignKeyUtils.loadResource(Host.class, hostName, clock.nowUtc());
|
||||
Optional<Host> host = ForeignKeyUtils.loadResource(Host.class, hostName, clock.now());
|
||||
checkState(host.isPresent(), "Host %s cannot be loaded", hostName);
|
||||
update.add(makeAddressSet(host.get()));
|
||||
update.add(makeV6AddressSet(host.get()));
|
||||
@@ -234,7 +236,9 @@ public class DnsUpdateWriter extends BaseDnsWriter {
|
||||
new NSRecord(
|
||||
toAbsoluteName(domain.getDomainName()),
|
||||
DClass.IN,
|
||||
tld.getDnsNsTtl().orElse(dnsDefaultNsTtl).getStandardSeconds(),
|
||||
tld.getDnsNsTtl()
|
||||
.orElse(dnsDefaultNsTtl)
|
||||
.toSeconds(),
|
||||
toAbsoluteName(hostName));
|
||||
nameServerSet.addRR(record);
|
||||
}
|
||||
|
||||
@@ -47,8 +47,6 @@ import java.time.Instant;
|
||||
import java.util.List;
|
||||
import org.hibernate.query.NativeQuery;
|
||||
import org.hibernate.query.TupleTransformer;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
/**
|
||||
* An action that exports the list of active domains on all real TLDs to Google Drive and GCS.
|
||||
@@ -175,8 +173,7 @@ public class ExportDomainListsAction implements Runnable {
|
||||
@Override
|
||||
public String transformTuple(Object[] domainResult, String[] strings) {
|
||||
String domainName = (String) domainResult[0];
|
||||
Instant deletionInstant = (Instant) domainResult[1];
|
||||
DateTime deletionTime = new DateTime(deletionInstant.toEpochMilli(), DateTimeZone.UTC);
|
||||
Instant deletionTime = (Instant) domainResult[1];
|
||||
String[] domainStatuses = (String[]) domainResult[2];
|
||||
String gracePeriodType = (String) domainResult[3];
|
||||
boolean inPendingDelete =
|
||||
|
||||
@@ -26,6 +26,7 @@ import static google.registry.model.registrar.RegistrarPoc.Type.TECH;
|
||||
import static google.registry.model.registrar.RegistrarPoc.Type.WHOIS;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -37,7 +38,6 @@ import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
@@ -63,9 +63,9 @@ class SyncRegistrarsSheet {
|
||||
boolean wereRegistrarsModified() {
|
||||
Optional<Cursor> cursor =
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(Cursor.createGlobalVKey(SYNC_REGISTRAR_SHEET)));
|
||||
Instant lastUpdateTime = cursor.isEmpty() ? START_INSTANT : cursor.get().getCursorTimeInstant();
|
||||
Instant lastUpdateTime = cursor.isEmpty() ? START_INSTANT : cursor.get().getCursorTime();
|
||||
for (Registrar registrar : Registrar.loadAllCached()) {
|
||||
if (DateTimeUtils.isAtOrAfter(registrar.getLastUpdateTime(), lastUpdateTime)) {
|
||||
if (isAtOrAfter(registrar.getLastUpdateTime(), lastUpdateTime)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,10 +30,10 @@ import google.registry.request.auth.Auth;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Action for synchronizing the registrars spreadsheet.
|
||||
|
||||
@@ -60,7 +60,8 @@ public class CheckApiMetrics {
|
||||
}
|
||||
|
||||
public void recordProcessingTime(CheckApiMetric metric) {
|
||||
long elapsedTime = metric.endTimestamp().getMillis() - metric.startTimestamp().getMillis();
|
||||
long elapsedTime =
|
||||
metric.endTimestamp().toEpochMilli() - metric.startTimestamp().toEpochMilli();
|
||||
processingTime.record(
|
||||
elapsedTime,
|
||||
metric.tier().map(Tier::getDisplayLabel).orElse(""),
|
||||
|
||||
@@ -98,7 +98,7 @@ public class EppMetrics {
|
||||
String eppStatusCode =
|
||||
metric.getStatus().isPresent() ? String.valueOf(metric.getStatus().get().code) : "";
|
||||
long processingTime =
|
||||
metric.getEndTimestamp().getMillis() - metric.getStartTimestamp().getMillis();
|
||||
metric.getEndTimestamp().toEpochMilli() - metric.getStartTimestamp().toEpochMilli();
|
||||
String commandName = metric.getCommandName().orElse("");
|
||||
String tld = metric.getTld().orElse("");
|
||||
requestTime.record(processingTime, commandName, getTrafficType(tld).toString(), eppStatusCode);
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
package google.registry.flows.certs;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.toDateTime;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.plusDays;
|
||||
import static google.registry.util.DateTimeUtils.toLocalDate;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import jakarta.inject.Inject;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.StringWriter;
|
||||
@@ -33,6 +33,8 @@ import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
|
||||
import org.bouncycastle.jce.ECNamedCurveTable;
|
||||
@@ -41,51 +43,49 @@ import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||
import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
|
||||
import org.bouncycastle.util.io.pem.PemObjectGenerator;
|
||||
import org.bouncycastle.util.io.pem.PemWriter;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeComparator;
|
||||
import org.joda.time.Days;
|
||||
|
||||
/** A utility to check that a given certificate meets our requirements */
|
||||
public class CertificateChecker {
|
||||
|
||||
private final ImmutableSortedMap<DateTime, Integer> maxValidityLengthSchedule;
|
||||
private final int expirationWarningDays;
|
||||
private final ImmutableSortedMap<Instant, Integer> maxValidityLengthSchedule;
|
||||
private final long expirationWarningDays;
|
||||
private final int minimumRsaKeyLength;
|
||||
private final Clock clock;
|
||||
private final ImmutableSet<String> allowedEcdsaCurves;
|
||||
private final int expirationWarningIntervalDays;
|
||||
private final long expirationWarningIntervalDays;
|
||||
|
||||
/**
|
||||
* Constructs a CertificateChecker instance with the specified configuration parameters.
|
||||
*
|
||||
* <p>The max validity length schedule is a sorted map of {@link DateTime} to {@link Integer}
|
||||
* <p>The max validity length schedule is a sorted map of {@link Instant} to {@link Integer}
|
||||
* entries representing a maximum validity period for certificates issued on or after that date.
|
||||
* The first entry must have a key of {@link DateTimeUtils#START_OF_TIME}, such that every
|
||||
* possible date has an applicable max validity period. Since security requirements tighten over
|
||||
* time, the max validity periods will be decreasing as the date increases.
|
||||
* The first entry must have a key of {@link google.registry.util.DateTimeUtils#START_INSTANT},
|
||||
* such that every possible date has an applicable max validity period. Since security
|
||||
* requirements tighten over time, the max validity periods will be decreasing as the date
|
||||
* increases.
|
||||
*
|
||||
* <p>The validity length schedule used by all major Web browsers as of 2020Q4 would be
|
||||
* represented as:
|
||||
*
|
||||
* <pre>
|
||||
* ImmutableSortedMap.of(
|
||||
* START_OF_TIME, 825,
|
||||
* DateTime.parse("2020-09-01T00:00:00Z"), 398
|
||||
* START_INSTANT, 825,
|
||||
* Instant.parse("2020-09-01T00:00:00Z"), 398
|
||||
* );
|
||||
* </pre>
|
||||
*/
|
||||
@Inject
|
||||
public CertificateChecker(
|
||||
@Config("maxValidityDaysSchedule")
|
||||
ImmutableSortedMap<DateTime, Integer> maxValidityDaysSchedule,
|
||||
@Config("expirationWarningDays") int expirationWarningDays,
|
||||
@Config("expirationWarningIntervalDays") int expirationWarningIntervalDays,
|
||||
ImmutableSortedMap<Instant, Integer> maxValidityDaysSchedule,
|
||||
@Config("expirationWarningDays") long expirationWarningDays,
|
||||
@Config("expirationWarningIntervalDays") long expirationWarningIntervalDays,
|
||||
@Config("minimumRsaKeyLength") int minimumRsaKeyLength,
|
||||
@Config("allowedEcdsaCurves") ImmutableSet<String> allowedEcdsaCurves,
|
||||
Clock clock) {
|
||||
checkArgument(
|
||||
maxValidityDaysSchedule.containsKey(START_OF_TIME),
|
||||
"Max validity length schedule must contain an entry for START_OF_TIME");
|
||||
maxValidityDaysSchedule.containsKey(START_INSTANT),
|
||||
"Max validity length schedule must contain an entry for START_INSTANT");
|
||||
this.maxValidityLengthSchedule = maxValidityDaysSchedule;
|
||||
this.expirationWarningDays = expirationWarningDays;
|
||||
this.minimumRsaKeyLength = minimumRsaKeyLength;
|
||||
@@ -94,10 +94,10 @@ public class CertificateChecker {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
private static int getValidityLengthInDays(X509Certificate certificate) {
|
||||
DateTime start = toDateTime(certificate.getNotBefore().toInstant());
|
||||
DateTime end = toDateTime(certificate.getNotAfter().toInstant());
|
||||
return Days.daysBetween(start.withTimeAtStartOfDay(), end.withTimeAtStartOfDay()).getDays();
|
||||
private static long getValidityLengthInDays(X509Certificate certificate) {
|
||||
return ChronoUnit.DAYS.between(
|
||||
toLocalDate(certificate.getNotBefore().toInstant()),
|
||||
toLocalDate(certificate.getNotAfter().toInstant()));
|
||||
}
|
||||
|
||||
/** Checks if the curve used for a public key is in the list of acceptable curves. */
|
||||
@@ -159,16 +159,16 @@ public class CertificateChecker {
|
||||
ImmutableSet.Builder<CertificateViolation> violations = new ImmutableSet.Builder<>();
|
||||
|
||||
// Check if currently in validity period
|
||||
DateTime now = clock.nowUtc();
|
||||
if (DateTimeComparator.getInstance().compare(certificate.getNotAfter(), now) < 0) {
|
||||
Instant now = clock.now();
|
||||
if (certificate.getNotAfter().toInstant().isBefore(now)) {
|
||||
violations.add(CertificateViolation.EXPIRED);
|
||||
} else if (DateTimeComparator.getInstance().compare(certificate.getNotBefore(), now) > 0) {
|
||||
} else if (certificate.getNotBefore().toInstant().isAfter(now)) {
|
||||
violations.add(CertificateViolation.NOT_YET_VALID);
|
||||
}
|
||||
|
||||
// Check validity period length
|
||||
int maxValidityDays =
|
||||
maxValidityLengthSchedule.floorEntry(new DateTime(certificate.getNotBefore())).getValue();
|
||||
long maxValidityDays =
|
||||
maxValidityLengthSchedule.floorEntry(certificate.getNotBefore().toInstant()).getValue();
|
||||
if (getValidityLengthInDays(certificate) > maxValidityDays) {
|
||||
violations.add(CertificateViolation.VALIDITY_LENGTH_TOO_LONG);
|
||||
}
|
||||
@@ -225,25 +225,24 @@ public class CertificateChecker {
|
||||
|
||||
/** Returns whether the client should receive a notification email. */
|
||||
public boolean shouldReceiveExpiringNotification(
|
||||
DateTime lastExpiringNotificationSentDate, String certificateStr) {
|
||||
Instant lastExpiringNotificationSentDate, String certificateStr) {
|
||||
X509Certificate certificate = getCertificate(certificateStr);
|
||||
DateTime now = clock.nowUtc();
|
||||
Instant now = clock.now();
|
||||
// the expiration date is one day after lastValidDate
|
||||
DateTime lastValidDate = new DateTime(certificate.getNotAfter());
|
||||
Instant lastValidDate = certificate.getNotAfter().toInstant();
|
||||
if (lastValidDate.isBefore(now)) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Client should receive a notification if:
|
||||
* 1) client has never received notification (lastExpiringNotificationSentDate is initially
|
||||
* set to START_OF_TIME) and the certificate has entered the expiring period, OR
|
||||
* set to START_INSTANT) and the certificate has entered the expiring period, OR
|
||||
* 2) client has received notification but the interval between now and
|
||||
* lastExpiringNotificationSentDate is greater than expirationWarningIntervalDays.
|
||||
*/
|
||||
return !lastValidDate.isAfter(now.plusDays(expirationWarningDays))
|
||||
&& (lastExpiringNotificationSentDate.equals(START_OF_TIME)
|
||||
|| !lastExpiringNotificationSentDate
|
||||
.plusDays(expirationWarningIntervalDays)
|
||||
return !lastValidDate.isAfter(plusDays(now, expirationWarningDays))
|
||||
&& (lastExpiringNotificationSentDate.equals(START_INSTANT)
|
||||
|| !plusDays(lastExpiringNotificationSentDate, expirationWarningIntervalDays)
|
||||
.isAfter(now));
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ import static google.registry.model.tld.label.ReservationType.NAME_COLLISION;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.END_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.plusYears;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -121,10 +120,9 @@ import google.registry.model.tmch.ClaimsList;
|
||||
import google.registry.model.tmch.ClaimsListDao;
|
||||
import google.registry.tmch.LordnTaskUtils.LordnPhase;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* An EPP flow that creates a new domain resource.
|
||||
@@ -565,7 +563,7 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
ImmutableSet.of(
|
||||
DomainTransactionRecord.create(
|
||||
tld.getTldStr(),
|
||||
now.plusMillis(addGracePeriod.getMillis()),
|
||||
now.plus(addGracePeriod),
|
||||
TransactionReportField.netAddsFieldFromYears(period.getValue()),
|
||||
1)));
|
||||
}
|
||||
@@ -603,11 +601,10 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
.setEventTime(now)
|
||||
.setAllocationToken(allocationToken.map(AllocationToken::createVKey).orElse(null))
|
||||
.setBillingTime(
|
||||
now.plusMillis(
|
||||
(isAnchorTenant
|
||||
? tld.getAnchorTenantAddGracePeriodLength()
|
||||
: tld.getAddGracePeriodLength())
|
||||
.getMillis()))
|
||||
now.plus(
|
||||
isAnchorTenant
|
||||
? tld.getAnchorTenantAddGracePeriodLength()
|
||||
: tld.getAddGracePeriodLength()))
|
||||
.setFlags(flagsBuilder.build())
|
||||
.setDomainHistoryId(domainHistoryId)
|
||||
.build();
|
||||
@@ -652,10 +649,9 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
}
|
||||
|
||||
private void verifyDomainDoesNotExist() throws ResourceCreateContentionException {
|
||||
Optional<DateTime> previousDeletionTime =
|
||||
Optional<Instant> previousDeletionTime =
|
||||
domainDeletionTimeCache.getDeletionTimeForDomain(targetId);
|
||||
if (previousDeletionTime.isPresent()
|
||||
&& !tm().getTxTime().isAfter(toInstant(previousDeletionTime.get()))) {
|
||||
if (previousDeletionTime.isPresent() && !tm().getTxTime().isAfter(previousDeletionTime.get())) {
|
||||
throw new ResourceCreateContentionException(targetId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static google.registry.util.CollectionUtils.union;
|
||||
import static google.registry.util.DateTimeUtils.toDateTime;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
import static google.registry.util.DateTimeUtils.minusYears;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -100,15 +99,13 @@ import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* An EPP flow that deletes a domain.
|
||||
@@ -172,10 +169,9 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
|
||||
eppInput.getSingleExtension(DomainDeleteSuperuserExtension.class);
|
||||
if (domainDeleteSuperuserExtension.isPresent()) {
|
||||
redemptionGracePeriodLength =
|
||||
Duration.standardDays(
|
||||
domainDeleteSuperuserExtension.get().getRedemptionGracePeriodDays());
|
||||
Duration.ofDays(domainDeleteSuperuserExtension.get().getRedemptionGracePeriodDays());
|
||||
pendingDeleteLength =
|
||||
Duration.standardDays(domainDeleteSuperuserExtension.get().getPendingDeleteDays());
|
||||
Duration.ofDays(domainDeleteSuperuserExtension.get().getPendingDeleteDays());
|
||||
}
|
||||
boolean inAddGracePeriod =
|
||||
existingDomain.getGracePeriodStatuses().contains(GracePeriodStatus.ADD);
|
||||
@@ -188,15 +184,13 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
|
||||
: redemptionGracePeriodLength.plus(pendingDeleteLength);
|
||||
HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
|
||||
historyBuilder.setRevisionId(domainHistoryId.getRevisionId());
|
||||
Instant deletionTime = now.plusMillis(durationUntilDelete.getMillis());
|
||||
Instant deletionTime = now.plus(durationUntilDelete);
|
||||
if (durationUntilDelete.equals(Duration.ZERO)) {
|
||||
builder.setDeletionTime(now).setStatusValues(null);
|
||||
} else {
|
||||
Instant redemptionTime = now.plusMillis(redemptionGracePeriodLength.getMillis());
|
||||
Instant redemptionTime = now.plus(redemptionGracePeriodLength);
|
||||
asyncTaskEnqueuer.enqueueAsyncResave(
|
||||
existingDomain.createVKey(),
|
||||
toDateTime(now),
|
||||
ImmutableSortedSet.of(toDateTime(redemptionTime), toDateTime(deletionTime)));
|
||||
existingDomain.createVKey(), now, ImmutableSortedSet.of(redemptionTime, deletionTime));
|
||||
builder
|
||||
.setDeletionTime(deletionTime)
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
||||
@@ -219,7 +213,7 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
|
||||
// Enqueue the deletion poll message if the delete is asynchronous or if requested by a
|
||||
// superuser (i.e. the registrar didn't request this delete and thus should be notified even if
|
||||
// it is synchronous).
|
||||
if (durationUntilDelete.isLongerThan(Duration.ZERO) || isSuperuser) {
|
||||
if (durationUntilDelete.compareTo(Duration.ZERO) > 0 || isSuperuser) {
|
||||
if (RegistryConfig.getNoPollMessageOnDeletionRegistrarIds()
|
||||
.contains(existingDomain.getCurrentSponsorRegistrarId())) {
|
||||
logger.atInfo().log(
|
||||
@@ -235,7 +229,7 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
|
||||
|
||||
// Send a second poll message immediately if the domain is being deleted asynchronously by a
|
||||
// registrar other than the sponsoring registrar (which will necessarily be a superuser).
|
||||
if (durationUntilDelete.isLongerThan(Duration.ZERO)
|
||||
if (durationUntilDelete.compareTo(Duration.ZERO) > 0
|
||||
&& !registrarId.equals(existingDomain.getPersistedCurrentSponsorRegistrarId())) {
|
||||
entitiesToInsert.add(
|
||||
createImmediateDeletePollMessage(existingDomain, domainHistoryId, now, deletionTime));
|
||||
@@ -252,15 +246,11 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
|
||||
// Take the amount of registration time being refunded off the expiration time.
|
||||
// This can be either add grace periods or renew grace periods.
|
||||
BillingEvent billingEvent = tm().loadByKey(gracePeriod.getBillingEvent());
|
||||
newExpirationTime =
|
||||
newExpirationTime
|
||||
.atZone(ZoneOffset.UTC)
|
||||
.minusYears(billingEvent.getPeriodYears())
|
||||
.toInstant();
|
||||
newExpirationTime = minusYears(newExpirationTime, billingEvent.getPeriodYears());
|
||||
} else if (gracePeriod.getBillingRecurrence() != null) {
|
||||
// Take 1 year off the registration if in the autorenew grace period (no need to load the
|
||||
// recurrence billing event; all autorenews are for 1 year).
|
||||
newExpirationTime = newExpirationTime.atZone(ZoneOffset.UTC).minusYears(1).toInstant();
|
||||
newExpirationTime = minusYears(newExpirationTime, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,7 +338,7 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
|
||||
cancelledRecords,
|
||||
DomainTransactionRecord.create(
|
||||
domain.getTld(),
|
||||
now.plusMillis(durationUntilDelete.getMillis()),
|
||||
now.plus(durationUntilDelete),
|
||||
inAddGracePeriod
|
||||
? TransactionReportField.DELETED_DOMAINS_GRACE
|
||||
: TransactionReportField.DELETED_DOMAINS_NOGRACE,
|
||||
@@ -425,9 +415,9 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
|
||||
BillingRecurrence billingRecurrence, GracePeriod gracePeriod, Instant now) {
|
||||
if (gracePeriod.getType() == GracePeriodStatus.AUTO_RENEW) {
|
||||
// If we updated the autorenew billing event, reuse it.
|
||||
DateTime autoRenewTime =
|
||||
toDateTime(billingRecurrence.getRecurrenceTimeOfYear().getLastInstanceBeforeOrAt(now));
|
||||
return getDomainRenewCost(targetId, toInstant(autoRenewTime), 1);
|
||||
Instant autoRenewTime =
|
||||
billingRecurrence.getRecurrenceTimeOfYear().getLastInstanceBeforeOrAt(now);
|
||||
return getDomainRenewCost(targetId, autoRenewTime, 1);
|
||||
}
|
||||
return tm().loadByKey(checkNotNull(gracePeriod.getBillingEvent())).getCost();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.flows.domain;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.END_INSTANT;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
@@ -24,9 +25,10 @@ import com.github.benmanes.caffeine.cache.Ticker;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Functionally-static loading cache that keeps track of deletion (AKA drop) times for domains.
|
||||
@@ -39,7 +41,7 @@ import org.joda.time.DateTime;
|
||||
*
|
||||
* <p>The cache is fairly short-lived (as we're concerned about many requests at basically the same
|
||||
* time), and entries also expire when the drop actually happens. If the domain is re-created after
|
||||
* a drop, the next load attempt will populate the cache with a deletion time of END_OF_TIME, which
|
||||
* a drop, the next load attempt will populate the cache with a deletion time of END_INSTANT, which
|
||||
* will be read from the cache by subsequent attempts.
|
||||
*
|
||||
* <p>We take advantage of the fact that Caffeine caches don't store nulls returned from the
|
||||
@@ -57,8 +59,7 @@ import org.joda.time.DateTime;
|
||||
public class DomainDeletionTimeCache {
|
||||
|
||||
// Max expiry time is ten minutes
|
||||
private static final long NANOS_IN_ONE_MILLISECOND = 100000L;
|
||||
private static final long MAX_EXPIRY_NANOS = 10L * 60L * 1000L * NANOS_IN_ONE_MILLISECOND;
|
||||
private static final long MAX_EXPIRY_NANOS = Duration.ofMinutes(10).toNanos();
|
||||
private static final int MAX_ENTRIES = 500;
|
||||
|
||||
/**
|
||||
@@ -69,15 +70,15 @@ public class DomainDeletionTimeCache {
|
||||
*
|
||||
* <p>NB: the Expiry class requires the return value in <b>nanoseconds</b>, not milliseconds
|
||||
*/
|
||||
private static final Expiry<String, DateTime> EXPIRY_POLICY =
|
||||
private static final Expiry<String, Instant> EXPIRY_POLICY =
|
||||
new Expiry<>() {
|
||||
@Override
|
||||
public long expireAfterCreate(String key, DateTime value, long currentTime) {
|
||||
public long expireAfterCreate(String key, Instant value, long currentTime) {
|
||||
// Watch out for Long overflow
|
||||
long deletionTimeNanos =
|
||||
value.equals(DateTimeUtils.END_OF_TIME)
|
||||
value.equals(END_INSTANT)
|
||||
? Long.MAX_VALUE
|
||||
: value.getMillis() * NANOS_IN_ONE_MILLISECOND;
|
||||
: ChronoUnit.NANOS.between(Instant.EPOCH, value);
|
||||
long nanosUntilDeletion = deletionTimeNanos - currentTime;
|
||||
return Math.max(0L, Math.min(MAX_EXPIRY_NANOS, nanosUntilDeletion));
|
||||
}
|
||||
@@ -85,31 +86,31 @@ public class DomainDeletionTimeCache {
|
||||
/** Reset the time entirely on update, as if we were creating the entry anew. */
|
||||
@Override
|
||||
public long expireAfterUpdate(
|
||||
String key, DateTime value, long currentTime, long currentDuration) {
|
||||
String key, Instant value, long currentTime, long currentDuration) {
|
||||
return expireAfterCreate(key, value, currentTime);
|
||||
}
|
||||
|
||||
/** Reads do not change the expiry duration. */
|
||||
@Override
|
||||
public long expireAfterRead(
|
||||
String key, DateTime value, long currentTime, long currentDuration) {
|
||||
String key, Instant value, long currentTime, long currentDuration) {
|
||||
return currentDuration;
|
||||
}
|
||||
};
|
||||
|
||||
/** Attempt to load the domain's deletion time if the domain exists. */
|
||||
private static final CacheLoader<String, DateTime> CACHE_LOADER =
|
||||
private static final CacheLoader<String, Instant> CACHE_LOADER =
|
||||
(domainName) -> {
|
||||
ForeignKeyUtils.MostRecentResource mostRecentResource =
|
||||
ForeignKeyUtils.loadMostRecentResources(
|
||||
Domain.class, ImmutableSet.of(domainName), false)
|
||||
.get(domainName);
|
||||
return mostRecentResource == null ? null : mostRecentResource.getDeletionTime();
|
||||
return mostRecentResource == null ? null : mostRecentResource.deletionTime();
|
||||
};
|
||||
|
||||
// Unfortunately, maintenance tasks aren't necessarily already in a transaction
|
||||
private static final Ticker TRANSACTION_TIME_TICKER =
|
||||
() -> tm().reTransact(() -> tm().getTransactionTime().getMillis() * NANOS_IN_ONE_MILLISECOND);
|
||||
() -> tm().reTransact(() -> ChronoUnit.NANOS.between(Instant.EPOCH, tm().getTxTime()));
|
||||
|
||||
public static DomainDeletionTimeCache create() {
|
||||
return new DomainDeletionTimeCache(
|
||||
@@ -120,14 +121,14 @@ public class DomainDeletionTimeCache {
|
||||
.build(CACHE_LOADER));
|
||||
}
|
||||
|
||||
private final LoadingCache<String, DateTime> cache;
|
||||
private final LoadingCache<String, Instant> cache;
|
||||
|
||||
private DomainDeletionTimeCache(LoadingCache<String, DateTime> cache) {
|
||||
private DomainDeletionTimeCache(LoadingCache<String, Instant> cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
/** Returns the domain's deletion time, or null if it doesn't currently exist. */
|
||||
public Optional<DateTime> getDeletionTimeForDomain(String domainName) {
|
||||
public Optional<Instant> getDeletionTimeForDomain(String domainName) {
|
||||
return Optional.ofNullable(cache.get(domainName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import static google.registry.util.DateTimeUtils.END_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
import static google.registry.util.DateTimeUtils.minusDays;
|
||||
import static google.registry.util.DomainNameUtils.ACE_PREFIX;
|
||||
import static java.time.ZoneOffset.UTC;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
@@ -123,8 +124,8 @@ import google.registry.tldconfig.idn.IdnLabelValidator;
|
||||
import google.registry.tools.DigestType;
|
||||
import google.registry.util.Idn;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
@@ -133,7 +134,6 @@ import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.Duration;
|
||||
import org.xbill.DNS.DNSSEC.Algorithm;
|
||||
|
||||
/** Static utility functions for domain flows. */
|
||||
@@ -848,10 +848,7 @@ public class DomainFlowUtils {
|
||||
*/
|
||||
public static void validateRegistrationPeriod(Instant now, Instant newExpirationTime)
|
||||
throws EppException {
|
||||
if (now.atZone(ZoneOffset.UTC)
|
||||
.plusYears(MAX_REGISTRATION_YEARS)
|
||||
.toInstant()
|
||||
.isBefore(newExpirationTime)) {
|
||||
if (now.atZone(UTC).plusYears(MAX_REGISTRATION_YEARS).toInstant().isBefore(newExpirationTime)) {
|
||||
throw new ExceedsMaxRegistrationYearsException();
|
||||
}
|
||||
}
|
||||
@@ -1117,7 +1114,7 @@ public class DomainFlowUtils {
|
||||
"FROM DomainHistory WHERE modificationTime >= :beginning AND repoId = "
|
||||
+ ":repoId ORDER BY modificationTime ASC",
|
||||
DomainHistory.class)
|
||||
.setParameter("beginning", now.minusMillis(maxSearchPeriod.getMillis()))
|
||||
.setParameter("beginning", now.minus(maxSearchPeriod))
|
||||
.setParameter("repoId", domain.getRepoId())
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ import static google.registry.flows.domain.token.AllocationTokenFlowUtils.maybeA
|
||||
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyBulkTokenAllowedOnDomain;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RENEW;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.toDateTime;
|
||||
import static google.registry.util.DateTimeUtils.toLocalDate;
|
||||
import static java.time.ZoneOffset.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -86,11 +87,10 @@ import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Tld;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* An EPP flow that renews a domain.
|
||||
@@ -194,7 +194,7 @@ public final class DomainRenewFlow implements MutatingFlow {
|
||||
Instant newExpirationTime =
|
||||
existingDomain
|
||||
.getRegistrationExpirationTime()
|
||||
.atZone(ZoneOffset.UTC)
|
||||
.atZone(UTC)
|
||||
.plusYears(years)
|
||||
.toInstant(); // Uncapped
|
||||
validateRegistrationPeriod(now, newExpirationTime);
|
||||
@@ -310,7 +310,7 @@ public final class DomainRenewFlow implements MutatingFlow {
|
||||
ImmutableSet.of(
|
||||
DomainTransactionRecord.create(
|
||||
newDomain.getTld(),
|
||||
now.plusMillis(renewGracePeriod.getMillis()),
|
||||
now.plus(renewGracePeriod),
|
||||
TransactionReportField.netRenewsFieldFromYears(period.getValue()),
|
||||
1)))
|
||||
.build();
|
||||
@@ -335,7 +335,7 @@ public final class DomainRenewFlow implements MutatingFlow {
|
||||
// If the date they specify doesn't match the expiration, fail. (This is an idempotence check).
|
||||
if (!command
|
||||
.getCurrentExpirationDate()
|
||||
.equals(toDateTime(existingDomain.getRegistrationExpirationTime()).toLocalDate())) {
|
||||
.equals(toLocalDate(existingDomain.getRegistrationExpirationTime()))) {
|
||||
throw new IncorrectCurrentExpirationDateException();
|
||||
}
|
||||
}
|
||||
@@ -359,7 +359,7 @@ public final class DomainRenewFlow implements MutatingFlow {
|
||||
.filter(t -> AllocationToken.TokenBehavior.DEFAULT.equals(t.getTokenBehavior()))
|
||||
.map(AllocationToken::createVKey)
|
||||
.orElse(null))
|
||||
.setBillingTime(now.plusMillis(Tld.get(tld).getRenewGracePeriodLength().getMillis()))
|
||||
.setBillingTime(now.plus(Tld.get(tld).getRenewGracePeriodLength()))
|
||||
.setDomainHistoryId(domainHistoryId)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActi
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RESTORE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.END_INSTANT;
|
||||
import static java.time.ZoneOffset.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -70,7 +71,6 @@ import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Tld;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.Money;
|
||||
|
||||
@@ -152,7 +152,7 @@ public final class DomainRestoreRequestFlow implements MutatingFlow {
|
||||
Instant newExpirationTime =
|
||||
existingDomain
|
||||
.getRegistrationExpirationTime()
|
||||
.atZone(ZoneOffset.UTC)
|
||||
.atZone(UTC)
|
||||
.plusYears(isExpired ? 1 : 0)
|
||||
.toInstant();
|
||||
// Restore the expiration time on the deleted domain, except if that's already passed, then add
|
||||
|
||||
@@ -168,8 +168,7 @@ public final class DomainTransferApproveFlow implements MutatingFlow {
|
||||
hasBulkToken ? null : existingBillingRecurrence)
|
||||
.getRenewCost())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(
|
||||
now.plusMillis(Tld.get(tldStr).getTransferGracePeriodLength().getMillis()))
|
||||
.setBillingTime(now.plus(Tld.get(tldStr).getTransferGracePeriodLength()))
|
||||
.setDomainHistoryId(domainHistoryId)
|
||||
.build());
|
||||
|
||||
@@ -292,7 +291,7 @@ public final class DomainTransferApproveFlow implements MutatingFlow {
|
||||
cancelingRecords,
|
||||
DomainTransactionRecord.create(
|
||||
newDomain.getTld(),
|
||||
now.plusMillis(tld.getTransferGracePeriodLength().getMillis()),
|
||||
now.plus(tld.getTransferGracePeriodLength()),
|
||||
TRANSFER_SUCCESSFUL,
|
||||
1)))
|
||||
.build();
|
||||
|
||||
@@ -32,7 +32,6 @@ import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.union;
|
||||
import static google.registry.util.DateTimeUtils.END_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.toDateTime;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.flows.EppException;
|
||||
@@ -108,8 +107,7 @@ public final class DomainTransferRejectFlow implements MutatingFlow {
|
||||
checkAllowedAccessToTld(registrarId, existingDomain.getTld());
|
||||
}
|
||||
Domain newDomain =
|
||||
denyPendingTransfer(
|
||||
existingDomain, TransferStatus.CLIENT_REJECTED, toDateTime(now), registrarId);
|
||||
denyPendingTransfer(existingDomain, TransferStatus.CLIENT_REJECTED, now, registrarId);
|
||||
DomainHistory domainHistory = buildDomainHistory(newDomain, tld, now);
|
||||
tm().update(newDomain);
|
||||
tm().insertAll(
|
||||
|
||||
@@ -35,7 +35,7 @@ import static google.registry.flows.domain.DomainTransferUtils.createTransferSer
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.toDateTime;
|
||||
import static java.time.temporal.ChronoUnit.DAYS;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -82,7 +82,6 @@ import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@@ -220,9 +219,8 @@ public final class DomainTransferRequestFlow implements MutatingFlow {
|
||||
.map(
|
||||
domainTransferRequestSuperuserExtension ->
|
||||
now.plus(
|
||||
domainTransferRequestSuperuserExtension.getAutomaticTransferLength(),
|
||||
ChronoUnit.DAYS))
|
||||
.orElseGet(() -> now.plusMillis(tld.getAutomaticTransferLength().getMillis()));
|
||||
domainTransferRequestSuperuserExtension.getAutomaticTransferLength(), DAYS))
|
||||
.orElseGet(() -> now.plus(tld.getAutomaticTransferLength()));
|
||||
// If the domain will be in the auto-renew grace period at the moment of transfer, the transfer
|
||||
// will subsume the autorenew, so we don't add the normal extra year from the transfer.
|
||||
// The gaining registrar is still billed for the extra year; the losing registrar will get a
|
||||
@@ -284,9 +282,7 @@ public final class DomainTransferRequestFlow implements MutatingFlow {
|
||||
DomainHistory domainHistory = buildDomainHistory(newDomain, tld, now, period);
|
||||
|
||||
asyncTaskEnqueuer.enqueueAsyncResave(
|
||||
newDomain.createVKey(),
|
||||
toDateTime(now),
|
||||
ImmutableSortedSet.of(toDateTime(automaticTransferTime)));
|
||||
newDomain.createVKey(), now, ImmutableSortedSet.of(automaticTransferTime));
|
||||
tm().put(newDomain);
|
||||
tm().putAll(serverApproveEntities);
|
||||
tm().insertAll(domainHistory, requestPollMessage);
|
||||
@@ -372,8 +368,8 @@ public final class DomainTransferRequestFlow implements MutatingFlow {
|
||||
ImmutableSet.of(
|
||||
DomainTransactionRecord.create(
|
||||
tld.getTldStr(),
|
||||
now.plusMillis(tld.getAutomaticTransferLength().getMillis())
|
||||
.plusMillis(tld.getTransferGracePeriodLength().getMillis()),
|
||||
now.plus(tld.getAutomaticTransferLength())
|
||||
.plus(tld.getTransferGracePeriodLength()),
|
||||
TransactionReportField.TRANSFER_SUCCESSFUL,
|
||||
1)))
|
||||
.build();
|
||||
|
||||
@@ -316,8 +316,7 @@ public final class DomainTransferUtils {
|
||||
.setCost(transferCost)
|
||||
.setPeriodYears(1)
|
||||
.setEventTime(automaticTransferTime)
|
||||
.setBillingTime(
|
||||
automaticTransferTime.plusMillis(registry.getTransferGracePeriodLength().getMillis()))
|
||||
.setBillingTime(automaticTransferTime.plus(registry.getTransferGracePeriodLength()))
|
||||
.setDomainHistoryId(domainHistoryId)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ public final class PollAckFlow implements MutatingFlow {
|
||||
throw new InvalidMessageIdException(messageId);
|
||||
}
|
||||
|
||||
final Instant now = tm().getTxTime();
|
||||
Instant now = tm().getTxTime();
|
||||
|
||||
// Load the message to be acked. If a message is queued to be delivered in the future, we treat
|
||||
// it as if it doesn't exist yet. Same for if the message ID year isn't the same as the actual
|
||||
|
||||
@@ -33,13 +33,13 @@ import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.Function;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Simple load test action that can generate configurable QPSes of various EPP actions.
|
||||
@@ -160,7 +160,7 @@ public class LoadTestAction implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
validateAndLogRequest();
|
||||
DateTime initialStartSecond = clock.nowUtc().plusSeconds(delaySeconds);
|
||||
Instant initialStartSecond = clock.now().plus(Duration.ofSeconds(delaySeconds));
|
||||
ImmutableList.Builder<String> preTaskXmls = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<String> hostPrefixesBuilder = new ImmutableList.Builder<>();
|
||||
for (int i = 0; i < successfulDomainCreatesPerSecond; i++) {
|
||||
@@ -169,12 +169,12 @@ public class LoadTestAction implements Runnable {
|
||||
preTaskXmls.add(
|
||||
xmlHostCreateTmpl.replace("%host%", hostPrefix));
|
||||
}
|
||||
enqueue(createTasks(preTaskXmls.build(), clock.nowUtc()));
|
||||
enqueue(createTasks(preTaskXmls.build(), clock.now()));
|
||||
ImmutableList<String> hostPrefixes = hostPrefixesBuilder.build();
|
||||
|
||||
ImmutableList.Builder<Task> tasks = new ImmutableList.Builder<>();
|
||||
for (int offsetSeconds = 0; offsetSeconds < runSeconds; offsetSeconds++) {
|
||||
DateTime startSecond = initialStartSecond.plusSeconds(offsetSeconds);
|
||||
Instant startSecond = initialStartSecond.plus(Duration.ofSeconds(offsetSeconds));
|
||||
// The first "failed" creates might actually succeed if the object doesn't already exist, but
|
||||
// that shouldn't affect the load numbers.
|
||||
tasks.addAll(
|
||||
@@ -265,12 +265,11 @@ public class LoadTestAction implements Runnable {
|
||||
return name.toString();
|
||||
}
|
||||
|
||||
private ImmutableList<Task> createTasks(ImmutableList<String> xmls, DateTime start) {
|
||||
private ImmutableList<Task> createTasks(ImmutableList<String> xmls, Instant start) {
|
||||
ImmutableList.Builder<Task> tasks = new ImmutableList.Builder<>();
|
||||
for (int i = 0; i < xmls.size(); i++) {
|
||||
// Space tasks evenly within across a second.
|
||||
Instant scheduleTime =
|
||||
Instant.ofEpochMilli(start.plusMillis((int) (1000.0 / xmls.size() * i)).getMillis());
|
||||
Instant scheduleTime = start.plus(Duration.ofMillis((long) (1000.0 / xmls.size() * i)));
|
||||
tasks.add(
|
||||
cloudTasksUtils
|
||||
.createTask(
|
||||
|
||||
@@ -42,6 +42,7 @@ import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -51,7 +52,6 @@ import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** A collection of static utility classes/functions to convert entities to/from YAML files. */
|
||||
public class EntityYamlUtils {
|
||||
@@ -337,7 +337,7 @@ public class EntityYamlUtils {
|
||||
public TimedTransitionProperty<TldState> deserialize(
|
||||
JsonParser jp, DeserializationContext context) throws IOException {
|
||||
SortedMap<String, String> valueMap = jp.readValueAs(SortedMap.class);
|
||||
return TimedTransitionProperty.fromValueMapInstant(
|
||||
return TimedTransitionProperty.fromValueMap(
|
||||
valueMap.keySet().stream()
|
||||
.collect(
|
||||
toImmutableSortedMap(
|
||||
@@ -363,7 +363,7 @@ public class EntityYamlUtils {
|
||||
public TimedTransitionProperty<Money> deserialize(JsonParser jp, DeserializationContext context)
|
||||
throws IOException {
|
||||
SortedMap<String, LinkedHashMap<String, Object>> valueMap = jp.readValueAs(SortedMap.class);
|
||||
return TimedTransitionProperty.fromValueMapInstant(
|
||||
return TimedTransitionProperty.fromValueMap(
|
||||
valueMap.keySet().stream()
|
||||
.collect(
|
||||
toImmutableSortedMap(
|
||||
@@ -393,7 +393,7 @@ public class EntityYamlUtils {
|
||||
public TimedTransitionProperty<FeatureStatus> deserialize(
|
||||
JsonParser jp, DeserializationContext context) throws IOException {
|
||||
SortedMap<String, String> valueMap = jp.readValueAs(SortedMap.class);
|
||||
return TimedTransitionProperty.fromValueMapInstant(
|
||||
return TimedTransitionProperty.fromValueMap(
|
||||
valueMap.keySet().stream()
|
||||
.collect(
|
||||
toImmutableSortedMap(
|
||||
|
||||
@@ -115,7 +115,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
* <ul>
|
||||
* <li>For deleted resources, this is in the past.
|
||||
* <li>For pending-delete resources, this is in the near future.
|
||||
* <li>For active resources, this is {@code END_OF_TIME}.
|
||||
* <li>For active resources, this is {@code END_INSTANT}.
|
||||
* </ul>
|
||||
*
|
||||
* <p>This scheme allows for setting pending deletes in the future and having them magically drop
|
||||
|
||||
@@ -20,7 +20,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
@@ -39,7 +38,6 @@ import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Utilities for working with {@link EppResource}. */
|
||||
public final class EppResourceUtils {
|
||||
@@ -65,18 +63,12 @@ public final class EppResourceUtils {
|
||||
return String.format("%X-%s", repoId, roidSuffix);
|
||||
}
|
||||
|
||||
/** Helper to call {@link EppResource#cloneProjectedAtTime} without warnings. */
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends EppResource> T cloneProjectedAtTime(T resource, DateTime now) {
|
||||
return (T) resource.cloneProjectedAtTime(toInstant(now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Function that transforms an EppResource to the given DateTime, suitable for use with
|
||||
* Returns a Function that transforms an EppResource to the given Instant, suitable for use with
|
||||
* Iterables.transform() over a collection of EppResources.
|
||||
*/
|
||||
public static <T extends EppResource> Function<T, T> transformAtTime(final DateTime now) {
|
||||
return (T resource) -> cloneProjectedAtTime(resource, now);
|
||||
public static <T extends EppResource> Function<T, T> transformAtTime(final Instant now) {
|
||||
return (T resource) -> (T) resource.cloneProjectedAtTime(now);
|
||||
}
|
||||
|
||||
public static boolean isActive(EppResource resource, Instant time) {
|
||||
@@ -84,28 +76,10 @@ public final class EppResourceUtils {
|
||||
&& time.isBefore(resource.getDeletionTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #isActive(EppResource, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static boolean isActive(EppResource resource, DateTime time) {
|
||||
return isActive(resource, toInstant(time));
|
||||
}
|
||||
|
||||
public static boolean isDeleted(EppResource resource, Instant time) {
|
||||
return !isActive(resource, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #isDeleted(EppResource, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static boolean isDeleted(EppResource resource, DateTime time) {
|
||||
return isDeleted(resource, toInstant(time));
|
||||
}
|
||||
|
||||
/** Process an automatic transfer on a domain. */
|
||||
public static void setAutomaticTransferSuccessProperties(
|
||||
DomainBase.Builder<?, ?> builder, DomainTransferData transferData) {
|
||||
@@ -173,16 +147,6 @@ public final class EppResourceUtils {
|
||||
: null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #loadAtPointInTime(EppResource, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static <T extends EppResource> T loadAtPointInTime(
|
||||
final T resource, final DateTime timestamp) {
|
||||
return loadAtPointInTime(resource, toInstant(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.
|
||||
@@ -207,21 +171,6 @@ public final class EppResourceUtils {
|
||||
return resourceAtPointInTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of {@link VKey} for domains that reference a specified host.
|
||||
*
|
||||
* @param key the referent key
|
||||
* @param now the logical time of the check /** Returns the domains that are linked to this host
|
||||
* at the given time.
|
||||
* @deprecated Use {@link #getLinkedDomainKeys(VKey, Instant, Integer)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static ImmutableSet<VKey<Domain>> getLinkedDomainKeys(
|
||||
VKey<Host> key, DateTime now, @Nullable Integer limit) {
|
||||
return getLinkedDomainKeys(key, toInstant(now), limit);
|
||||
}
|
||||
|
||||
/** Returns the domains that are linked to this host at the given time. */
|
||||
public static ImmutableSet<VKey<Domain>> getLinkedDomainKeys(
|
||||
VKey<Host> key, Instant now, @Nullable Integer limit) {
|
||||
@@ -246,17 +195,6 @@ public final class EppResourceUtils {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this host is linked to any domains at the given time.
|
||||
*
|
||||
* @deprecated Use {@link #isLinked(VKey, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static boolean isLinked(VKey<Host> key, DateTime now) {
|
||||
return isLinked(key, toInstant(now));
|
||||
}
|
||||
|
||||
/** Returns whether this resource is linked to any domains at the given time. */
|
||||
public static boolean isLinked(VKey<Host> key, Instant now) {
|
||||
return !getLinkedDomainKeys(key, now, 1).isEmpty();
|
||||
|
||||
@@ -20,8 +20,6 @@ import static google.registry.config.RegistryConfig.getEppResourceCachingDuratio
|
||||
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.toDateTime;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
@@ -43,7 +41,6 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Util class for mapping a foreign key to a {@link VKey} or {@link EppResource}.
|
||||
@@ -65,30 +62,7 @@ public final class ForeignKeyUtils {
|
||||
Domain.class, "domainName",
|
||||
Host.class, "hostName");
|
||||
|
||||
public record MostRecentResource(String repoId, Instant deletionTime) {
|
||||
/**
|
||||
* @deprecated Use {@link #deletionTime()}
|
||||
*/
|
||||
@Deprecated
|
||||
public DateTime getDeletionTime() {
|
||||
return toDateTime(deletionTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an optional {@link VKey} to an {@link EppResource} from the database by foreign key.
|
||||
*
|
||||
* <p>Returns empty if no resource with this foreign key was ever created, or if the most recently
|
||||
* created resource was deleted before time "now".
|
||||
*
|
||||
* @deprecated Use {@link #loadKey(Class, String, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static <E extends EppResource> Optional<VKey<E>> loadKey(
|
||||
Class<E> clazz, String foreignKey, DateTime now) {
|
||||
return loadKey(clazz, foreignKey, toInstant(now));
|
||||
}
|
||||
public record MostRecentResource(String repoId, Instant deletionTime) {}
|
||||
|
||||
/**
|
||||
* Loads an optional {@link VKey} to an {@link EppResource} from the database by foreign key.
|
||||
@@ -101,21 +75,6 @@ public final class ForeignKeyUtils {
|
||||
return Optional.ofNullable(loadKeys(clazz, ImmutableList.of(foreignKey), now).get(foreignKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an {@link EppResource} from the database by foreign key.
|
||||
*
|
||||
* <p>Returns null if no resource with this foreign key was ever created or if the most recently
|
||||
* created resource was deleted before time "now".
|
||||
*
|
||||
* @deprecated Use {@link #loadResource(Class, String, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static <E extends EppResource> Optional<E> loadResource(
|
||||
Class<E> clazz, String foreignKey, DateTime now) {
|
||||
return loadResource(clazz, foreignKey, toInstant(now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an {@link EppResource} from the database by foreign key.
|
||||
*
|
||||
@@ -129,22 +88,6 @@ public final class ForeignKeyUtils {
|
||||
loadResources(clazz, ImmutableList.of(foreignKey), now).get(foreignKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a map of {@link String} foreign keys to {@link VKey}s to {@link EppResource} that are
|
||||
* active at or after the specified moment in time.
|
||||
*
|
||||
* <p>The returned map will omit any foreign keys for which the {@link EppResource} doesn't exist
|
||||
* or has been soft-deleted.
|
||||
*
|
||||
* @deprecated Use {@link #loadKeys(Class, Collection, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static <E extends EppResource> ImmutableMap<String, VKey<E>> loadKeys(
|
||||
Class<E> clazz, Collection<String> foreignKeys, DateTime now) {
|
||||
return loadKeys(clazz, foreignKeys, toInstant(now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a map of {@link String} foreign keys to {@link VKey}s to {@link EppResource} that are
|
||||
* active at or after the specified moment in time.
|
||||
@@ -159,20 +102,6 @@ public final class ForeignKeyUtils {
|
||||
.collect(toImmutableMap(Entry::getKey, e -> VKey.create(clazz, e.getValue().repoId())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a map of {@link String} foreign keys to the {@link EppResource} that are active at or
|
||||
* after the specified moment in time.
|
||||
*
|
||||
* <p>The returned map will omit any foreign keys for which the {@link EppResource} doesn't exist
|
||||
* or has been soft-deleted.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "InlineMeSuggester"})
|
||||
@Deprecated
|
||||
public static <E extends EppResource> ImmutableMap<String, E> loadResources(
|
||||
Class<E> clazz, Collection<String> foreignKeys, DateTime now) {
|
||||
return loadResources(clazz, foreignKeys, toInstant(now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a map of {@link String} foreign keys to the {@link EppResource} that are active at or
|
||||
* after the specified moment in time.
|
||||
@@ -198,7 +127,7 @@ public final class ForeignKeyUtils {
|
||||
* should monotonically increase as one cannot create a domain/host/contact with the same foreign
|
||||
* key without soft deleting the existing resource first. However, in test, there's no such
|
||||
* guarantee and one must make sure that no two resources with the same foreign key exist with the
|
||||
* same max {@code deleteTime}, usually {@code END_OF_TIME}, lest this method throws an error due
|
||||
* same max {@code deleteTime}, usually {@code END_INSTANT}, lest this method throws an error due
|
||||
* to duplicate keys.
|
||||
*/
|
||||
public static <E extends EppResource>
|
||||
@@ -322,25 +251,6 @@ public final class ForeignKeyUtils {
|
||||
foreignKeyToRepoIdCache = createForeignKeyToRepoIdCache(effectiveExpiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of {@link VKey} to {@link EppResource} instances by class and foreign key strings
|
||||
* that are active at or after the specified moment in time, using the cache if enabled.
|
||||
*
|
||||
* <p>The returned map will omit any keys for which the {@link EppResource} doesn't exist or has
|
||||
* been soft-deleted.
|
||||
*
|
||||
* <p>Don't use the cached version of this method unless you really need it for performance
|
||||
* reasons, and are OK with the trade-offs in loss of transactional consistency.
|
||||
*
|
||||
* @deprecated Use {@link #loadKeysByCacheIfEnabled(Class, Collection, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static <E extends EppResource> ImmutableMap<String, VKey<E>> loadKeysByCacheIfEnabled(
|
||||
Class<E> clazz, Collection<String> foreignKeys, DateTime now) {
|
||||
return loadKeysByCacheIfEnabled(clazz, foreignKeys, toInstant(now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of {@link VKey} to {@link EppResource} instances by class and foreign key strings
|
||||
* that are active at or after the specified moment in time, using the cache if enabled.
|
||||
@@ -367,18 +277,6 @@ public final class ForeignKeyUtils {
|
||||
e -> VKey.create(clazz, e.getValue().get().repoId())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an optional {@link VKey} to an {@link EppResource} using the cache.
|
||||
*
|
||||
* @deprecated Use {@link #loadKeyByCache(Class, String, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static <E extends EppResource> Optional<VKey<E>> loadKeyByCache(
|
||||
Class<E> clazz, String foreignKey, DateTime now) {
|
||||
return loadKeyByCache(clazz, foreignKey, toInstant(now));
|
||||
}
|
||||
|
||||
/** Loads an optional {@link VKey} to an {@link EppResource} using the cache. */
|
||||
public static <E extends EppResource> Optional<VKey<E>> loadKeyByCache(
|
||||
Class<E> clazz, String foreignKey, Instant now) {
|
||||
@@ -461,33 +359,6 @@ public final class ForeignKeyUtils {
|
||||
foreignKeyToResourceCache = createForeignKeyToResourceCache(effectiveExpiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the last created version of an {@link EppResource} from the database by foreign key,
|
||||
* using a cache, if caching is enabled in config settings.
|
||||
*
|
||||
* <p>Returns null if no resource with this foreign key was ever created, or if the most recently
|
||||
* created resource was deleted before time "now".
|
||||
*
|
||||
* <p>Loading an {@link EppResource} by itself is not sufficient to know its current state since
|
||||
* it may have various expirable conditions and status values that might implicitly change its
|
||||
* state as time progresses even if it has not been updated in the database. Rather, the resource
|
||||
* must be combined with a timestamp to view its current state. We use a global last updated
|
||||
* timestamp to guarantee monotonically increasing write times, and forward our projected time to
|
||||
* the greater of this timestamp or "now". This guarantees that we're not projecting into the
|
||||
* past.
|
||||
*
|
||||
* <p>Do not call this cached version for anything that needs transactional consistency. It should
|
||||
* only be used when it's OK if the data is potentially being out of date, e.g. RDAP.
|
||||
*
|
||||
* @deprecated Use {@link #loadResourceByCacheIfEnabled(Class, String, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static <E extends EppResource> Optional<E> loadResourceByCacheIfEnabled(
|
||||
Class<E> clazz, String foreignKey, DateTime now) {
|
||||
return loadResourceByCacheIfEnabled(clazz, foreignKey, toInstant(now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the last created version of an {@link EppResource} from the database by foreign key,
|
||||
* using a cache, if caching is enabled in config settings.
|
||||
@@ -513,22 +384,6 @@ public final class ForeignKeyUtils {
|
||||
: loadResource(clazz, foreignKey, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the last created version of an {@link EppResource} from the replica database by foreign
|
||||
* key, using a cache.
|
||||
*
|
||||
* <p>This method ignores the config setting for caching, and is reserved for use cases that can
|
||||
* tolerate slightly stale data.
|
||||
*
|
||||
* @deprecated Use {@link #loadResourceByCache(Class, String, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static <E extends EppResource> Optional<E> loadResourceByCache(
|
||||
Class<E> clazz, String foreignKey, DateTime now) {
|
||||
return loadResourceByCache(clazz, foreignKey, toInstant(now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the last created version of an {@link EppResource} from the replica database by foreign
|
||||
* key, using a cache.
|
||||
|
||||
@@ -22,7 +22,6 @@ import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -46,6 +45,7 @@ import google.registry.persistence.VKey;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -55,8 +55,6 @@ import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Class to help build and persist all the OT&E entities in the database.
|
||||
@@ -94,9 +92,9 @@ public final class OteAccountBuilder {
|
||||
private static final Pattern REGISTRAR_PATTERN = Pattern.compile("^[a-z\\d]{3,14}$");
|
||||
|
||||
// Durations are short so that registrars can test with quick transfer (etc.) turnaround.
|
||||
private static final Duration SHORT_ADD_GRACE_PERIOD = Duration.standardMinutes(60);
|
||||
private static final Duration SHORT_REDEMPTION_GRACE_PERIOD = Duration.standardMinutes(10);
|
||||
private static final Duration SHORT_PENDING_DELETE_LENGTH = Duration.standardMinutes(5);
|
||||
private static final Duration SHORT_ADD_GRACE_PERIOD = Duration.ofMinutes(60);
|
||||
private static final Duration SHORT_REDEMPTION_GRACE_PERIOD = Duration.ofMinutes(10);
|
||||
private static final Duration SHORT_PENDING_DELETE_LENGTH = Duration.ofMinutes(5);
|
||||
|
||||
private static final String DEFAULT_PREMIUM_LIST = "default_sandbox_list";
|
||||
|
||||
@@ -234,8 +232,8 @@ public final class OteAccountBuilder {
|
||||
}
|
||||
|
||||
/** Sets the client certificate to all the OT&E Registrars. */
|
||||
public OteAccountBuilder setCertificate(String asciiCert, DateTime now) {
|
||||
return transformRegistrars(builder -> builder.setClientCertificate(asciiCert, toInstant(now)));
|
||||
public OteAccountBuilder setCertificate(String asciiCert, Instant now) {
|
||||
return transformRegistrars(builder -> builder.setClientCertificate(asciiCert, now));
|
||||
}
|
||||
|
||||
/** Sets the IP allowlist to all the OT&E Registrars. */
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.model;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -33,7 +32,6 @@ import google.registry.model.transfer.TransferResponse;
|
||||
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import java.time.Instant;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Static utility functions for domain transfers. */
|
||||
public final class ResourceTransferUtils {
|
||||
@@ -73,20 +71,6 @@ public final class ResourceTransferUtils {
|
||||
domain.getDomainName(), actionResult, transferRequestTrid, processedDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a pending action notification response indicating the resolution of a transfer.
|
||||
*
|
||||
* @deprecated Use {@link #createPendingTransferNotificationResponse(Domain, Trid, boolean,
|
||||
* Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static PendingActionNotificationResponse createPendingTransferNotificationResponse(
|
||||
Domain domain, Trid transferRequestTrid, boolean actionResult, DateTime processedDate) {
|
||||
return createPendingTransferNotificationResponse(
|
||||
domain, transferRequestTrid, actionResult, toInstant(processedDate));
|
||||
}
|
||||
|
||||
/** If there is a transfer out, delete the server-approve entities and enqueue a poll message. */
|
||||
public static void handlePendingTransferOnDelete(
|
||||
Domain domain, Domain newDomain, Instant now, HistoryEntry historyEntry) {
|
||||
@@ -109,18 +93,6 @@ public final class ResourceTransferUtils {
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a transfer out, delete the server-approve entities and enqueue a poll message.
|
||||
*
|
||||
* @deprecated Use {@link #handlePendingTransferOnDelete(Domain, Domain, Instant, HistoryEntry)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static void handlePendingTransferOnDelete(
|
||||
Domain domain, Domain newDomain, DateTime now, HistoryEntry historyEntry) {
|
||||
handlePendingTransferOnDelete(domain, newDomain, toInstant(now), historyEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a domain into a builder with its pending transfer resolved.
|
||||
*
|
||||
@@ -165,18 +137,6 @@ public final class ResourceTransferUtils {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a pending transfer by awarding it to the gaining client.
|
||||
*
|
||||
* @deprecated Use {@link #approvePendingTransfer(Domain, TransferStatus, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static Domain approvePendingTransfer(
|
||||
Domain domain, TransferStatus transferStatus, DateTime now) {
|
||||
return approvePendingTransfer(domain, transferStatus, toInstant(now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a pending transfer by denying it.
|
||||
*
|
||||
@@ -193,16 +153,4 @@ public final class ResourceTransferUtils {
|
||||
.setLastEppUpdateRegistrarId(lastEppUpdateRegistrarId)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a pending transfer by denying it.
|
||||
*
|
||||
* @deprecated Use {@link #denyPendingTransfer(Domain, TransferStatus, Instant, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static Domain denyPendingTransfer(
|
||||
Domain domain, TransferStatus transferStatus, DateTime now, String lastEppUpdateRegistrarId) {
|
||||
return denyPendingTransfer(domain, transferStatus, toInstant(now), lastEppUpdateRegistrarId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,9 +83,9 @@ public class BillingRecurrence extends BillingBase {
|
||||
*
|
||||
* <p>Note that this is a recurrence of the event time, not the billing time. The billing time can
|
||||
* be calculated by adding the relevant grace period length to this date. The reason for this
|
||||
* requirement is that the event time recurs on a {@link org.joda.time.Period} schedule (same day
|
||||
* of year, which can be 365 or 366 days later) which is what {@link TimeOfYear} can model,
|
||||
* whereas the billing time is a fixed {@link org.joda.time.Duration} later.
|
||||
* requirement is that the event time recurs on a year-based schedule (same day of year, which can
|
||||
* be 365 or 366 days later) which is what {@link TimeOfYear} can model, whereas the billing time
|
||||
* is a fixed {@link java.time.Duration} later.
|
||||
*/
|
||||
@Embedded
|
||||
@AttributeOverrides(
|
||||
|
||||
@@ -17,8 +17,6 @@ package google.registry.model.common;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.toDateTime;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.UnsafeSerializable;
|
||||
@@ -35,7 +33,6 @@ import jakarta.persistence.Id;
|
||||
import jakarta.persistence.IdClass;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Shared entity for date cursors.
|
||||
@@ -103,7 +100,13 @@ public class Cursor extends UpdateAutoTimestampEntity {
|
||||
ICANN_UPLOAD_TX(true),
|
||||
|
||||
/** Cursor for tracking monthly uploads of ICANN activity reports. */
|
||||
ICANN_UPLOAD_ACTIVITY(true);
|
||||
ICANN_UPLOAD_ACTIVITY(true),
|
||||
|
||||
/** Cursor for tracking the reflection of domain changes in the remote cache. */
|
||||
REMOTE_CACHE_DOMAIN_SYNC(false),
|
||||
|
||||
/** Cursor for tracking the reflection of host changes in the remote cache. */
|
||||
REMOTE_CACHE_HOST_SYNC(false);
|
||||
|
||||
private final boolean scoped;
|
||||
|
||||
@@ -150,15 +153,6 @@ public class Cursor extends UpdateAutoTimestampEntity {
|
||||
return getUpdateTimestamp().getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getLastUpdateTime()}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public DateTime getLastUpdateDateTime() {
|
||||
return toDateTime(getUpdateTimestamp().getTimestamp());
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
@@ -183,30 +177,12 @@ public class Cursor extends UpdateAutoTimestampEntity {
|
||||
return create(cursorType, cursorTime, GLOBAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #createGlobal(CursorType, Instant)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static Cursor createGlobal(CursorType cursorType, DateTime cursorTime) {
|
||||
return createGlobal(cursorType, toInstant(cursorTime));
|
||||
}
|
||||
|
||||
/** Creates a new cursor instance with a given {@link Tld} scope. */
|
||||
public static Cursor createScoped(CursorType cursorType, Instant cursorTime, Tld scope) {
|
||||
checkNotNull(scope, "Cursor scope cannot be null");
|
||||
return create(cursorType, cursorTime, scope.getTldStr());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #createScoped(CursorType, Instant, Tld)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static Cursor createScoped(CursorType cursorType, DateTime cursorTime, Tld scope) {
|
||||
return createScoped(cursorType, toInstant(cursorTime), scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cursor instance with a given TLD scope, or global if the scope is {@link
|
||||
* #GLOBAL}.
|
||||
@@ -224,29 +200,11 @@ public class Cursor extends UpdateAutoTimestampEntity {
|
||||
/**
|
||||
* Returns the current time for a given cursor, or {@code START_INSTANT} if the cursor is null.
|
||||
*/
|
||||
public static Instant getCursorTimeOrStartOfTimeInstant(Optional<Cursor> cursor) {
|
||||
return cursor.map(Cursor::getCursorTimeInstant).orElse(START_INSTANT);
|
||||
public static Instant getCursorTimeOrStartOfTime(Optional<Cursor> cursor) {
|
||||
return cursor.map(Cursor::getCursorTime).orElse(START_INSTANT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getCursorTimeOrStartOfTimeInstant(Optional)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static DateTime getCursorTimeOrStartOfTime(Optional<Cursor> cursor) {
|
||||
return toDateTime(getCursorTimeOrStartOfTimeInstant(cursor));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getCursorTimeInstant()}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public DateTime getCursorTime() {
|
||||
return toDateTime(cursorTime);
|
||||
}
|
||||
|
||||
public Instant getCursorTimeInstant() {
|
||||
public Instant getCursorTime() {
|
||||
return cursorTime;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.model.common;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
|
||||
import google.registry.dns.DnsUtils.TargetType;
|
||||
import google.registry.dns.PublishDnsUpdatesAction;
|
||||
@@ -31,8 +31,8 @@ import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.Instant;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@Entity
|
||||
@Table(indexes = {@Index(columnList = "requestTime"), @Index(columnList = "lastProcessTime")})
|
||||
@@ -54,10 +54,10 @@ public class DnsRefreshRequest extends ImmutableObject {
|
||||
private String tld;
|
||||
|
||||
@Column(nullable = false)
|
||||
private DateTime requestTime;
|
||||
private Instant requestTime;
|
||||
|
||||
@Column(nullable = false)
|
||||
private DateTime lastProcessTime;
|
||||
private Instant lastProcessTime;
|
||||
|
||||
public TargetType getType() {
|
||||
return type;
|
||||
@@ -71,7 +71,7 @@ public class DnsRefreshRequest extends ImmutableObject {
|
||||
return tld;
|
||||
}
|
||||
|
||||
public DateTime getRequestTime() {
|
||||
public Instant getRequestTime() {
|
||||
return requestTime;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public class DnsRefreshRequest extends ImmutableObject {
|
||||
* there are concurrent reads that all attempt to read the rows with oldest {@link #requestTime},
|
||||
* or another read that comes too early after the previous read.
|
||||
*/
|
||||
public DateTime getLastProcessTime() {
|
||||
public Instant getLastProcessTime() {
|
||||
return lastProcessTime;
|
||||
}
|
||||
|
||||
@@ -102,8 +102,8 @@ public class DnsRefreshRequest extends ImmutableObject {
|
||||
TargetType type,
|
||||
String name,
|
||||
String tld,
|
||||
DateTime requestTime,
|
||||
DateTime lastProcessTime) {
|
||||
Instant requestTime,
|
||||
Instant lastProcessTime) {
|
||||
checkNotNull(type, "Target type cannot be null");
|
||||
checkNotNull(name, "Domain/host name cannot be null");
|
||||
checkNotNull(tld, "TLD cannot be null");
|
||||
@@ -119,11 +119,11 @@ public class DnsRefreshRequest extends ImmutableObject {
|
||||
this.lastProcessTime = lastProcessTime;
|
||||
}
|
||||
|
||||
public DnsRefreshRequest(TargetType type, String name, String tld, DateTime requestTime) {
|
||||
this(null, type, name, tld, requestTime, START_OF_TIME);
|
||||
public DnsRefreshRequest(TargetType type, String name, String tld, Instant requestTime) {
|
||||
this(null, type, name, tld, requestTime, START_INSTANT);
|
||||
}
|
||||
|
||||
public DnsRefreshRequest updateProcessTime(DateTime processTime) {
|
||||
public DnsRefreshRequest updateProcessTime(Instant processTime) {
|
||||
checkArgument(
|
||||
processTime.isAfter(getRequestTime()),
|
||||
"Process time %s must be later than request time %s",
|
||||
|
||||
@@ -20,7 +20,6 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
@@ -32,7 +31,6 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Ordering;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.EntityYamlUtils.TimedTransitionPropertyFeatureStatusDeserializer;
|
||||
@@ -49,7 +47,6 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@Entity
|
||||
public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
@@ -182,10 +179,6 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
return status;
|
||||
}
|
||||
|
||||
public FeatureStatus getStatus(DateTime time) {
|
||||
return getStatus(toInstant(time));
|
||||
}
|
||||
|
||||
public FeatureStatus getStatus(Instant time) {
|
||||
return status.getValueAtTime(time);
|
||||
}
|
||||
@@ -194,24 +187,14 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
* Returns whether the flag is active now, or else the flag's default value if it doesn't exist.
|
||||
*/
|
||||
public static boolean isActiveNow(FeatureName featureName) {
|
||||
tm().assertInTransaction();
|
||||
return isActiveAt(featureName, tm().getTxTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the flag is active at the given time, or else the flag's default value if it
|
||||
* doesn't exist.
|
||||
*/
|
||||
public static boolean isActiveAt(FeatureName featureName, DateTime dateTime) {
|
||||
return isActiveAt(featureName, toInstant(dateTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the flag is active at the given time, or else the flag's default value if it
|
||||
* doesn't exist.
|
||||
*/
|
||||
public static boolean isActiveAt(FeatureName featureName, Instant instant) {
|
||||
tm().assertInTransaction();
|
||||
return CACHE
|
||||
.get(featureName)
|
||||
.map(flag -> flag.getStatus(instant).equals(ACTIVE))
|
||||
@@ -244,16 +227,8 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setStatusMap(ImmutableSortedMap<DateTime, FeatureStatus> statusMap) {
|
||||
return setStatusMapInstant(
|
||||
statusMap.entrySet().stream()
|
||||
.collect(
|
||||
ImmutableSortedMap.toImmutableSortedMap(
|
||||
Ordering.natural(), e -> toInstant(e.getKey()), Map.Entry::getValue)));
|
||||
}
|
||||
|
||||
public Builder setStatusMapInstant(ImmutableSortedMap<Instant, FeatureStatus> statusMap) {
|
||||
getInstance().status = TimedTransitionProperty.fromValueMapInstant(statusMap);
|
||||
public Builder setStatusMap(ImmutableSortedMap<Instant, FeatureStatus> statusMap) {
|
||||
getInstance().status = TimedTransitionProperty.fromValueMap(statusMap);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static google.registry.util.DateTimeUtils.minusYears;
|
||||
import static google.registry.util.DateTimeUtils.plusYears;
|
||||
import static java.time.ZoneOffset.UTC;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ContiguousSet;
|
||||
@@ -32,7 +33,6 @@ import jakarta.persistence.Embeddable;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class TimeOfYear extends ImmutableObject implements UnsafeSerializable {
|
||||
* February 28. It is impossible to construct a {@link TimeOfYear} for February 29th.
|
||||
*/
|
||||
public static TimeOfYear fromInstant(Instant instant) {
|
||||
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
|
||||
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, UTC);
|
||||
int month = zdt.getMonthValue();
|
||||
int day = zdt.getDayOfMonth();
|
||||
if (month == 2 && day == 29) {
|
||||
@@ -88,8 +88,8 @@ public class TimeOfYear extends ImmutableObject implements UnsafeSerializable {
|
||||
Range<Instant> normalizedRange = range.intersection(Range.closed(START_INSTANT, END_INSTANT));
|
||||
Range<Integer> yearRange =
|
||||
Range.closed(
|
||||
ZonedDateTime.ofInstant(normalizedRange.lowerEndpoint(), ZoneOffset.UTC).getYear(),
|
||||
ZonedDateTime.ofInstant(normalizedRange.upperEndpoint(), ZoneOffset.UTC).getYear());
|
||||
ZonedDateTime.ofInstant(normalizedRange.lowerEndpoint(), UTC).getYear(),
|
||||
ZonedDateTime.ofInstant(normalizedRange.upperEndpoint(), UTC).getYear());
|
||||
return ContiguousSet.create(yearRange, integers()).stream()
|
||||
.map(this::toInstantWithYear)
|
||||
.filter(normalizedRange)
|
||||
@@ -107,20 +107,18 @@ public class TimeOfYear extends ImmutableObject implements UnsafeSerializable {
|
||||
int millis = Integer.parseInt(monthDayMillis.get(2));
|
||||
return LocalDate.of(year, month, day)
|
||||
.atTime(LocalTime.ofNanoOfDay(millis * 1000000L))
|
||||
.toInstant(ZoneOffset.UTC);
|
||||
.toInstant(UTC);
|
||||
}
|
||||
|
||||
/** Get the first {@link Instant} with this month/day/millis that is at or after the start. */
|
||||
public Instant getNextInstanceAtOrAfter(Instant start) {
|
||||
Instant withSameYear =
|
||||
toInstantWithYear(ZonedDateTime.ofInstant(start, ZoneOffset.UTC).getYear());
|
||||
Instant withSameYear = toInstantWithYear(ZonedDateTime.ofInstant(start, UTC).getYear());
|
||||
return isAtOrAfter(withSameYear, start) ? withSameYear : plusYears(withSameYear, 1);
|
||||
}
|
||||
|
||||
/** Get the first {@link Instant} with this month/day/millis that is at or before the end. */
|
||||
public Instant getLastInstanceBeforeOrAt(Instant end) {
|
||||
Instant withSameYear =
|
||||
toInstantWithYear(ZonedDateTime.ofInstant(end, ZoneOffset.UTC).getYear());
|
||||
Instant withSameYear = toInstantWithYear(ZonedDateTime.ofInstant(end, UTC).getYear());
|
||||
return isBeforeOrAt(withSameYear, end) ? withSameYear : minusYears(withSameYear, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@ import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
|
||||
import static google.registry.util.DateTimeUtils.START_INSTANT;
|
||||
import static google.registry.util.DateTimeUtils.formatInstant;
|
||||
import static google.registry.util.DateTimeUtils.latestOf;
|
||||
import static google.registry.util.DateTimeUtils.toDateTime;
|
||||
import static google.registry.util.DateTimeUtils.toInstant;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
@@ -34,7 +32,6 @@ import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An entity property whose value transitions over time. Each value it takes on becomes active at a
|
||||
@@ -70,6 +67,9 @@ public class TimedTransitionProperty<V extends Serializable> implements UnsafeSe
|
||||
checkArgument(
|
||||
backingMap.containsKey(START_INSTANT),
|
||||
"Must provide transition entry for the start of time (Unix Epoch)");
|
||||
checkArgument(
|
||||
Ordering.natural().equals(backingMap.comparator()),
|
||||
"Timed transition value map must have transition time keys in chronological order");
|
||||
this.backingMap = ImmutableSortedMap.copyOfSorted(backingMap);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ public class TimedTransitionProperty<V extends Serializable> implements UnsafeSe
|
||||
* START_INSTANT}.
|
||||
*/
|
||||
public static <V extends Serializable> TimedTransitionProperty<V> withInitialValue(V value) {
|
||||
return fromValueMapInstant(ImmutableSortedMap.of(START_INSTANT, value));
|
||||
return fromValueMap(ImmutableSortedMap.of(START_INSTANT, value));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,53 +91,12 @@ public class TimedTransitionProperty<V extends Serializable> implements UnsafeSe
|
||||
* in the given map.
|
||||
*
|
||||
* <p>The map must contain a value for {@code START_INSTANT}.
|
||||
*
|
||||
* @deprecated Use {@link #fromValueMapInstant(ImmutableSortedMap)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static <V extends Serializable> TimedTransitionProperty<V> fromValueMap(
|
||||
ImmutableSortedMap<DateTime, V> valueMap) {
|
||||
return fromValueMapInstant(toInstantMap(valueMap));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link TimedTransitionProperty} that contains the transition values and times defined
|
||||
* in the given map.
|
||||
*
|
||||
* <p>The map must contain a value for {@code START_INSTANT}.
|
||||
*/
|
||||
public static <V extends Serializable> TimedTransitionProperty<V> fromValueMapInstant(
|
||||
ImmutableSortedMap<Instant, V> valueMap) {
|
||||
return new TimedTransitionProperty<>(valueMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link TimedTransitionProperty} that contains the transition values and times defined
|
||||
* in the given map.
|
||||
*
|
||||
* <p>The map must contain a value for {@code START_OF_TIME}. The map is also validated against a
|
||||
* set of allowed transitions.
|
||||
*
|
||||
* @deprecated Use {@link #makeInstant(ImmutableSortedMap, ImmutableMultimap, String,
|
||||
* Serializable, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static <V extends Serializable> TimedTransitionProperty<V> make(
|
||||
ImmutableSortedMap<DateTime, V> valueMap,
|
||||
ImmutableMultimap<V, V> allowedTransitions,
|
||||
String mapName,
|
||||
V initialValue,
|
||||
String initialValueErrorMessage) {
|
||||
return makeInstant(
|
||||
toInstantMap(valueMap),
|
||||
allowedTransitions,
|
||||
mapName,
|
||||
initialValue,
|
||||
initialValueErrorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link TimedTransitionProperty} that contains the transition values and times defined
|
||||
* in the given map.
|
||||
@@ -145,44 +104,27 @@ public class TimedTransitionProperty<V extends Serializable> implements UnsafeSe
|
||||
* <p>The map must contain a value for {@code START_INSTANT}. The map is also validated against a
|
||||
* set of allowed transitions.
|
||||
*/
|
||||
public static <V extends Serializable> TimedTransitionProperty<V> makeInstant(
|
||||
public static <V extends Serializable> TimedTransitionProperty<V> make(
|
||||
ImmutableSortedMap<Instant, V> valueMap,
|
||||
ImmutableMultimap<V, V> allowedTransitions,
|
||||
String mapName,
|
||||
V initialValue,
|
||||
String initialValueErrorMessage) {
|
||||
validateTimedTransitionMapInstant(valueMap, allowedTransitions, mapName);
|
||||
validateTimedTransitionMap(valueMap, allowedTransitions, mapName);
|
||||
checkArgument(valueMap.firstEntry().getValue().equals(initialValue), initialValueErrorMessage);
|
||||
return fromValueMapInstant(valueMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a timed transition map.
|
||||
*
|
||||
* @deprecated Use {@link #validateTimedTransitionMapInstant(ImmutableSortedMap,
|
||||
* ImmutableMultimap, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static <V extends Serializable> void validateTimedTransitionMap(
|
||||
ImmutableSortedMap<DateTime, V> valueMap,
|
||||
ImmutableMultimap<V, V> allowedTransitions,
|
||||
String mapName) {
|
||||
validateTimedTransitionMapInstant(toInstantMap(valueMap), allowedTransitions, mapName);
|
||||
return fromValueMap(valueMap);
|
||||
}
|
||||
|
||||
/** Validates a timed transition map. */
|
||||
public static <V extends Serializable> void validateTimedTransitionMapInstant(
|
||||
public static <V extends Serializable> void validateTimedTransitionMap(
|
||||
ImmutableSortedMap<Instant, V> valueMap,
|
||||
ImmutableMultimap<V, V> allowedTransitions,
|
||||
String mapName) {
|
||||
checkArgument(
|
||||
Ordering.natural().equals(valueMap.comparator()),
|
||||
"Timed transition value map must have transition time keys in chronological order");
|
||||
checkArgument(!valueMap.isEmpty(), "%s map cannot be null or empty.", mapName);
|
||||
|
||||
checkArgument(
|
||||
valueMap.firstKey().equals(START_INSTANT), "%s map must start at START_OF_TIME.", mapName);
|
||||
valueMap.firstKey().equals(START_INSTANT), "%s map must start at START_INSTANT.", mapName);
|
||||
|
||||
V lastValue = null;
|
||||
for (V value : valueMap.values()) {
|
||||
@@ -199,17 +141,6 @@ public class TimedTransitionProperty<V extends Serializable> implements UnsafeSe
|
||||
}
|
||||
}
|
||||
|
||||
private static <V> ImmutableSortedMap<Instant, V> toInstantMap(
|
||||
ImmutableSortedMap<DateTime, V> valueMap) {
|
||||
checkArgument(
|
||||
Ordering.natural().equals(valueMap.comparator()),
|
||||
"Timed transition value map must have transition time keys in chronological order");
|
||||
return valueMap.entrySet().stream()
|
||||
.collect(
|
||||
toImmutableSortedMap(
|
||||
Ordering.natural(), e -> toInstant(e.getKey()), Map.Entry::getValue));
|
||||
}
|
||||
|
||||
/** Checks whether the property is valid. */
|
||||
public void checkValidity() {
|
||||
checkState(
|
||||
@@ -217,39 +148,16 @@ public class TimedTransitionProperty<V extends Serializable> implements UnsafeSe
|
||||
"Timed transition values missing required entry for the start of time (Unix Epoch)");
|
||||
}
|
||||
|
||||
/** Returns the value of the property that is active at the given time. */
|
||||
public V getValueAtTime(DateTime time) {
|
||||
return getValueAtTime(toInstant(time));
|
||||
}
|
||||
|
||||
/** Returns the value of the property that is active at the given time. */
|
||||
public V getValueAtTime(Instant time) {
|
||||
return backingMap.floorEntry(latestOf(START_INSTANT, time)).getValue();
|
||||
}
|
||||
|
||||
/** Returns the map of all the transitions that have been defined for this property. */
|
||||
public ImmutableSortedMap<DateTime, V> toValueMap() {
|
||||
return backingMap.entrySet().stream()
|
||||
.collect(
|
||||
toImmutableSortedMap(
|
||||
Ordering.natural(), e -> toDateTime(e.getKey()), Map.Entry::getValue));
|
||||
}
|
||||
|
||||
/** Returns the map of all the transitions that have been defined for this property. */
|
||||
public ImmutableSortedMap<Instant, V> toValueMapInstant() {
|
||||
public ImmutableSortedMap<Instant, V> toValueMap() {
|
||||
return backingMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time of the next transition after the given time. Returns null if there is no
|
||||
* subsequent transition.
|
||||
*/
|
||||
@Nullable
|
||||
public DateTime getNextTransitionAfter(DateTime time) {
|
||||
Instant nextTransition = getNextTransitionAfter(toInstant(time));
|
||||
return nextTransition == null ? null : toDateTime(nextTransition);
|
||||
}
|
||||
|
||||
/** Returns the time of the next transition. Returns null if there is no subsequent transition. */
|
||||
@Nullable
|
||||
public Instant getNextTransitionAfter(Instant time) {
|
||||
|
||||
@@ -30,8 +30,8 @@ import jakarta.persistence.Index;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@Entity
|
||||
@WithVKey(Long.class)
|
||||
@@ -47,7 +47,7 @@ public class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
|
||||
|
||||
@Column(nullable = false)
|
||||
@Expose
|
||||
DateTime modificationTime;
|
||||
Instant modificationTime;
|
||||
|
||||
/** The HTTP method (e.g. POST, PUT) used to make this modification. */
|
||||
@Column(nullable = false)
|
||||
@@ -76,7 +76,7 @@ public class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
|
||||
return revisionId;
|
||||
}
|
||||
|
||||
public DateTime getModificationTime() {
|
||||
public Instant getModificationTime() {
|
||||
return modificationTime;
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ public class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
|
||||
return super.build();
|
||||
}
|
||||
|
||||
public Builder setModificationTime(DateTime modificationTime) {
|
||||
public Builder setModificationTime(Instant modificationTime) {
|
||||
getInstance().modificationTime = modificationTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.model.console;
|
||||
|
||||
import static google.registry.util.DateTimeUtils.toDateTime;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import google.registry.model.Buildable;
|
||||
@@ -31,7 +30,6 @@ import jakarta.persistence.Id;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Represents a password reset request of some type.
|
||||
@@ -64,7 +62,7 @@ public class PasswordResetRequest extends ImmutableObject implements Buildable {
|
||||
@Column(nullable = false)
|
||||
String requester;
|
||||
|
||||
@Column DateTime fulfillmentTime;
|
||||
@Column Instant fulfillmentTime;
|
||||
|
||||
@Column(nullable = false)
|
||||
String destinationEmail;
|
||||
@@ -84,20 +82,11 @@ public class PasswordResetRequest extends ImmutableObject implements Buildable {
|
||||
return requestTime.getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getRequestTime()}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public DateTime getRequestDateTime() {
|
||||
return toDateTime(requestTime.getTimestamp());
|
||||
}
|
||||
|
||||
public String getRequester() {
|
||||
return requester;
|
||||
}
|
||||
|
||||
public Optional<DateTime> getFulfillmentTime() {
|
||||
public Optional<Instant> getFulfillmentTime() {
|
||||
return Optional.ofNullable(fulfillmentTime);
|
||||
}
|
||||
|
||||
@@ -153,7 +142,7 @@ public class PasswordResetRequest extends ImmutableObject implements Buildable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFulfillmentTime(DateTime fulfillmentTime) {
|
||||
public Builder setFulfillmentTime(Instant fulfillmentTime) {
|
||||
getInstance().fulfillmentTime = fulfillmentTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
elementFormDefault = XmlNsForm.QUALIFIED)
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlJavaTypeAdapters({
|
||||
@XmlJavaTypeAdapter(UtcInstantAdapter.class),
|
||||
@XmlJavaTypeAdapter(UtcInstantAdapter.class)
|
||||
})
|
||||
package google.registry.model.contact;
|
||||
|
||||
@@ -80,7 +80,6 @@ import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.Transient;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.HashSet;
|
||||
@@ -495,8 +494,7 @@ public class DomainBase extends EppResource {
|
||||
GracePeriodStatus.TRANSFER,
|
||||
domain.getRepoId(),
|
||||
transferExpirationTime.plus(
|
||||
Duration.ofMillis(
|
||||
Tld.get(domain.getTld()).getTransferGracePeriodLength().getMillis())),
|
||||
Tld.get(domain.getTld()).getTransferGracePeriodLength()),
|
||||
transferData.getGainingRegistrarId(),
|
||||
transferData.getServerApproveBillingEvent())));
|
||||
} else {
|
||||
@@ -533,8 +531,7 @@ public class DomainBase extends EppResource {
|
||||
GracePeriod.createForRecurrence(
|
||||
GracePeriodStatus.AUTO_RENEW,
|
||||
domain.getRepoId(),
|
||||
lastAutorenewTime.plusMillis(
|
||||
Tld.get(domain.getTld()).getAutoRenewGracePeriodLength().getMillis()),
|
||||
lastAutorenewTime.plus(Tld.get(domain.getTld()).getAutoRenewGracePeriodLength()),
|
||||
domain.getCurrentSponsorRegistrarId(),
|
||||
domain.getAutorenewBillingEvent()));
|
||||
newLastEppUpdateTime = Optional.of(lastAutorenewTime);
|
||||
@@ -782,7 +779,7 @@ public class DomainBase extends EppResource {
|
||||
/**
|
||||
* Sets the autorenew end time, or clears it if empty is passed.
|
||||
*
|
||||
* <p>Note that {@link DateTimeUtils#END_OF_TIME} is used as a sentinel value in the database
|
||||
* <p>Note that {@link DateTimeUtils#END_INSTANT} is used as a sentinel value in the database
|
||||
* representation to signify that autorenew doesn't end, and is mapped to empty here for the
|
||||
* purposes of more legible business logic.
|
||||
*/
|
||||
|
||||
@@ -46,10 +46,10 @@ import jakarta.xml.bind.annotation.XmlTransient;
|
||||
import jakarta.xml.bind.annotation.XmlType;
|
||||
import jakarta.xml.bind.annotation.XmlValue;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/** A collection of {@link Domain} commands. */
|
||||
public class DomainCommand {
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.UpdateAutoTimestampEntity;
|
||||
import google.registry.persistence.converter.DurationUserType;
|
||||
import jakarta.persistence.Access;
|
||||
import jakarta.persistence.AccessType;
|
||||
import jakarta.persistence.AttributeOverride;
|
||||
@@ -37,10 +38,11 @@ import jakarta.persistence.Index;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.Duration;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
/**
|
||||
* Represents a registry lock/unlock object, meaning that the domain is locked on the registry
|
||||
@@ -156,6 +158,8 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
|
||||
|
||||
/** The duration after which we will re-lock this domain after it is unlocked. */
|
||||
@Column(columnDefinition = "interval")
|
||||
@Type(DurationUserType.class)
|
||||
@Expose
|
||||
private Duration relockDuration;
|
||||
|
||||
public String getRepoId() {
|
||||
|
||||
@@ -18,9 +18,8 @@
|
||||
elementFormDefault = XmlNsForm.QUALIFIED)
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlJavaTypeAdapters({
|
||||
@XmlJavaTypeAdapter(UtcInstantAdapter.class),
|
||||
@XmlJavaTypeAdapter(UtcInstantAdapter.class),
|
||||
@XmlJavaTypeAdapter(DateAdapter.class)
|
||||
@XmlJavaTypeAdapter(value = UtcInstantAdapter.class, type = java.time.Instant.class),
|
||||
@XmlJavaTypeAdapter(value = DateAdapter.class, type = java.time.LocalDate.class)
|
||||
})
|
||||
package google.registry.model.domain;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user