diff --git a/java/google/registry/rdap/RdapActionBase.java b/java/google/registry/rdap/RdapActionBase.java index e2d28a23c..f1961ad22 100644 --- a/java/google/registry/rdap/RdapActionBase.java +++ b/java/google/registry/rdap/RdapActionBase.java @@ -33,6 +33,7 @@ import com.googlecode.objectify.cmd.Query; import google.registry.config.RegistryConfig.Config; import google.registry.model.EppResource; import google.registry.model.registrar.Registrar; +import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.rdap.RdapSearchResults.IncompletenessWarningType; import google.registry.request.Action; import google.registry.request.FullServletPath; @@ -92,6 +93,9 @@ public abstract class RdapActionBase implements Runnable { /** Returns a string like "domain name" or "nameserver", used for error strings. */ abstract String getHumanReadableObjectTypeName(); + /** Returns the endpoint type used for recording metrics. */ + abstract EndpointType getEndpointType(); + /** Returns the servlet action path; used to extract the search string from the incoming path. */ abstract String getActionPath(); @@ -381,23 +385,24 @@ public abstract class RdapActionBase implements Runnable { * the query could not check deletion status (due to Datastore limitations such as the * limit of one field queried for inequality, for instance), it may need to be set to true * even when not including deleted records - * @return an {@link RdapResourcesAndIncompletenessWarningType} object containing the list of + * @return an {@link RdapResultSet} object containing the list of * resources and an incompleteness warning flag, which is set to MIGHT_BE_INCOMPLETE iff * any resources were excluded due to lack of visibility, and the resulting list of * resources is less than the maximum allowable, which indicates that we may not have * fetched enough resources */ - RdapResourcesAndIncompletenessWarningType getMatchingResources( + RdapResultSet getMatchingResources( Query query, boolean checkForVisibility, DateTime now) { Optional desiredRegistrar = getDesiredRegistrar(); if (desiredRegistrar.isPresent()) { query = query.filter("currentSponsorClientId", desiredRegistrar.get()); } if (!checkForVisibility) { - return RdapResourcesAndIncompletenessWarningType.create(query.list()); + return RdapResultSet.create(query.list()); } // If we are including deleted resources, we need to check that we're authorized for each one. List resources = new ArrayList<>(); + int numResourcesQueried = 0; boolean someExcluded = false; for (T resource : query) { if (shouldBeVisible(resource, now)) { @@ -405,14 +410,16 @@ public abstract class RdapActionBase implements Runnable { } else { someExcluded = true; } + numResourcesQueried++; if (resources.size() > rdapResultSetMaxSize) { break; } } - return RdapResourcesAndIncompletenessWarningType.create( + return RdapResultSet.create( resources, (someExcluded && (resources.size() < rdapResultSetMaxSize + 1)) ? IncompletenessWarningType.MIGHT_BE_INCOMPLETE - : IncompletenessWarningType.NONE); + : IncompletenessWarningType.NONE, + numResourcesQueried); } } diff --git a/java/google/registry/rdap/RdapAutnumAction.java b/java/google/registry/rdap/RdapAutnumAction.java index 48a163220..3eba76fab 100644 --- a/java/google/registry/rdap/RdapAutnumAction.java +++ b/java/google/registry/rdap/RdapAutnumAction.java @@ -18,6 +18,7 @@ import static google.registry.request.Action.Method.GET; import static google.registry.request.Action.Method.HEAD; import com.google.common.collect.ImmutableMap; +import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.request.Action; import google.registry.request.HttpException.NotImplementedException; import google.registry.request.auth.Auth; @@ -46,6 +47,11 @@ public class RdapAutnumAction extends RdapActionBase { return "autnum"; } + @Override + public EndpointType getEndpointType() { + return EndpointType.AUTNUM; + } + @Override public String getActionPath() { return PATH; diff --git a/java/google/registry/rdap/RdapDomainAction.java b/java/google/registry/rdap/RdapDomainAction.java index ce170d9e0..3b55655ce 100644 --- a/java/google/registry/rdap/RdapDomainAction.java +++ b/java/google/registry/rdap/RdapDomainAction.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableMap; import google.registry.flows.EppException; import google.registry.model.domain.DomainResource; import google.registry.rdap.RdapJsonFormatter.OutputDataType; +import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.NotFoundException; @@ -56,6 +57,11 @@ public class RdapDomainAction extends RdapActionBase { return PATH; } + @Override + public EndpointType getEndpointType() { + return EndpointType.DOMAIN; + } + @Override public ImmutableMap getJsonObjectForResource( String pathSearchString, boolean isHeadRequest) { diff --git a/java/google/registry/rdap/RdapDomainSearchAction.java b/java/google/registry/rdap/RdapDomainSearchAction.java index cdacc2f16..e698c2421 100644 --- a/java/google/registry/rdap/RdapDomainSearchAction.java +++ b/java/google/registry/rdap/RdapDomainSearchAction.java @@ -34,6 +34,7 @@ import google.registry.model.domain.DomainResource; import google.registry.model.host.HostResource; import google.registry.rdap.RdapJsonFormatter.BoilerplateType; import google.registry.rdap.RdapJsonFormatter.OutputDataType; +import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.rdap.RdapSearchResults.IncompletenessWarningType; import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; @@ -88,6 +89,11 @@ public class RdapDomainSearchAction extends RdapActionBase { return "domain search"; } + @Override + public EndpointType getEndpointType() { + return EndpointType.DOMAINS; + } + @Override public String getActionPath() { return PATH; @@ -468,15 +474,10 @@ public class RdapDomainSearchAction extends RdapActionBase { return makeSearchResults(domains, IncompletenessWarningType.NONE, now); } - /** Output JSON from data in an {@link RdapResourcesAndIncompletenessWarningType} object. */ + /** Output JSON from data in an {@link RdapResultSet} object. */ private RdapSearchResults makeSearchResults( - RdapResourcesAndIncompletenessWarningType - resourcesAndIncompletenessWarningType, - DateTime now) { - return makeSearchResults( - resourcesAndIncompletenessWarningType.resources(), - resourcesAndIncompletenessWarningType.incompletenessWarningType(), - now); + RdapResultSet resultSet, DateTime now) { + return makeSearchResults(resultSet.resources(), resultSet.incompletenessWarningType(), now); } /** diff --git a/java/google/registry/rdap/RdapEntityAction.java b/java/google/registry/rdap/RdapEntityAction.java index 7d52c3044..cd742153d 100644 --- a/java/google/registry/rdap/RdapEntityAction.java +++ b/java/google/registry/rdap/RdapEntityAction.java @@ -27,6 +27,7 @@ import google.registry.model.contact.ContactResource; import google.registry.model.domain.DesignatedContact; import google.registry.model.registrar.Registrar; import google.registry.rdap.RdapJsonFormatter.OutputDataType; +import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.NotFoundException; @@ -66,6 +67,11 @@ public class RdapEntityAction extends RdapActionBase { return "entity"; } + @Override + public EndpointType getEndpointType() { + return EndpointType.ENTITY; + } + @Override public String getActionPath() { return PATH; diff --git a/java/google/registry/rdap/RdapEntitySearchAction.java b/java/google/registry/rdap/RdapEntitySearchAction.java index 780e27336..99b816ee9 100644 --- a/java/google/registry/rdap/RdapEntitySearchAction.java +++ b/java/google/registry/rdap/RdapEntitySearchAction.java @@ -31,6 +31,7 @@ import google.registry.model.domain.DesignatedContact; import google.registry.model.registrar.Registrar; import google.registry.rdap.RdapJsonFormatter.BoilerplateType; import google.registry.rdap.RdapJsonFormatter.OutputDataType; +import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.rdap.RdapSearchResults.IncompletenessWarningType; import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; @@ -76,6 +77,11 @@ public class RdapEntitySearchAction extends RdapActionBase { return "entity search"; } + @Override + public EndpointType getEndpointType() { + return EndpointType.ENTITIES; + } + @Override public String getActionPath() { return PATH; @@ -169,12 +175,10 @@ public class RdapEntitySearchAction extends RdapActionBase { // Get the contact matches and return the results, fetching an additional contact to detect // truncation. Don't bother searching for contacts by name if the request would not be able to // see any names anyway. - RdapResourcesAndIncompletenessWarningType - resourcesAndIncompletenessWarningType; + RdapResultSet resultSet; RdapAuthorization authorization = getAuthorization(); if (authorization.role() == RdapAuthorization.Role.PUBLIC) { - resourcesAndIncompletenessWarningType = - RdapResourcesAndIncompletenessWarningType.create(ImmutableList.of()); + resultSet = RdapResultSet.create(ImmutableList.of()); } else { Query query = queryItems( @@ -186,9 +190,9 @@ public class RdapEntitySearchAction extends RdapActionBase { if (authorization.role() != RdapAuthorization.Role.ADMINISTRATOR) { query = query.filter("currentSponsorClientId in", authorization.clientIds()); } - resourcesAndIncompletenessWarningType = getMatchingResources(query, false, now); + resultSet = getMatchingResources(query, false, now); } - return makeSearchResults(resourcesAndIncompletenessWarningType, registrars, now); + return makeSearchResults(resultSet, registrars, now); } /** @@ -264,15 +268,9 @@ public class RdapEntitySearchAction extends RdapActionBase { * arguments. */ private RdapSearchResults makeSearchResults( - RdapResourcesAndIncompletenessWarningType - resourcesAndIncompletenessWarningType, - List registrars, - DateTime now) { + RdapResultSet resultSet, List registrars, DateTime now) { return makeSearchResults( - resourcesAndIncompletenessWarningType.resources(), - resourcesAndIncompletenessWarningType.incompletenessWarningType(), - registrars, - now); + resultSet.resources(), resultSet.incompletenessWarningType(), registrars, now); } /** diff --git a/java/google/registry/rdap/RdapHelpAction.java b/java/google/registry/rdap/RdapHelpAction.java index 939fa4d08..323521380 100644 --- a/java/google/registry/rdap/RdapHelpAction.java +++ b/java/google/registry/rdap/RdapHelpAction.java @@ -20,6 +20,7 @@ import static google.registry.request.Action.Method.HEAD; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import google.registry.rdap.RdapJsonFormatter.BoilerplateType; +import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.request.Action; import google.registry.request.auth.Auth; import google.registry.util.Clock; @@ -44,6 +45,11 @@ public class RdapHelpAction extends RdapActionBase { return "help"; } + @Override + public EndpointType getEndpointType() { + return EndpointType.HELP; + } + @Override public String getActionPath() { return PATH; diff --git a/java/google/registry/rdap/RdapIpAction.java b/java/google/registry/rdap/RdapIpAction.java index 8bfc71845..27ce4a6c8 100644 --- a/java/google/registry/rdap/RdapIpAction.java +++ b/java/google/registry/rdap/RdapIpAction.java @@ -18,6 +18,7 @@ import static google.registry.request.Action.Method.GET; import static google.registry.request.Action.Method.HEAD; import com.google.common.collect.ImmutableMap; +import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.request.Action; import google.registry.request.HttpException.NotImplementedException; import google.registry.request.auth.Auth; @@ -46,6 +47,11 @@ public class RdapIpAction extends RdapActionBase { return "ip"; } + @Override + public EndpointType getEndpointType() { + return EndpointType.IP; + } + @Override public String getActionPath() { return PATH; diff --git a/java/google/registry/rdap/RdapMetrics.java b/java/google/registry/rdap/RdapMetrics.java new file mode 100644 index 000000000..7fa9e55a4 --- /dev/null +++ b/java/google/registry/rdap/RdapMetrics.java @@ -0,0 +1,31 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.rdap; + +/** RDAP Instrumentation. */ +public class RdapMetrics { + + enum EndpointType { + AUTNUM, + DOMAIN, + DOMAINS, + ENTITY, + ENTITIES, + HELP, + IP, + NAMESERVER, + NAMESERVERS + } +} diff --git a/java/google/registry/rdap/RdapNameserverAction.java b/java/google/registry/rdap/RdapNameserverAction.java index 3559843a2..0d0e0f4a3 100644 --- a/java/google/registry/rdap/RdapNameserverAction.java +++ b/java/google/registry/rdap/RdapNameserverAction.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableMap; import google.registry.flows.EppException; import google.registry.model.host.HostResource; import google.registry.rdap.RdapJsonFormatter.OutputDataType; +import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.NotFoundException; @@ -51,6 +52,11 @@ public class RdapNameserverAction extends RdapActionBase { return "nameserver"; } + @Override + public EndpointType getEndpointType() { + return EndpointType.NAMESERVER; + } + @Override public String getActionPath() { return PATH; diff --git a/java/google/registry/rdap/RdapNameserverSearchAction.java b/java/google/registry/rdap/RdapNameserverSearchAction.java index a1a9cc88b..42754ab00 100644 --- a/java/google/registry/rdap/RdapNameserverSearchAction.java +++ b/java/google/registry/rdap/RdapNameserverSearchAction.java @@ -29,6 +29,7 @@ import google.registry.model.domain.DomainResource; import google.registry.model.host.HostResource; import google.registry.rdap.RdapJsonFormatter.BoilerplateType; import google.registry.rdap.RdapJsonFormatter.OutputDataType; +import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.rdap.RdapSearchResults.IncompletenessWarningType; import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; @@ -75,6 +76,11 @@ public class RdapNameserverSearchAction extends RdapActionBase { return "nameserver search"; } + @Override + public EndpointType getEndpointType() { + return EndpointType.NAMESERVERS; + } + @Override public String getActionPath() { return PATH; @@ -244,17 +250,9 @@ public class RdapNameserverSearchAction extends RdapActionBase { return makeSearchResults(getMatchingResources(query, shouldIncludeDeleted(), now), now); } - /** - * Output JSON for a lists of hosts contained in an {@link - * RdapResourcesAndIncompletenessWarningType}. - */ - private RdapSearchResults makeSearchResults( - RdapResourcesAndIncompletenessWarningType resourcesAndIncompletenessWarningType, - DateTime now) { - return makeSearchResults( - resourcesAndIncompletenessWarningType.resources(), - resourcesAndIncompletenessWarningType.incompletenessWarningType(), - now); + /** Output JSON for a lists of hosts contained in an {@link RdapResultSet}. */ + private RdapSearchResults makeSearchResults(RdapResultSet resultSet, DateTime now) { + return makeSearchResults(resultSet.resources(), resultSet.incompletenessWarningType(), now); } /** Output JSON for a list of hosts. */ diff --git a/java/google/registry/rdap/RdapResourcesAndIncompletenessWarningType.java b/java/google/registry/rdap/RdapResultSet.java similarity index 62% rename from java/google/registry/rdap/RdapResourcesAndIncompletenessWarningType.java rename to java/google/registry/rdap/RdapResultSet.java index 17d6d9f16..d56bb898c 100644 --- a/java/google/registry/rdap/RdapResourcesAndIncompletenessWarningType.java +++ b/java/google/registry/rdap/RdapResultSet.java @@ -20,17 +20,18 @@ import google.registry.rdap.RdapSearchResults.IncompletenessWarningType; import java.util.List; @AutoValue -abstract class RdapResourcesAndIncompletenessWarningType { +abstract class RdapResultSet { - static RdapResourcesAndIncompletenessWarningType create( - List resources) { - return create(resources, IncompletenessWarningType.NONE); + static RdapResultSet create(List resources) { + return create(resources, IncompletenessWarningType.NONE, resources.size()); } - static RdapResourcesAndIncompletenessWarningType create( - List resources, IncompletenessWarningType incompletenessWarningType) { - return new AutoValue_RdapResourcesAndIncompletenessWarningType<>( - resources, incompletenessWarningType); + static RdapResultSet create( + List resources, + IncompletenessWarningType incompletenessWarningType, + int numResourcesRetrieved) { + return new AutoValue_RdapResultSet<>( + resources, incompletenessWarningType, numResourcesRetrieved); } /** List of EPP resources. */ @@ -38,5 +39,8 @@ abstract class RdapResourcesAndIncompletenessWarningType /** Type of warning to display regarding possible incomplete data. */ abstract IncompletenessWarningType incompletenessWarningType(); + + /** Number of resources retrieved from the database in the process of assembling the data set. */ + abstract int numResourcesRetrieved(); } diff --git a/javatests/google/registry/rdap/RdapActionBaseTest.java b/javatests/google/registry/rdap/RdapActionBaseTest.java index 7c6257a2d..bbac23c6d 100644 --- a/javatests/google/registry/rdap/RdapActionBaseTest.java +++ b/javatests/google/registry/rdap/RdapActionBaseTest.java @@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import google.registry.model.ofy.Ofy; import google.registry.rdap.RdapJsonFormatter.BoilerplateType; +import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.testing.AppEngineRule; import google.registry.testing.FakeClock; import google.registry.testing.FakeResponse; @@ -64,6 +65,11 @@ public class RdapActionBaseTest { return "human-readable string"; } + @Override + public EndpointType getEndpointType() { + return EndpointType.HELP; + } + @Override public String getActionPath() { return PATH;