Skip to content

Commit

Permalink
only consider grouping keys for id mapping (#1119)
Browse files Browse the repository at this point in the history
When using a group by expression, only consider the tags
that are part of the grouping rather than the full set
when mapping the id.
  • Loading branch information
brharrington authored Mar 13, 2024
1 parent 0195a20 commit 33036d4
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -471,15 +471,19 @@ final class Count implements AggregateFunction {
final class GroupBy implements DataExpr {

private final AggregateFunction af;
private final List<String> keys;
private final Set<String> keys;

/** Create a new instance. */
GroupBy(AggregateFunction af, List<String> keys) {
GroupBy(AggregateFunction af, Set<String> keys) {
Preconditions.checkArg(!keys.isEmpty(), "key list for group by cannot be empty");
this.af = af;
this.keys = keys;
}

Set<String> keys() {
return keys;
}

@SuppressWarnings("PMD.ReturnEmptyCollectionRatherThanNull")
private Map<String, String> keyTags(Map<String, String> tags) {
Map<String, String> result = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

/**
* Evaluates all the expressions for a set of subscriptions.
Expand All @@ -49,7 +49,7 @@ public class Evaluator {

private final Lock lock = new ReentrantLock();
private final Map<String, String> commonTags;
private final Function<Id, Map<String, String>> idMapper;
private final BiFunction<Id, Set<String>, Map<String, String>> idMapper;
private final long step;
private final boolean delayGaugeAggregation;
private final QueryIndex<SubscriptionEntry> index;
Expand Down Expand Up @@ -174,11 +174,12 @@ public EvalPayload eval(long timestamp) {
final double v = consolidator.value(timestamp);
if (!Double.isNaN(v)) {
Map<String, String> tags = Collections.emptyMap();
if (!(expr instanceof DataExpr.AggregateFunction)) {
if (expr instanceof DataExpr.GroupBy) {
// Aggregation functions only use tags based on the expression. Avoid overhead of
// considering the tags for the data.
tags = idMapper.apply(entry.getKey());
tags.putAll(commonTags);
Set<String> keys = ((DataExpr.GroupBy) expr).keys();
tags = idMapper.apply(entry.getKey(), keys);
putCommonTags(tags, keys);
}
if (delayGaugeAggr && consolidator.isGauge()) {
// When performing a group by, datapoints missing tag used for the grouping
Expand Down Expand Up @@ -211,6 +212,18 @@ public EvalPayload eval(long timestamp) {
return new EvalPayload(timestamp, metrics);
}

private void putCommonTags(Map<String, String> dst, Set<String> keys) {
if (dst.size() < keys.size()) {
// Skip this step unless there is something pending
for (String key : keys) {
String value = commonTags.get(key);
if (value != null) {
dst.put(key, value);
}
}
}
}

private String idHash(Id id) {
Hash64 hasher = new Hash64();
hasher.updateString(id.name());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import com.netflix.spectator.atlas.AtlasConfig;

import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
Expand All @@ -45,7 +47,7 @@ static EvaluatorConfig fromAtlasConfig(AtlasConfig config) {
return config.commonTags();
}

@Override public Function<Id, Map<String, String>> idMapper() {
@Override public BiFunction<Id, Set<String>, Map<String, String>> idMapper() {
return new IdMapper(JsonUtils.createReplacementFunction(config.validTagCharacters()));
}
};
Expand All @@ -59,7 +61,7 @@ static EvaluatorConfig fromAtlasConfig(AtlasConfig config) {
Map<String, String> commonTags();

/** Function to convert an id to a map of key/value pairs. */
default Function<Id, Map<String, String>> idMapper() {
default BiFunction<Id, Set<String>, Map<String, String>> idMapper() {
return new IdMapper(Function.identity());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
Expand All @@ -27,7 +29,7 @@
* <p><b>Classes in this package are only intended for use internally within spectator. They may
* change at any time and without notice.</b>
*/
public final class IdMapper implements Function<Id, Map<String, String>> {
public final class IdMapper implements BiFunction<Id, Set<String>, Map<String, String>> {

private final Function<String, String> fixTagString;

Expand All @@ -37,21 +39,25 @@ public IdMapper(Function<String, String> fixTagString) {
}

@Override
public Map<String, String> apply(Id id) {
public Map<String, String> apply(Id id, Set<String> keys) {
int size = id.size();
Map<String, String> tags = new HashMap<>(size);
Map<String, String> tags = new HashMap<>(keys.size());

// Start at 1 as name will be added last
for (int i = 1; i < size; ++i) {
String k = fixTagString.apply(id.getKey(i));
String v = fixTagString.apply(id.getValue(i));
tags.put(k, v);
if (keys.contains(k)) {
String v = fixTagString.apply(id.getValue(i));
tags.put(k, v);
}
}

// Add the name, it is added last so it will have precedence if the user tried to
// use a tag key of "name".
String name = fixTagString.apply(id.name());
tags.put("name", name);
if (keys.contains("name")) {
String name = fixTagString.apply(id.name());
tags.put("name", name);
}

return tags;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;

/**
Expand Down Expand Up @@ -196,7 +197,7 @@ private static Object parse(String expr) {
case ":by":
tmp = (List<String>) stack.pop();
af = (DataExpr.AggregateFunction) stack.pop();
stack.push(new DataExpr.GroupBy(af, tmp));
stack.push(new DataExpr.GroupBy(af, new LinkedHashSet<>(tmp)));
break;
case ":rollup-drop":
tmp = (List<String>) stack.pop();
Expand Down

0 comments on commit 33036d4

Please sign in to comment.