Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[break] Fix MetricName comparator for tags, MetricName#safeTags() returns SortedMap #272

Merged
merged 5 commits into from
May 26, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
package com.palantir.tritium.metrics.registry;

import com.palantir.logsafe.Safe;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.NavigableMap;
import org.immutables.value.Value;

@Value.Immutable(prehash = true)
Expand All @@ -30,25 +28,6 @@
visibility = Value.Style.ImplementationVisibility.PACKAGE)
public interface MetricName extends Comparable<MetricName> {

Comparator<Map.Entry<String, String>> entryComparator =
Map.Entry.<String, String>comparingByKey()
.thenComparing(Map.Entry.comparingByValue());
Comparator<MetricName> metricNameComparator =
Comparator.comparing(MetricName::safeName)
.thenComparing(metricName -> metricName.safeTags().entrySet(), (set1, set2) -> {
Iterator<Map.Entry<String, String>> i1 = set1.iterator();
Iterator<Map.Entry<String, String>> i2 = set2.iterator();
while (i1.hasNext() && i2.hasNext()) {
Map.Entry<String, String> e1 = i1.next();
Map.Entry<String, String> e2 = i2.next();
int compare = entryComparator.compare(e1, e2);
if (compare != 0) {
return compare;
}
}
return i1.hasNext() ? -1 : i2.hasNext() ? 1 : 0;
});

/**
* General/abstract measure (e.g. server.response-time).
* <p>
Expand All @@ -61,11 +40,12 @@ public interface MetricName extends Comparable<MetricName> {
* <p>
* All tags and keys must be {@link Safe} to log.
*/
Map<String, String> safeTags();
@Value.NaturalOrder
NavigableMap<String, String> safeTags();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we loosen this to SortedMap?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to just leave this as a NavigableMap as the underlying impl generated by immutables is a TreeMap either way with jdkOnly = true (though we could remove that since we already rely on Guava implementation dependency, created optional #273 for that).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer the guava approach. We heavily rely on guava immutable collections, and they will avoid collection re-allocations when we attempt to re-wrap them.

I'm not sure any of the functionality provided by NavigableMap will be helpful for safeTags, where SortedMap provides clear advantages. Using the less specific type allows more freedom to change the implementation in the future. That said, I'm not actually aware of a SortedMap implementation which doesn't also implement NavigableMap, so I defer to your judgement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, you convinced me, went with SortedMap for now


Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually should make this:

    @Value.NaturalOrder
    NavigableMap<String, String> safeTags();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would do the trick, if we're okay with exposing NavigatableMap instead of Map

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does MetricName need to be comparable? Are we better off removing that piece?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mainly added it for convenience for the toString form when analyzing getMetrics output

@Override
default int compareTo(MetricName that) {
return metricNameComparator.compare(this, that);
return MetricNames.metricNameComparator.compare(this, that);
}

static Builder builder() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* (c) Copyright 2019 Palantir Technologies Inc. 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 com.palantir.tritium.metrics.registry;

import com.google.common.annotations.VisibleForTesting;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;

final class MetricNames {

private MetricNames() {}

private static final Comparator<Map.Entry<String, String>> entryComparator =
Map.Entry.<String, String>comparingByKey()
.thenComparing(Map.Entry.comparingByValue());

private static final Comparator<Map<String, String>> sizeComparator = Comparator.comparingInt(Map::size);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice touch


private static final Comparator<Map<String, String>> mapComparator = (m1, m2) -> {
Iterator<Map.Entry<String, String>> i1 = m1.entrySet().iterator();
Iterator<Map.Entry<String, String>> i2 = m2.entrySet().iterator();
while (i1.hasNext() && i2.hasNext()) {
Map.Entry<String, String> e1 = i1.next();
Map.Entry<String, String> e2 = i2.next();
int compare = entryComparator.compare(e1, e2);
if (compare != 0) {
return compare;
}
}
return i1.hasNext() ? -1 : i2.hasNext() ? 1 : 0;
};

@VisibleForTesting
static final Comparator<MetricName> metricNameComparator =
Comparator.comparing(MetricName::safeName)
.thenComparing(MetricName::safeTags, sizeComparator)
.thenComparing(MetricName::safeTags, mapComparator);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,33 @@ public void compareSame() {
MetricName one = MetricName.builder()
.safeName("test")
.putSafeTags("key", "value")
.putSafeTags("key1", "value1")
.putSafeTags("key2", "value2")
.build();
MetricName two = MetricName.builder()
.safeName("test")
.putSafeTags("key2", "value2")
.putSafeTags("key", "value")
.putSafeTags("key1", "value1")
.build();

assertThat(one).isEqualTo(two);
assertThat(two).isEqualTo(one);
assertThat(one).isEqualByComparingTo(two);
assertThat(one).usingComparator(MetricName.metricNameComparator).isEqualByComparingTo(two);
assertThat(one).usingComparator(MetricNames.metricNameComparator).isEqualByComparingTo(two);
}

@Test
public void compareNameOrder() {
MetricName one = MetricName.builder()
.safeName("a")
.putSafeTags("key", "value")
.putSafeTags("key1", "value1")
.putSafeTags("key2", "value2")
.build();
MetricName two = MetricName.builder()
.safeName("b")
.putSafeTags("key", "value")
.putSafeTags("key1", "value1")
.putSafeTags("key2", "value2")
.build();

assertThat(one).isLessThan(two);
Expand All @@ -68,4 +74,54 @@ public void compareSameName() {
assertThat(one).isLessThan(two);
assertThat(two).isGreaterThan(one);
}

@Test
public void compareTagsName() {
MetricName one = MetricName.builder()
.safeName("a")
.putSafeTags("keyA", "value1")
.putSafeTags("keyB", "value2")
.putSafeTags("keyB", "value1")
.build();
MetricName two = MetricName.builder()
.safeName("a")
.putSafeTags("keyC", "value2")
.putSafeTags("keyA", "value1")
.putSafeTags("keyB", "value1")
.build();

assertThat(one).isLessThan(two);
assertThat(two).isGreaterThan(one);
}

@Test
public void compareTagsSize() {
MetricName one = MetricName.builder()
.safeName("a")
.build();
MetricName two = MetricName.builder()
.safeName("a")
.putSafeTags("keyA", "value1")
.build();

assertThat(one).isLessThan(two);
assertThat(two).isGreaterThan(one);
}

@Test
public void compareTagsValue() {
MetricName one = MetricName.builder()
.safeName("a")
.putSafeTags("keyA", "value1")
.putSafeTags("keyB", "value2")
.build();
MetricName two = MetricName.builder()
.safeName("a")
.putSafeTags("keyB", "value3")
.putSafeTags("keyA", "value1")
.build();

assertThat(one).isLessThan(two);
assertThat(two).isGreaterThan(one);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,33 @@ public void testReplaceMetricRegistry() {
assertThat(registry.getMetrics()).isEmpty();
}

@Test
public void testGetMetrics() {
MetricName metricName = MetricName.builder()
.safeName("counter1")
.putSafeTags("tagA", Long.toString(1))
.putSafeTags("tagB", Long.toString(2))
.build();
Counter counter = registry.counter(metricName);
counter.inc();
Metric metric = registry.getMetrics().get(metricName);
assertThat(metric)
.isInstanceOf(Counter.class)
.isSameAs(counter)
.isSameAs(registry.counter(
MetricName.builder()
.safeName("counter1")
.putSafeTags("tagB", "2")
.putSafeTags("tagA", Long.toString(1))
.build()))
.isSameAs(registry.getMetrics().get(MetricName.builder()
.safeName("counter1")
.putSafeTags("tagA", Long.toString(1))
.putSafeTags("tagB", Integer.toString(2))
.build()));
assertThat(counter.getCount()).isEqualTo(1);
}

private void assertMetric(String name, String tagKey, String tagValue, Meter meter) {
assertThat(registry.getMetrics())
.containsEntry(MetricName.builder().safeName(name).putSafeTags(tagKey, tagValue).build(), meter);
Expand Down