mirror of
https://github.com/google/nomulus.git
synced 2025-05-14 16:37:13 +02:00
Define Stackdriver metrics for premium and reserved lists
This CL defines metrics for both premium and reserved lists, but actually uses only the reserved list metrics. The premium list metrics will be used in a future CL. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=149982639
This commit is contained in:
parent
5779cc988b
commit
5253f6fd6b
5 changed files with 277 additions and 4 deletions
|
@ -14,6 +14,7 @@ java_library(
|
|||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//java/google/registry/config",
|
||||
"//java/google/registry/monitoring/metrics",
|
||||
"//java/google/registry/util",
|
||||
"//java/google/registry/xml",
|
||||
"//third_party/java/objectify:objectify-v4_1",
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.registry.label;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.monitoring.metrics.EventMetric;
|
||||
import google.registry.monitoring.metrics.IncrementableMetric;
|
||||
import google.registry.monitoring.metrics.LabelDescriptor;
|
||||
import google.registry.monitoring.metrics.MetricRegistryImpl;
|
||||
|
||||
/** Instrumentation for reserved lists. */
|
||||
class DomainLabelMetrics {
|
||||
|
||||
@AutoValue
|
||||
abstract static class MetricsReservedListMatch {
|
||||
static MetricsReservedListMatch create(
|
||||
String reservedListName, ReservationType reservationType) {
|
||||
return new AutoValue_DomainLabelMetrics_MetricsReservedListMatch(
|
||||
reservedListName, reservationType);
|
||||
}
|
||||
|
||||
abstract String reservedListName();
|
||||
abstract ReservationType reservationType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Labels attached to {@link #reservedListChecks} and {@link #reservedListProcessingTimes}
|
||||
* metrics.
|
||||
*
|
||||
* <p>A domain name can be matched by multiple reserved lists. To keep the metrics useful by
|
||||
* emitting only one metric result for each check, while avoiding potential combinatorial
|
||||
* explosion if all the matching lists and reservation types were to be displayed, we store as
|
||||
* labels only the number of matching lists, along with the most severe match found. Note that
|
||||
* "most severe" may not be meaningful, and this should only be treated as "one of the matches
|
||||
* that we found". But we might as well make it as useful as possible.
|
||||
*/
|
||||
private static final ImmutableSet<LabelDescriptor> RESERVED_LIST_LABEL_DESCRIPTORS =
|
||||
ImmutableSet.of(
|
||||
LabelDescriptor.create("tld", "TLD"),
|
||||
LabelDescriptor.create("reserved_list_count", "Number of matching reserved lists."),
|
||||
LabelDescriptor.create("most_severe_reserved_list", "Reserved list name, if any."),
|
||||
LabelDescriptor.create("most_severe_reservation_type", "Type of reservation found."));
|
||||
|
||||
/** Labels attached to {@link #reservedListHits} metric. */
|
||||
private static final ImmutableSet<LabelDescriptor> RESERVED_LIST_HIT_LABEL_DESCRIPTORS =
|
||||
ImmutableSet.of(
|
||||
LabelDescriptor.create("tld", "TLD"),
|
||||
LabelDescriptor.create("reserved_list", "Reserved list name."),
|
||||
LabelDescriptor.create("reservation_type", "Type of reservation found."));
|
||||
|
||||
/** Metric counting the number of times a label was checked against all reserved lists. */
|
||||
@VisibleForTesting
|
||||
static final IncrementableMetric reservedListChecks =
|
||||
MetricRegistryImpl.getDefault()
|
||||
.newIncrementableMetric(
|
||||
"/domain_label/reserved/checks",
|
||||
"Count of reserved list checks",
|
||||
"count",
|
||||
RESERVED_LIST_LABEL_DESCRIPTORS);
|
||||
|
||||
/** Metric recording the amount of time required to check a label against all reserved lists. */
|
||||
@VisibleForTesting
|
||||
static final EventMetric reservedListProcessingTimes =
|
||||
MetricRegistryImpl.getDefault()
|
||||
.newEventMetric(
|
||||
"/domain_label/reserved/processing_time",
|
||||
"Reserved list check processing time",
|
||||
"milliseconds",
|
||||
RESERVED_LIST_LABEL_DESCRIPTORS,
|
||||
EventMetric.DEFAULT_FITTER);
|
||||
|
||||
/**
|
||||
* Metric recording the number of times a label was found in a reserved list.
|
||||
*
|
||||
* <p>Each time a label is checked, and a list associated with the TLD contains that label, that
|
||||
* count is incremented. A label can be found in more than one list, which would result in a
|
||||
* single increment of {@link #reservedListChecks}, but multiple increments of {@link
|
||||
* #reservedListHits}. It can of course also match zero lists, which would still result in a
|
||||
* single increment of {@link #reservedListChecks}, but no increments of {@link
|
||||
* #reservedListHits}.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final IncrementableMetric reservedListHits =
|
||||
MetricRegistryImpl.getDefault()
|
||||
.newIncrementableMetric(
|
||||
"/domain_label/reserved/hits",
|
||||
"Count of reserved list hits",
|
||||
"count",
|
||||
RESERVED_LIST_HIT_LABEL_DESCRIPTORS);
|
||||
|
||||
/** Update all three reserved list metrics. */
|
||||
static void recordReservedListCheckOutcome(
|
||||
String tld, ImmutableSet<MetricsReservedListMatch> matches, double elapsedMillis) {
|
||||
MetricsReservedListMatch mostSevereMatch = null;
|
||||
for (MetricsReservedListMatch match : matches) {
|
||||
reservedListHits.increment(tld, match.reservedListName(), match.reservationType().toString());
|
||||
if ((mostSevereMatch == null)
|
||||
|| (match.reservationType().compareTo(mostSevereMatch.reservationType()) > 0)) {
|
||||
mostSevereMatch = match;
|
||||
}
|
||||
}
|
||||
String matchCount = String.valueOf(matches.size());
|
||||
String mostSevereReservedList =
|
||||
matches.isEmpty() ? "(none)" : mostSevereMatch.reservedListName();
|
||||
String mostSevereReservationType =
|
||||
(matches.isEmpty() ? ReservationType.UNRESERVED : mostSevereMatch.reservationType())
|
||||
.toString();
|
||||
reservedListChecks.increment(
|
||||
tld, matchCount, mostSevereReservedList, mostSevereReservationType);
|
||||
reservedListProcessingTimes.record(
|
||||
elapsedMillis, tld, matchCount, mostSevereReservedList, mostSevereReservationType);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_
|
|||
import static google.registry.model.registry.label.ReservationType.UNRESERVED;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
|
@ -46,12 +47,14 @@ import com.googlecode.objectify.annotation.Entity;
|
|||
import com.googlecode.objectify.annotation.Mapify;
|
||||
import com.googlecode.objectify.mapper.Mapper;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.DomainLabelMetrics.MetricsReservedListMatch;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A reserved list entity, persisted to Datastore, that is used to check domain label reservations.
|
||||
|
@ -211,19 +214,27 @@ public final class ReservedList
|
|||
* no such entry exists.
|
||||
*/
|
||||
private static ImmutableSet<ReservedListEntry> getReservedListEntries(String label, String tld) {
|
||||
DateTime startTime = DateTime.now(UTC);
|
||||
Registry registry = Registry.get(checkNotNull(tld, "tld"));
|
||||
ImmutableSet<Key<ReservedList>> reservedLists = registry.getReservedLists();
|
||||
ImmutableSet<ReservedList> lists = loadReservedLists(reservedLists);
|
||||
ImmutableSet.Builder<ReservedListEntry> entries = new ImmutableSet.Builder<>();
|
||||
ImmutableSet.Builder<ReservedListEntry> entriesBuilder = new ImmutableSet.Builder<>();
|
||||
ImmutableSet.Builder<MetricsReservedListMatch> metricMatchesBuilder =
|
||||
new ImmutableSet.Builder<>();
|
||||
|
||||
// Loop through all reservation lists and add each of them.
|
||||
for (ReservedList rl : lists) {
|
||||
if (rl.getReservedListEntries().containsKey(label)) {
|
||||
entries.add(rl.getReservedListEntries().get(label));
|
||||
ReservedListEntry entry = rl.getReservedListEntries().get(label);
|
||||
entriesBuilder.add(entry);
|
||||
metricMatchesBuilder.add(
|
||||
MetricsReservedListMatch.create(rl.getName(), entry.reservationType));
|
||||
}
|
||||
}
|
||||
|
||||
return entries.build();
|
||||
ImmutableSet<ReservedListEntry> entries = entriesBuilder.build();
|
||||
DomainLabelMetrics.recordReservedListCheckOutcome(
|
||||
tld, metricMatchesBuilder.build(), DateTime.now(UTC).getMillis() - startTime.getMillis());
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static ImmutableSet<ReservedList> loadReservedLists(
|
||||
|
|
|
@ -22,6 +22,7 @@ java_library(
|
|||
"//java/google/registry/config",
|
||||
"//java/google/registry/flows",
|
||||
"//java/google/registry/model",
|
||||
"//java/google/registry/monitoring/metrics/contrib",
|
||||
"//java/google/registry/util",
|
||||
"//java/google/registry/xml",
|
||||
"//javatests/google/registry/testing",
|
||||
|
|
|
@ -16,13 +16,19 @@ package google.registry.model.registry.label;
|
|||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.reservedListChecks;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.reservedListHits;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.reservedListProcessingTimes;
|
||||
import static google.registry.model.registry.label.ReservationType.ALLOWED_IN_SUNRISE;
|
||||
import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED;
|
||||
import static google.registry.model.registry.label.ReservationType.MISTAKEN_PREMIUM;
|
||||
import static google.registry.model.registry.label.ReservationType.NAME_COLLISION;
|
||||
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT;
|
||||
import static google.registry.model.registry.label.ReservationType.UNRESERVED;
|
||||
import static google.registry.model.registry.label.ReservedList.getReservationTypes;
|
||||
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
|
||||
import static google.registry.monitoring.metrics.contrib.EventMetricSubject.assertThat;
|
||||
import static google.registry.monitoring.metrics.contrib.IncrementableMetricSubject.assertThat;
|
||||
import static google.registry.testing.DatastoreHelper.createTld;
|
||||
import static google.registry.testing.DatastoreHelper.persistReservedList;
|
||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||
|
@ -67,6 +73,21 @@ public class ReservedListTest {
|
|||
public void before() throws Exception {
|
||||
inject.setStaticField(Ofy.class, "clock", clock);
|
||||
createTld("tld");
|
||||
reservedListChecks.reset();
|
||||
reservedListProcessingTimes.reset();
|
||||
reservedListHits.reset();
|
||||
}
|
||||
|
||||
private static void verifyUnreservedCheckCount(int unreservedCount) {
|
||||
assertThat(reservedListChecks)
|
||||
.hasValueForLabels(unreservedCount, "tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListProcessingTimes)
|
||||
.hasAnyValueForLabels("tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListHits).hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -75,11 +96,13 @@ public class ReservedListTest {
|
|||
assertThat(getReservationTypes("doodle", "tld")).containsExactly(UNRESERVED);
|
||||
assertThat(getReservationTypes("access", "tld")).containsExactly(UNRESERVED);
|
||||
assertThat(getReservationTypes("rich", "tld")).containsExactly(UNRESERVED);
|
||||
verifyUnreservedCheckCount(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZeroReservedLists_doesNotCauseError() throws Exception {
|
||||
assertThat(getReservationTypes("doodle", "tld")).containsExactly(UNRESERVED);
|
||||
verifyUnreservedCheckCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -87,6 +110,7 @@ public class ReservedListTest {
|
|||
for (String sld : ImmutableList.of("aa", "az", "zz", "91", "1n", "j5")) {
|
||||
assertThat(getReservationTypes(sld, "tld")).containsExactly(UNRESERVED);
|
||||
}
|
||||
verifyUnreservedCheckCount(6);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -95,6 +119,7 @@ public class ReservedListTest {
|
|||
for (char c = 'a'; c <= 'z'; c++) {
|
||||
assertThat(getReservationTypes("" + c, "tld")).containsExactly(UNRESERVED);
|
||||
}
|
||||
verifyUnreservedCheckCount(26);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -120,6 +145,22 @@ public class ReservedListTest {
|
|||
.isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("random.tld"), "abcdefg"))
|
||||
.isFalse();
|
||||
assertThat(reservedListChecks)
|
||||
.hasValueForLabels(1, "tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasValueForLabels(6, "tld", "1", "reserved1", RESERVED_FOR_ANCHOR_TENANT.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListProcessingTimes)
|
||||
.hasAnyValueForLabels("tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "1", "reserved1", RESERVED_FOR_ANCHOR_TENANT.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListHits)
|
||||
.hasValueForLabels(6, "tld", "reserved1", RESERVED_FOR_ANCHOR_TENANT.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -138,6 +179,40 @@ public class ReservedListTest {
|
|||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol3.tld"), "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol4.tld"), "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol5.tld"), "")).isFalse();
|
||||
assertThat(reservedListChecks)
|
||||
.hasValueForLabels(1, "tld", "1", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "1", "reserved2", NAME_COLLISION.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "1", "reserved2", MISTAKEN_PREMIUM.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "1", "reserved2", ALLOWED_IN_SUNRISE.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListProcessingTimes)
|
||||
.hasAnyValueForLabels("tld", "1", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "1", "reserved2", NAME_COLLISION.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "1", "reserved2", MISTAKEN_PREMIUM.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "1", "reserved2", ALLOWED_IN_SUNRISE.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListHits)
|
||||
.hasValueForLabels(1, "tld", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "reserved2", NAME_COLLISION.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "reserved2", MISTAKEN_PREMIUM.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "reserved2", ALLOWED_IN_SUNRISE.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -173,6 +248,28 @@ public class ReservedListTest {
|
|||
assertThat(getReservationTypes("roflcopter", "tld")).containsExactly(FULLY_BLOCKED);
|
||||
assertThat(getReservationTypes("snowcrash", "tld")).containsExactly(FULLY_BLOCKED);
|
||||
assertThat(getReservationTypes("doge", "tld")).containsExactly(UNRESERVED);
|
||||
assertThat(reservedListChecks)
|
||||
.hasValueForLabels(1, "tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasValueForLabels(2, "tld", "1", "reserved1", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasValueForLabels(2, "tld", "1", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListProcessingTimes)
|
||||
.hasAnyValueForLabels("tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "1", "reserved1", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "1", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListHits)
|
||||
.hasValueForLabels(2, "tld", "reserved1", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasValueForLabels(2, "tld", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -207,6 +304,22 @@ public class ReservedListTest {
|
|||
+ " after unsetting the registry's second reserved list")
|
||||
.that(getReservationTypes("roflcopter", "tld"))
|
||||
.containsExactly(UNRESERVED);
|
||||
assertThat(reservedListChecks)
|
||||
.hasValueForLabels(1, "tld", "1", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListProcessingTimes)
|
||||
.hasAnyValueForLabels("tld", "1", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListHits)
|
||||
.hasValueForLabels(1, "tld", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -218,6 +331,26 @@ public class ReservedListTest {
|
|||
persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1, rl2).build());
|
||||
assertThat(getReservationTypes("lol", "tld")).containsExactly(FULLY_BLOCKED, NAME_COLLISION);
|
||||
assertThat(getReservationTypes("roflcopter", "tld")).containsExactly(ALLOWED_IN_SUNRISE);
|
||||
assertThat(reservedListChecks)
|
||||
.hasValueForLabels(1, "tld", "1", "reserved1", ALLOWED_IN_SUNRISE.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "2", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListProcessingTimes)
|
||||
.hasAnyValueForLabels("tld", "1", "reserved1", ALLOWED_IN_SUNRISE.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "2", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(reservedListHits)
|
||||
.hasValueForLabels(1, "tld", "reserved1", NAME_COLLISION.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "reserved1", ALLOWED_IN_SUNRISE.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue