diff --git a/core/src/main/java/google/registry/rdap/RdapActionBase.java b/core/src/main/java/google/registry/rdap/RdapActionBase.java index f9335092cc4..5d973573d2e 100644 --- a/core/src/main/java/google/registry/rdap/RdapActionBase.java +++ b/core/src/main/java/google/registry/rdap/RdapActionBase.java @@ -51,6 +51,7 @@ import jakarta.inject.Inject; import java.net.URI; import java.net.URISyntaxException; +import java.time.Duration; import java.time.Instant; import java.util.Optional; @@ -131,6 +132,7 @@ abstract ReplyPayloadBase getJsonObjectForResource( @Override public void run() { + Instant startTime = clock.now(); metricInformationBuilder.setIncludeDeleted(includeDeletedParam.orElse(false)); metricInformationBuilder.setRole(rdapAuthorization.role()); metricInformationBuilder.setRequestMethod(requestMethod); @@ -179,6 +181,9 @@ public void run() { setError(SC_INTERNAL_SERVER_ERROR, "Internal Server Error", "An error was encountered"); logger.atSevere().withCause(e).log("Exception encountered while processing RDAP command."); } + long processingTime = Duration.between(startTime, clock.now()).toMillis(); + metricInformationBuilder.setProcessingTime(processingTime); + rdapMetrics.updateMetrics(metricInformationBuilder.build()); } diff --git a/core/src/main/java/google/registry/rdap/RdapMetrics.java b/core/src/main/java/google/registry/rdap/RdapMetrics.java index b39fd5fab7a..b845df842b4 100644 --- a/core/src/main/java/google/registry/rdap/RdapMetrics.java +++ b/core/src/main/java/google/registry/rdap/RdapMetrics.java @@ -14,6 +14,8 @@ package google.registry.rdap; +import static com.google.monitoring.metrics.EventMetric.DEFAULT_FITTER; + import com.google.auto.value.AutoBuilder; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; @@ -133,6 +135,16 @@ enum WildcardType { LABEL_DESCRIPTORS_FOR_RETRIEVAL_COUNTS, FIBONACCI_FITTER); + @VisibleForTesting + static final EventMetric requestTime = + MetricRegistryImpl.getDefault() + .newEventMetric( + "/rdap/request_time", + "RDAP Request Time", + "milliseconds", + LABEL_DESCRIPTORS_FOR_RESPONSES, + DEFAULT_FITTER); + @Inject public RdapMetrics() {} @@ -181,6 +193,15 @@ public void updateMetrics(RdapMetricInformation rdapMetricInformation) { getLabelStringForPrefixLength(rdapMetricInformation.prefixLength()), rdapMetricInformation.includeDeleted() ? "YES" : "NO"); } + if (rdapMetricInformation.processingTime().isPresent()) { + requestTime.record( + rdapMetricInformation.processingTime().get(), + rdapMetricInformation.endpointType().toString(), + rdapMetricInformation.searchType().toString(), + rdapMetricInformation.wildcardType().toString(), + String.valueOf(rdapMetricInformation.statusCode()), + rdapMetricInformation.incompletenessWarningType().toString()); + } } /** @@ -202,6 +223,8 @@ public void updateMetrics(RdapMetricInformation rdapMetricInformation) { * than were actually returned in the response; absent if a search was not performed. * @param numHostsRetrieved Number of hosts retrieved from the database; this might be more than * were actually returned in the response; absent if a search was not performed. + * @param processingTime The processing time for the request in milliseconds; absent if not + * recorded. */ public record RdapMetricInformation( EndpointType endpointType, @@ -215,7 +238,8 @@ public record RdapMetricInformation( int statusCode, IncompletenessWarningType incompletenessWarningType, Optional numDomainsRetrieved, - Optional numHostsRetrieved) { + Optional numHostsRetrieved, + Optional processingTime) { @AutoBuilder interface Builder { @@ -243,9 +267,12 @@ interface Builder { Builder setNumHostsRetrieved(long numHostsRetrieved); + Builder setProcessingTime(long processingTime); + RdapMetricInformation build(); } + static Builder builder() { return new AutoBuilder_RdapMetrics_RdapMetricInformation_Builder() .setSearchType(SearchType.NONE) diff --git a/core/src/test/java/google/registry/rdap/RdapActionBaseTest.java b/core/src/test/java/google/registry/rdap/RdapActionBaseTest.java index 22deba98595..61471e7a4ed 100644 --- a/core/src/test/java/google/registry/rdap/RdapActionBaseTest.java +++ b/core/src/test/java/google/registry/rdap/RdapActionBaseTest.java @@ -21,6 +21,7 @@ import static google.registry.request.Action.Method.GET; import static google.registry.request.Action.Method.HEAD; import static google.registry.testing.DatabaseHelper.createTld; +import static org.joda.time.Duration.millis; import static org.mockito.Mockito.verify; import google.registry.rdap.RdapMetrics.EndpointType; @@ -31,6 +32,7 @@ import google.registry.rdap.RdapSearchResults.IncompletenessWarningType; import google.registry.request.Action; import google.registry.request.auth.Auth; +import google.registry.testing.FakeClock; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -63,6 +65,9 @@ public ReplyPayloadBase getJsonObjectForResource( if (pathSearchString.equals("RuntimeException")) { throw new RuntimeException(); } + if (pathSearchString.equals("advanceClock")) { + ((FakeClock) clock).advanceBy(millis(50)); + } return new ReplyPayloadBase(BoilerplateType.OTHER) { @JsonableElement public String key = "value"; }; @@ -130,6 +135,7 @@ void testMetrics_onSuccess() { .setRequestMethod(Action.Method.GET) .setStatusCode(200) .setIncompletenessWarningType(IncompletenessWarningType.COMPLETE) + .setProcessingTime(0L) .build()); } @@ -149,6 +155,7 @@ void testMetrics_onError() { .setRequestMethod(Action.Method.GET) .setStatusCode(400) .setIncompletenessWarningType(IncompletenessWarningType.COMPLETE) + .setProcessingTime(0L) .build()); } @@ -172,4 +179,24 @@ void testFormatted() { assertThat(payload).contains("\n"); assertThat(parseJsonObject(payload)).isEqualTo(loadJsonFile("rdapjson_toplevel.json")); } + + @Test + void testMetrics_recordsProcessingTime() { + generateActualJson("advanceClock"); + verify(rdapMetrics) + .updateMetrics( + RdapMetrics.RdapMetricInformation.builder() + .setEndpointType(EndpointType.HELP) + .setSearchType(SearchType.NONE) + .setWildcardType(WildcardType.INVALID) + .setPrefixLength(0) + .setIncludeDeleted(false) + .setRegistrarSpecified(false) + .setRole(RdapAuthorization.Role.PUBLIC) + .setRequestMethod(Action.Method.GET) + .setStatusCode(200) + .setIncompletenessWarningType(IncompletenessWarningType.COMPLETE) + .setProcessingTime(50L) + .build()); + } } diff --git a/core/src/test/java/google/registry/rdap/RdapDomainActionTest.java b/core/src/test/java/google/registry/rdap/RdapDomainActionTest.java index a866d29a221..95c2cfedb7d 100644 --- a/core/src/test/java/google/registry/rdap/RdapDomainActionTest.java +++ b/core/src/test/java/google/registry/rdap/RdapDomainActionTest.java @@ -435,6 +435,7 @@ void testMetrics() { .setRequestMethod(Action.Method.GET) .setStatusCode(200) .setIncompletenessWarningType(IncompletenessWarningType.COMPLETE) + .setProcessingTime(0L) .build()); } diff --git a/core/src/test/java/google/registry/rdap/RdapEntityActionTest.java b/core/src/test/java/google/registry/rdap/RdapEntityActionTest.java index 105ff24015a..01e58beeae7 100644 --- a/core/src/test/java/google/registry/rdap/RdapEntityActionTest.java +++ b/core/src/test/java/google/registry/rdap/RdapEntityActionTest.java @@ -235,6 +235,7 @@ void testMetrics() { .setRequestMethod(Action.Method.GET) .setStatusCode(200) .setIncompletenessWarningType(IncompletenessWarningType.COMPLETE) + .setProcessingTime(0L) .build()); } } diff --git a/core/src/test/java/google/registry/rdap/RdapHelpActionTest.java b/core/src/test/java/google/registry/rdap/RdapHelpActionTest.java index 916d29c62c8..c20b04b4188 100644 --- a/core/src/test/java/google/registry/rdap/RdapHelpActionTest.java +++ b/core/src/test/java/google/registry/rdap/RdapHelpActionTest.java @@ -82,6 +82,7 @@ void testHelpActionMetrics() { .setRequestMethod(Action.Method.GET) .setStatusCode(200) .setIncompletenessWarningType(IncompletenessWarningType.COMPLETE) + .setProcessingTime(0L) .build()); } } diff --git a/core/src/test/java/google/registry/rdap/RdapMetricsTest.java b/core/src/test/java/google/registry/rdap/RdapMetricsTest.java index 4f4a6639a5e..92d2a61e0c8 100644 --- a/core/src/test/java/google/registry/rdap/RdapMetricsTest.java +++ b/core/src/test/java/google/registry/rdap/RdapMetricsTest.java @@ -37,6 +37,7 @@ void beforeEach() { RdapMetrics.responses.reset(); RdapMetrics.numberOfDomainsRetrieved.reset(); RdapMetrics.numberOfHostsRetrieved.reset(); + RdapMetrics.requestTime.reset(); } private RdapMetrics.RdapMetricInformation.Builder getBuilder() { @@ -274,4 +275,13 @@ void testEntitySearchByNameWithWildcard() { assertThat(RdapMetrics.numberOfDomainsRetrieved).hasNoOtherValues(); assertThat(RdapMetrics.numberOfHostsRetrieved).hasNoOtherValues(); } + + @Test + void testRecordProcessingTime() { + rdapMetrics.updateMetrics(getBuilder().setProcessingTime(100L).build()); + assertThat(RdapMetrics.requestTime) + .hasDataSetForLabels(ImmutableSet.of(100L), "DOMAINS", "NONE", "INVALID", "200", "COMPLETE") + .and() + .hasNoOtherValues(); + } } diff --git a/core/src/test/java/google/registry/rdap/RdapNameserverActionTest.java b/core/src/test/java/google/registry/rdap/RdapNameserverActionTest.java index 513896f5a39..a3778ad0e15 100644 --- a/core/src/test/java/google/registry/rdap/RdapNameserverActionTest.java +++ b/core/src/test/java/google/registry/rdap/RdapNameserverActionTest.java @@ -287,6 +287,7 @@ void testMetrics() { .setRequestMethod(Action.Method.GET) .setStatusCode(200) .setIncompletenessWarningType(IncompletenessWarningType.COMPLETE) + .setProcessingTime(0L) .build()); } } diff --git a/core/src/test/java/google/registry/rdap/RdapSearchActionTestCase.java b/core/src/test/java/google/registry/rdap/RdapSearchActionTestCase.java index 4301bd16a87..c626819a93d 100644 --- a/core/src/test/java/google/registry/rdap/RdapSearchActionTestCase.java +++ b/core/src/test/java/google/registry/rdap/RdapSearchActionTestCase.java @@ -90,7 +90,8 @@ void verifyMetrics( .setRole(metricRole) .setRequestMethod(requestMethod) .setStatusCode(metricStatusCode) - .setIncompletenessWarningType(incompletenessWarningType); + .setIncompletenessWarningType(incompletenessWarningType) + .setProcessingTime(0L); numDomainsRetrieved.ifPresent(builder::setNumDomainsRetrieved); numHostsRetrieved.ifPresent(builder::setNumHostsRetrieved); verify(rdapMetrics).updateMetrics(builder.build());