Skip to content

Commit

Permalink
Expose agg usage in Feature Usage API (elastic#55732)
Browse files Browse the repository at this point in the history
Counts usage of the aggs and exposes them on the _nodes/usage/.

Closes elastic#53746
  • Loading branch information
imotov committed Apr 30, 2020
1 parent c36bcb4 commit d0fdc5c
Show file tree
Hide file tree
Showing 40 changed files with 521 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static TransformConfig randomTransformConfig() {
randomSourceConfig(),
randomDestConfig(),
randomBoolean() ? null : TimeValue.timeValueMillis(randomIntBetween(1000, 1000000)),
randomBoolean() ? null : randomSyncConfig(),
randomBoolean() ? null : randomSyncConfig(),
PivotConfigTests.randomPivotConfig(),
randomBoolean() ? null : randomAlphaOfLengthBetween(1, 100),
randomBoolean() ? null : Instant.now(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@

package org.elasticsearch.test.rest;

import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.Request;
import org.elasticsearch.common.Strings;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

Expand All @@ -41,14 +45,7 @@ public void testWithRestUsage() throws IOException {
Response beforeResponse = client().performRequest(new Request("GET", path));
Map<String, Object> beforeResponseBodyMap = entityAsMap(beforeResponse);
assertThat(beforeResponseBodyMap, notNullValue());
Map<String, Object> before_nodesMap = (Map<String, Object>) beforeResponseBodyMap.get("_nodes");
assertThat(before_nodesMap, notNullValue());
Integer beforeTotal = (Integer) before_nodesMap.get("total");
Integer beforeSuccessful = (Integer) before_nodesMap.get("successful");
Integer beforeFailed = (Integer) before_nodesMap.get("failed");
assertThat(beforeTotal, greaterThan(0));
assertThat(beforeSuccessful, equalTo(beforeTotal));
assertThat(beforeFailed, equalTo(0));
int beforeSuccessful = assertSuccess(beforeResponseBodyMap);

Map<String, Object> beforeNodesMap = (Map<String, Object>) beforeResponseBodyMap.get("nodes");
assertThat(beforeNodesMap, notNullValue());
Expand Down Expand Up @@ -98,14 +95,7 @@ public void testWithRestUsage() throws IOException {
Response response = client().performRequest(new Request("GET", "_nodes/usage"));
Map<String, Object> responseBodyMap = entityAsMap(response);
assertThat(responseBodyMap, notNullValue());
Map<String, Object> _nodesMap = (Map<String, Object>) responseBodyMap.get("_nodes");
assertThat(_nodesMap, notNullValue());
Integer total = (Integer) _nodesMap.get("total");
Integer successful = (Integer) _nodesMap.get("successful");
Integer failed = (Integer) _nodesMap.get("failed");
assertThat(total, greaterThan(0));
assertThat(successful, equalTo(total));
assertThat(failed, equalTo(0));
int successful = assertSuccess(responseBodyMap);

Map<String, Object> nodesMap = (Map<String, Object>) responseBodyMap.get("nodes");
assertThat(nodesMap, notNullValue());
Expand Down Expand Up @@ -143,4 +133,97 @@ public void testMetricsWithAll() throws IOException {
+ "\"reason\":\"request [_nodes/usage/_all,rest_actions] contains _all and individual metrics [_all,rest_actions]\""));
}

@SuppressWarnings("unchecked")
public void testAggregationUsage() throws IOException {
// First get the current usage figures
String path = randomFrom("_nodes/usage", "_nodes/usage/aggregations", "_nodes/usage/_all");
Response beforeResponse = client().performRequest(new Request("GET", path));
Map<String, Object> beforeResponseBodyMap = entityAsMap(beforeResponse);
assertThat(beforeResponseBodyMap, notNullValue());
int beforeSuccessful = assertSuccess(beforeResponseBodyMap);

Map<String, Object> beforeNodesMap = (Map<String, Object>) beforeResponseBodyMap.get("nodes");
assertThat(beforeNodesMap, notNullValue());
assertThat(beforeNodesMap.size(), equalTo(beforeSuccessful));

Map<String, Map<String, Long>> beforeCombinedAggsUsage = getTotalUsage(beforeNodesMap);
// Do some requests to get some rest usage stats
Request create = new Request("PUT", "/test");
create.setJsonEntity("{\"mappings\": {\"properties\": { \"str\": {\"type\": \"keyword\"}, " +
"\"foo\": {\"type\": \"keyword\"}, \"num\": {\"type\": \"long\"}, \"start\": {\"type\": \"date\"} } }}");
client().performRequest(create);

Request searchRequest = new Request("GET", "/test/_search");
SearchSourceBuilder searchSource = new SearchSourceBuilder()
.aggregation(AggregationBuilders.terms("str_terms").field("str.keyword"))
.aggregation(AggregationBuilders.terms("num_terms").field("num"))
.aggregation(AggregationBuilders.avg("num_avg").field("num"));
searchRequest.setJsonEntity(Strings.toString(searchSource));
searchRequest.setJsonEntity(Strings.toString(searchSource));
client().performRequest(searchRequest);

searchRequest = new Request("GET", "/test/_search");
searchSource = new SearchSourceBuilder()
.aggregation(AggregationBuilders.terms("start").field("start"))
.aggregation(AggregationBuilders.avg("num1").field("num"))
.aggregation(AggregationBuilders.avg("num2").field("num"))
.aggregation(AggregationBuilders.terms("foo").field("foo.keyword"));
String r = Strings.toString(searchSource);
searchRequest.setJsonEntity(Strings.toString(searchSource));
client().performRequest(searchRequest);

Response response = client().performRequest(new Request("GET", "_nodes/usage"));
Map<String, Object> responseBodyMap = entityAsMap(response);
assertThat(responseBodyMap, notNullValue());
int successful = assertSuccess(responseBodyMap);

Map<String, Object> nodesMap = (Map<String, Object>) responseBodyMap.get("nodes");
assertThat(nodesMap, notNullValue());
assertThat(nodesMap.size(), equalTo(successful));

Map<String, Map<String, Long>> afterCombinedAggsUsage = getTotalUsage(nodesMap);

assertDiff(beforeCombinedAggsUsage, afterCombinedAggsUsage, "terms", "numeric", 1L);
assertDiff(beforeCombinedAggsUsage, afterCombinedAggsUsage, "terms", "date", 1L);
assertDiff(beforeCombinedAggsUsage, afterCombinedAggsUsage, "terms", "bytes", 2L);
assertDiff(beforeCombinedAggsUsage, afterCombinedAggsUsage, "avg", "numeric", 3L);
}

private void assertDiff(Map<String, Map<String, Long>> before, Map<String, Map<String, Long>> after, String agg, String vst,
long diff) {
Long valBefore = before.getOrDefault(agg, Collections.emptyMap()).getOrDefault(vst, 0L);
Long valAfter = after.getOrDefault(agg, Collections.emptyMap()).getOrDefault(vst, 0L);
assertThat(agg + "." + vst, valAfter - valBefore, equalTo(diff) );
}

private Map<String, Map<String, Long>> getTotalUsage(Map<String, Object> nodeUsage) {
Map<String, Map<String, Long>> combined = new HashMap<>();
for (Map.Entry<String, Object> nodeEntry : nodeUsage.entrySet()) {
@SuppressWarnings("unchecked")
Map<String, Object> beforeAggsUsage = (Map<String, Object>) ((Map<String, Object>) nodeEntry.getValue()).get("aggregations");
assertThat(beforeAggsUsage, notNullValue());
for (Map.Entry<String, Object> aggEntry : beforeAggsUsage.entrySet()) {
@SuppressWarnings("unchecked") Map<String, Object> aggMap = (Map<String, Object>) aggEntry.getValue();
Map<String, Long> combinedAggMap = combined.computeIfAbsent(aggEntry.getKey(), k -> new HashMap<>());
for (Map.Entry<String, Object> valSourceEntry : aggMap.entrySet()) {
combinedAggMap.put(valSourceEntry.getKey(),
combinedAggMap.getOrDefault(valSourceEntry.getKey(), 0L) + ((Number) valSourceEntry.getValue()).longValue());
}
}
}
return combined;
}

private int assertSuccess(Map<String, Object> responseBodyMap) {
@SuppressWarnings("unchecked") Map<String, Object> nodesResultMap = (Map<String, Object>) responseBodyMap.get("_nodes");
assertThat(nodesResultMap, notNullValue());
Integer total = (Integer) nodesResultMap.get("total");
Integer successful = (Integer) nodesResultMap.get("successful");
Integer failed = (Integer) nodesResultMap.get("failed");
assertThat(total, greaterThan(0));
assertThat(successful, equalTo(total));
assertThat(failed, equalTo(0));
return successful;
}

}
23 changes: 14 additions & 9 deletions docs/reference/cluster/nodes-usage.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ of features for each node. All the nodes selective options are explained
==== {api-path-parms-title}

`<metric>`::
(Optional, string) Limits the information returned to the specific metrics.
A comma-separated list of the following options:
(Optional, string) Limits the information returned to the specific metrics.
A comma-separated list of the following options:
+
--
`_all`::
Returns all stats.

`rest_actions`::
Returns the REST actions classname with a count of the number of times
Returns the REST actions classname with a count of the number of times
that action has been called on the node.
--

Expand Down Expand Up @@ -79,11 +79,14 @@ The API returns the following response:
"timestamp": 1492553961812, <1>
"since": 1492553906606, <2>
"rest_actions": {
"org.elasticsearch.rest.action.admin.cluster.RestNodesUsageAction": 1,
"org.elasticsearch.rest.action.admin.indices.RestCreateIndexAction": 1,
"org.elasticsearch.rest.action.document.RestGetAction": 1,
"org.elasticsearch.rest.action.search.RestSearchAction": 19, <3>
"org.elasticsearch.rest.action.admin.cluster.RestNodesInfoAction": 36
"nodes_usage_action": 1,
"create_index_action": 1,
"document_get_action": 1,
"search_action": 19, <3>
"nodes_info_action": 36
},
"aggregations": {
...
}
}
}
Expand All @@ -94,7 +97,9 @@ The API returns the following response:
// TESTRESPONSE[s/1492553961812/$body.$_path/]
// TESTRESPONSE[s/1492553906606/$body.$_path/]
// TESTRESPONSE[s/"rest_actions": [^}]+}/"rest_actions": $body.$_path/]
// TESTRESPONSE[s/"aggregations": [^}]+}/"aggregations": $body.$_path/]
<1> Timestamp for when this nodes usage request was performed.
<2> Timestamp for when the usage information recording was started. This is
equivalent to the time that the node was started.
<3> Search action has been called 19 times for this node.

Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@
import org.apache.lucene.util.NumericUtils;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.NumberFieldMapper;
import org.elasticsearch.plugins.SearchPlugin;
import org.elasticsearch.search.aggregations.AggregatorTestCase;
import org.elasticsearch.search.aggregations.matrix.MatrixAggregationPlugin;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class MatrixStatsAggregatorTests extends AggregatorTestCase {

Expand Down Expand Up @@ -136,4 +139,8 @@ public void testTwoFieldsReduce() throws Exception {
}
}

