diff options
author | Jon Marius Venstad <jvenstad@yahoo-inc.com> | 2019-04-16 10:31:58 +0200 |
---|---|---|
committer | Jon Marius Venstad <jvenstad@yahoo-inc.com> | 2019-06-06 13:03:28 +0200 |
commit | 0f554ee318980e0a2cb82e1cbd16bfde4e1aa9a0 (patch) | |
tree | 96eb5253f5b6d64cf0159d7d3deb61426a3bf4f3 /tenant-cd/src/main/java/com | |
parent | 5e326ad275e3f45ebae1e137271f63cb3bdaabe9 (diff) |
Add aggregation to metrics
Diffstat (limited to 'tenant-cd/src/main/java/com')
3 files changed, 49 insertions, 19 deletions
diff --git a/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Metric.java b/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Metric.java index 5da4c3fb750..b7517668e8f 100644 --- a/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Metric.java +++ b/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Metric.java @@ -1,5 +1,6 @@ package com.yahoo.vespa.tenant.cd.metrics; +import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; @@ -18,7 +19,7 @@ public class Metric { private final Map<Map<String, ?>, Statistic> statistics; private Metric(Map<Map<String, ?>, Statistic> statistics) { - this.statistics = copyOf(statistics); + this.statistics = statistics; } /** Creates a new Metric with a copy of the given data. */ @@ -26,19 +27,16 @@ public class Metric { if (data.isEmpty()) throw new IllegalArgumentException("No data given."); + Map<Map<String, ?>, Statistic> copies = new HashMap<>(); Set<String> dimensions = data.keySet().iterator().next().keySet(); - for (Map<String, ?> point : data.keySet()) { - if (point.keySet().contains(null)) - throw new IllegalArgumentException("Dimensions may not be null: '" + point.keySet() + "'."); - + data.forEach((point, statistic) -> { if ( ! point.keySet().equals(dimensions)) throw new IllegalArgumentException("Given data has inconsistent dimensions: '" + dimensions + "' vs '" + point.keySet() + "'."); - if (point.values().contains(null)) - throw new IllegalArgumentException("Position along a dimension may not be null: '" + point + "'."); - } + copies.put(copyOf(point), statistic); + }); - return new Metric(data); + return new Metric(copyOf(copies)); } /** Returns a Metric view of the subset of points in the given hyperplane; its dimensions must be a subset of those of this Metric. */ @@ -48,6 +46,21 @@ public class Metric { .collect(toUnmodifiableMap(point -> point, statistics::get))); } + /** Returns a version of this where statistics along the given hyperspace are aggregated. This does not preserve last, 95 and 99 percentile values. */ + public Metric collapse(Set<String> hyperspace) { + return new Metric(statistics.keySet().stream() + .collect(toUnmodifiableMap(point -> point.keySet().stream() + .filter(dimension -> ! hyperspace.contains(dimension)) + .collect(toUnmodifiableMap(dimension -> dimension, point::get)), + statistics::get, + Statistic::mergedWith))); + } + + /** Returns a collapsed version of this, with all statistics aggregated. This does not preserve last, 95 and 99 percentile values. */ + public Metric collapse() { + return collapse(statistics.keySet().iterator().next().keySet()); + } + /** If this Metric contains a single point, returns the Statistic of that point; otherwise, throws an exception. */ public Statistic statistic() { if (statistics.size() == 1) diff --git a/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Metrics.java b/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Metrics.java index c080d82a343..fb2528c4df5 100644 --- a/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Metrics.java +++ b/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Metrics.java @@ -28,17 +28,14 @@ public class Metrics { private Metrics(Instant start, Instant end, Map<String, Metric> metrics) { this.start = start; this.end = end; - this.metrics = copyOf(metrics); + this.metrics = metrics; } public static Metrics of(Instant start, Instant end, Map<String, Metric> metrics) { - if (metrics.containsKey(null) || metrics.containsValue(null)) - throw new IllegalArgumentException("Metrics may not contain null keys or values: '" + metrics + "'."); - if ( ! start.isBefore(end)) throw new IllegalArgumentException("Given time interval must be positive: '" + start + "' to '" + end + "'."); - return new Metrics(start, end, metrics); + return new Metrics(start, end, copyOf(metrics)); } /** Returns the start of the time window from which these metrics were sampled, or throws if the status is {@code Status.down}. */ diff --git a/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Statistic.java b/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Statistic.java index 65c6dc3ed3f..e3315bcd181 100644 --- a/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Statistic.java +++ b/tenant-cd/src/main/java/com/yahoo/vespa/tenant/cd/metrics/Statistic.java @@ -1,8 +1,11 @@ package com.yahoo.vespa.tenant.cd.metrics; +import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; import java.util.StringJoiner; +import java.util.function.Function; +import java.util.stream.Collectors; import static java.util.Map.copyOf; @@ -17,14 +20,11 @@ public class Statistic { /** Creates a new Statistic with a copy of the given data. */ private Statistic(Map<Type, Double> data) { - this.data = copyOf(data); + this.data = data; } public static Statistic of(Map<Type, Double> data) { - if (data.containsKey(null) || data.containsValue(null)) - throw new IllegalArgumentException("Data may not contain null keys or values: '" + data + "'."); - - return new Statistic(data); + return new Statistic(copyOf(data)); } /** Returns the value of the given type, or throws a NoSuchElementException if this isn't known. */ @@ -40,6 +40,26 @@ public class Statistic { return data; } + Statistic mergedWith(Statistic other) { + if (data.keySet().equals(other.data.keySet())) + throw new IllegalArgumentException("Incompatible key sets '" + data.keySet() + "' and '" + other.data.keySet() + "'."); + + Map<Type, Double> merged = new HashMap<>(); + double n1 = get(Type.count), n2 = other.get(Type.count); + for (Type type : data.keySet()) switch (type) { + case count: merged.put(type, n1 + n2); break; + case rate: merged.put(type, get(Type.rate) + other.get(Type.rate)); break; + case max: merged.put(type, Math.max(get(Type.max), other.get(Type.max))); break; + case min: merged.put(type, Math.min(get(Type.min), other.get(Type.min))); break; + case average: merged.put(type, (n1 * get(Type.average) + n2 * other.get(Type.average)) / (n1 + n2)); break; + case last: + case percentile95: + case percentile99: break; + default: throw new IllegalArgumentException("Unexpected type '" + type + "'."); + } + return of(merged); + } + @Override public String toString() { return new StringJoiner(", ", Statistic.class.getSimpleName() + "[", "]") |