@Override
protected List<SearchPlugin> getSearchPlugins() {
return Collections.singletonList(new MatrixAggregationPlugin());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import java.io.IOException;
import java.util.Map;

import static org.elasticsearch.search.aggregations.support.AggregationUsageService.OTHER_SUBTYPE;

public class ChildrenAggregatorFactory extends ValuesSourceAggregatorFactory {

private final Query parentFilter;
Expand Down Expand Up @@ -84,4 +86,10 @@ protected Aggregator doCreateInternal(ValuesSource rawValuesSource,
return asMultiBucketAggregator(this, searchContext, parent);
}
}

@Override
public String getStatsSubtype() {
// Child Aggregation is registered in non-standard way, so it might return child's values type
return OTHER_SUBTYPE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import java.io.IOException;
import java.util.Map;

import static org.elasticsearch.search.aggregations.support.AggregationUsageService.OTHER_SUBTYPE;

public class ParentAggregatorFactory extends ValuesSourceAggregatorFactory {

private final Query parentFilter;
Expand Down Expand Up @@ -85,4 +87,10 @@ protected Aggregator doCreateInternal(ValuesSource rawValuesSource,
return asMultiBucketAggregator(this, searchContext, children);
}
}

@Override
public String getStatsSubtype() {
// Parent Aggregation is registered in non-standard way
return OTHER_SUBTYPE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@
import org.elasticsearch.index.mapper.NumberFieldMapper;
import org.elasticsearch.index.mapper.Uid;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.join.ParentJoinPlugin;
import org.elasticsearch.join.mapper.MetaJoinFieldMapper;
import org.elasticsearch.join.mapper.ParentJoinFieldMapper;
import org.elasticsearch.plugins.SearchPlugin;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregatorTestCase;
Expand Down Expand Up @@ -328,4 +330,9 @@ private void testCaseTermsParentTerms(Query query, IndexSearcher indexSearcher,
LongTerms result = search(indexSearcher, query, aggregationBuilder, fieldType, subFieldType);
verify.accept(result);
}

@Override
protected List<SearchPlugin> getSearchPlugins() {
return Collections.singletonList(new ParentJoinPlugin());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@
import org.elasticsearch.index.mapper.NumberFieldMapper;
import org.elasticsearch.index.mapper.Uid;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.join.ParentJoinPlugin;
import org.elasticsearch.join.mapper.MetaJoinFieldMapper;
import org.elasticsearch.join.mapper.ParentJoinFieldMapper;
import org.elasticsearch.plugins.SearchPlugin;
import org.elasticsearch.search.aggregations.AggregatorTestCase;
import org.elasticsearch.search.aggregations.metrics.InternalMin;
import org.elasticsearch.search.aggregations.metrics.MinAggregationBuilder;
Expand Down Expand Up @@ -187,4 +189,9 @@ private void testCase(Query query, IndexSearcher indexSearcher, Consumer<Interna
InternalChildren result = search(indexSearcher, query, aggregationBuilder, fieldType);
verify.accept(result);
}

@Override
protected List<SearchPlugin> getSearchPlugins() {
return Collections.singletonList(new ParentJoinPlugin());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,9 @@
- match: { hits.total: 1 }
- match: { hits.hits.0._id: q3 }

---
"Verify nodes usage works":
- do:
nodes.usage: {}
- is_true: nodes
- match: { _nodes.failed: 0 }
Loading

0 comments on commit d0fdc5c

Please sign in to comment.