Counts usage of the aggs and exposes them on the _nodes/usage/. Closes #53746
This commit is contained in:
parent
3dada1e2d3
commit
d8f9df771d
|
@ -45,7 +45,7 @@ public class TransformConfigTests extends AbstractXContentTestCase<TransformConf
|
|||
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(),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -41,14 +45,7 @@ public class NodeRestUsageIT extends ESRestTestCase {
|
|||
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());
|
||||
|
@ -98,14 +95,7 @@ public class NodeRestUsageIT extends ESRestTestCase {
|
|||
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());
|
||||
|
@ -143,4 +133,97 @@ public class NodeRestUsageIT extends ESRestTestCase {
|
|||
+ "\"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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
--
|
||||
|
||||
|
@ -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": {
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
|
|
|
@ -30,10 +30,13 @@ import org.apache.lucene.store.Directory;
|
|||
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 {
|
||||
|
||||
|
@ -136,4 +139,8 @@ public class MatrixStatsAggregatorTests extends AggregatorTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<SearchPlugin> getSearchPlugins() {
|
||||
return Collections.singletonList(new MatrixAggregationPlugin());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ import org.elasticsearch.search.internal.SearchContext;
|
|||
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;
|
||||
|
@ -84,4 +86,10 @@ public class ChildrenAggregatorFactory extends ValuesSourceAggregatorFactory {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ import org.elasticsearch.search.internal.SearchContext;
|
|||
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;
|
||||
|
@ -85,4 +87,10 @@ public class ParentAggregatorFactory extends ValuesSourceAggregatorFactory {
|
|||
return asMultiBucketAggregator(this, searchContext, children);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatsSubtype() {
|
||||
// Parent Aggregation is registered in non-standard way
|
||||
return OTHER_SUBTYPE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,10 @@ import org.elasticsearch.index.mapper.MapperService;
|
|||
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;
|
||||
|
@ -328,4 +330,9 @@ public class ChildrenToParentAggregatorTests extends AggregatorTestCase {
|
|||
LongTerms result = search(indexSearcher, query, aggregationBuilder, fieldType, subFieldType);
|
||||
verify.accept(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<SearchPlugin> getSearchPlugins() {
|
||||
return Collections.singletonList(new ParentJoinPlugin());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,10 @@ import org.elasticsearch.index.mapper.MapperService;
|
|||
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;
|
||||
|
@ -187,4 +189,9 @@ public class ParentToChildrenAggregatorTests extends AggregatorTestCase {
|
|||
InternalChildren result = search(indexSearcher, query, aggregationBuilder, fieldType);
|
||||
verify.accept(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<SearchPlugin> getSearchPlugins() {
|
||||
return Collections.singletonList(new ParentJoinPlugin());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.action.admin.cluster.node.usage;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.support.nodes.BaseNodeResponse;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
|
@ -31,15 +32,22 @@ import java.util.Map;
|
|||
|
||||
public class NodeUsage extends BaseNodeResponse implements ToXContentFragment {
|
||||
|
||||
private long timestamp;
|
||||
private long sinceTime;
|
||||
private Map<String, Long> restUsage;
|
||||
private final long timestamp;
|
||||
private final long sinceTime;
|
||||
private final Map<String, Long> restUsage;
|
||||
private final Map<String, Object> aggregationUsage;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public NodeUsage(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
timestamp = in.readLong();
|
||||
sinceTime = in.readLong();
|
||||
restUsage = (Map<String, Long>) in.readGenericValue();
|
||||
if (in.getVersion().onOrAfter(Version.V_7_8_0)) {
|
||||
aggregationUsage = (Map<String, Object>) in.readGenericValue();
|
||||
} else {
|
||||
aggregationUsage = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,11 +62,13 @@ public class NodeUsage extends BaseNodeResponse implements ToXContentFragment {
|
|||
* a map containing the counts of the number of times each REST
|
||||
* endpoint has been called
|
||||
*/
|
||||
public NodeUsage(DiscoveryNode node, long timestamp, long sinceTime, Map<String, Long> restUsage) {
|
||||
public NodeUsage(DiscoveryNode node, long timestamp, long sinceTime, Map<String, Long> restUsage,
|
||||
Map<String, Object> aggregationUsage) {
|
||||
super(node);
|
||||
this.timestamp = timestamp;
|
||||
this.sinceTime = sinceTime;
|
||||
this.restUsage = restUsage;
|
||||
this.aggregationUsage = aggregationUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,6 +93,14 @@ public class NodeUsage extends BaseNodeResponse implements ToXContentFragment {
|
|||
return restUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a map containing the counts of the number of times each REST
|
||||
* endpoint has been called
|
||||
*/
|
||||
public Map<String, Object> getAggregationUsage() {
|
||||
return aggregationUsage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.field("since", sinceTime);
|
||||
|
@ -90,6 +108,10 @@ public class NodeUsage extends BaseNodeResponse implements ToXContentFragment {
|
|||
builder.field("rest_actions");
|
||||
builder.map(restUsage);
|
||||
}
|
||||
if (aggregationUsage != null) {
|
||||
builder.field("aggregations");
|
||||
builder.map(aggregationUsage);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
@ -99,6 +121,9 @@ public class NodeUsage extends BaseNodeResponse implements ToXContentFragment {
|
|||
out.writeLong(timestamp);
|
||||
out.writeLong(sinceTime);
|
||||
out.writeGenericValue(restUsage);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_8_0)) {
|
||||
out.writeGenericValue(aggregationUsage);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.action.admin.cluster.node.usage;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.support.nodes.BaseNodesRequest;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
@ -28,10 +29,14 @@ import java.io.IOException;
|
|||
public class NodesUsageRequest extends BaseNodesRequest<NodesUsageRequest> {
|
||||
|
||||
private boolean restActions;
|
||||
private boolean aggregations;
|
||||
|
||||
public NodesUsageRequest(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
this.restActions = in.readBoolean();
|
||||
if (in.getVersion().onOrAfter(Version.V_7_8_0)) {
|
||||
this.aggregations = in.readBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,6 +52,7 @@ public class NodesUsageRequest extends BaseNodesRequest<NodesUsageRequest> {
|
|||
*/
|
||||
public NodesUsageRequest all() {
|
||||
this.restActions = true;
|
||||
this.aggregations = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -73,9 +79,28 @@ public class NodesUsageRequest extends BaseNodesRequest<NodesUsageRequest> {
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should the node rest actions usage statistics be returned.
|
||||
*/
|
||||
public boolean aggregations() {
|
||||
return this.aggregations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the node rest actions usage statistics be returned.
|
||||
*/
|
||||
public NodesUsageRequest aggregations(boolean aggregations) {
|
||||
this.aggregations = aggregations;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeBoolean(restActions);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_8_0)) {
|
||||
out.writeBoolean(aggregations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,24 +27,31 @@ import org.elasticsearch.cluster.service.ClusterService;
|
|||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.search.aggregations.support.AggregationUsageService;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.usage.UsageService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TransportNodesUsageAction
|
||||
extends TransportNodesAction<NodesUsageRequest, NodesUsageResponse, TransportNodesUsageAction.NodeUsageRequest, NodeUsage> {
|
||||
|
||||
private UsageService usageService;
|
||||
private final UsageService restUsageService;
|
||||
private final AggregationUsageService aggregationUsageService;
|
||||
private final long sinceTime;
|
||||
|
||||
@Inject
|
||||
public TransportNodesUsageAction(ThreadPool threadPool, ClusterService clusterService, TransportService transportService,
|
||||
ActionFilters actionFilters, UsageService usageService) {
|
||||
ActionFilters actionFilters, UsageService restUsageService,
|
||||
AggregationUsageService aggregationUsageService) {
|
||||
super(NodesUsageAction.NAME, threadPool, clusterService, transportService, actionFilters,
|
||||
NodesUsageRequest::new, NodeUsageRequest::new, ThreadPool.Names.MANAGEMENT, NodeUsage.class);
|
||||
this.usageService = usageService;
|
||||
this.restUsageService = restUsageService;
|
||||
this.aggregationUsageService = aggregationUsageService;
|
||||
this.sinceTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,7 +72,9 @@ public class TransportNodesUsageAction
|
|||
@Override
|
||||
protected NodeUsage nodeOperation(NodeUsageRequest nodeUsageRequest) {
|
||||
NodesUsageRequest request = nodeUsageRequest.request;
|
||||
return usageService.getUsageStats(clusterService.localNode(), request.restActions());
|
||||
Map<String, Long> restUsage = request.restActions() ? restUsageService.getRestUsageStats() : null;
|
||||
Map<String, Object> aggsUsage = request.aggregations() ? aggregationUsageService.getUsageStats() : null;
|
||||
return new NodeUsage(clusterService.localNode(), System.currentTimeMillis(), sinceTime, restUsage, aggsUsage);
|
||||
}
|
||||
|
||||
public static class NodeUsageRequest extends BaseNodeRequest {
|
||||
|
|
|
@ -58,6 +58,7 @@ import org.elasticsearch.script.Script;
|
|||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.ScriptFactory;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.search.aggregations.support.AggregationUsageService;
|
||||
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
|
||||
import org.elasticsearch.search.lookup.SearchLookup;
|
||||
import org.elasticsearch.transport.RemoteClusterAware;
|
||||
|
@ -116,7 +117,7 @@ public class QueryShardContext extends QueryRewriteContext {
|
|||
private boolean allowUnmappedFields;
|
||||
private boolean mapUnmappedFieldAsString;
|
||||
private NestedScope nestedScope;
|
||||
private ValuesSourceRegistry valuesSourceRegistry;
|
||||
private final ValuesSourceRegistry valuesSourceRegistry;
|
||||
|
||||
public QueryShardContext(int shardId,
|
||||
IndexSettings indexSettings,
|
||||
|
@ -504,4 +505,8 @@ public class QueryShardContext extends QueryRewriteContext {
|
|||
public BitsetFilterCache getBitsetFilterCache() {
|
||||
return bitsetFilterCache;
|
||||
}
|
||||
|
||||
public AggregationUsageService getUsageService() {
|
||||
return valuesSourceRegistry.getUsageService();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,6 +150,7 @@ import org.elasticsearch.script.ScriptModule;
|
|||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.search.SearchModule;
|
||||
import org.elasticsearch.search.SearchService;
|
||||
import org.elasticsearch.search.aggregations.support.AggregationUsageService;
|
||||
import org.elasticsearch.search.fetch.FetchPhase;
|
||||
import org.elasticsearch.snapshots.RestoreService;
|
||||
import org.elasticsearch.snapshots.SnapshotShardsService;
|
||||
|
@ -581,6 +582,7 @@ public class Node implements Closeable {
|
|||
b.bind(AnalysisRegistry.class).toInstance(analysisModule.getAnalysisRegistry());
|
||||
b.bind(IngestService.class).toInstance(ingestService);
|
||||
b.bind(UsageService.class).toInstance(usageService);
|
||||
b.bind(AggregationUsageService.class).toInstance(searchModule.getValuesSourceRegistry().getUsageService());
|
||||
b.bind(NamedWriteableRegistry.class).toInstance(namedWriteableRegistry);
|
||||
b.bind(MetadataUpgrader.class).toInstance(metadataUpgrader);
|
||||
b.bind(MetaStateService.class).toInstance(metaStateService);
|
||||
|
|
|
@ -68,6 +68,7 @@ public class RestNodesUsageAction extends BaseRestHandler {
|
|||
} else {
|
||||
nodesUsageRequest.clear();
|
||||
nodesUsageRequest.restActions(metrics.contains("rest_actions"));
|
||||
nodesUsageRequest.aggregations(metrics.contains("aggregations"));
|
||||
}
|
||||
|
||||
return channel -> client.admin().cluster().nodesUsage(nodesUsageRequest, new RestBuilderListener<NodesUsageResponse>(channel) {
|
||||
|
|
|
@ -307,14 +307,13 @@ public class SearchModule {
|
|||
private final Settings settings;
|
||||
private final List<NamedWriteableRegistry.Entry> namedWriteables = new ArrayList<>();
|
||||
private final List<NamedXContentRegistry.Entry> namedXContents = new ArrayList<>();
|
||||
private ValuesSourceRegistry valuesSourceRegistry;
|
||||
private final ValuesSourceRegistry valuesSourceRegistry;
|
||||
|
||||
/**
|
||||
* Constructs a new SearchModule object
|
||||
*
|
||||
* NOTE: This constructor should not be called in production unless an accurate {@link Settings} object is provided.
|
||||
* When constructed, a static flag is set in Lucene {@link BooleanQuery#setMaxClauseCount} according to the settings.
|
||||
*
|
||||
* @param settings Current settings
|
||||
* @param transportClient Is this being constructed in the TransportClient or not
|
||||
* @param plugins List of included {@link SearchPlugin} objects.
|
||||
|
@ -372,7 +371,8 @@ public class SearchModule {
|
|||
.addResultReader(InternalAvg::new)
|
||||
.setAggregatorRegistrar(AvgAggregationBuilder::registerAggregators), builder);
|
||||
registerAggregation(new AggregationSpec(WeightedAvgAggregationBuilder.NAME, WeightedAvgAggregationBuilder::new,
|
||||
WeightedAvgAggregationBuilder.PARSER).addResultReader(InternalWeightedAvg::new), builder);
|
||||
WeightedAvgAggregationBuilder.PARSER).addResultReader(InternalWeightedAvg::new)
|
||||
.setAggregatorRegistrar(WeightedAvgAggregationBuilder::registerUsage), builder);
|
||||
registerAggregation(new AggregationSpec(SumAggregationBuilder.NAME, SumAggregationBuilder::new, SumAggregationBuilder.PARSER)
|
||||
.addResultReader(InternalSum::new)
|
||||
.setAggregatorRegistrar(SumAggregationBuilder::registerAggregators), builder);
|
||||
|
@ -529,6 +529,10 @@ public class SearchModule {
|
|||
Consumer<ValuesSourceRegistry.Builder> register = spec.getAggregatorRegistrar();
|
||||
if (register != null) {
|
||||
register.accept(builder);
|
||||
} else {
|
||||
// Register is typically handling usage registration, but for the older aggregations that don't use register, we
|
||||
// have to register usage explicitly here.
|
||||
builder.registerUsage(spec.getName().getPreferredName());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@ public abstract class AbstractAggregationBuilder<AB extends AbstractAggregationB
|
|||
@Override
|
||||
public final AggregatorFactory build(QueryShardContext queryShardContext, AggregatorFactory parent) throws IOException {
|
||||
AggregatorFactory factory = doBuild(queryShardContext, parent, factoriesBuilder);
|
||||
queryShardContext.getUsageService().incAggregationUsage(getType(), factory.getStatsSubtype());
|
||||
return factory;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ import org.elasticsearch.search.internal.SearchContext.Lifetime;
|
|||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.search.aggregations.support.AggregationUsageService.OTHER_SUBTYPE;
|
||||
|
||||
public abstract class AggregatorFactory {
|
||||
|
||||
public static final class MultiBucketAggregatorWrapper extends Aggregator {
|
||||
|
@ -238,4 +240,13 @@ public abstract class AggregatorFactory {
|
|||
return new MultiBucketAggregatorWrapper(bigArrays, searchContext, parent, factory, first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the aggregation subtype for nodes usage stats.
|
||||
* <p>
|
||||
* It should match the types registered by calling {@linkplain org.elasticsearch.search.aggregations.support.AggregationUsageService}.
|
||||
* In other words, it should be ValueSourcesType for the VST aggregations OTHER_SUBTYPE for all other aggregations.
|
||||
*/
|
||||
public String getStatsSubtype() {
|
||||
return OTHER_SUBTYPE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ import org.elasticsearch.search.internal.SearchContext;
|
|||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.search.aggregations.support.AggregationUsageService.OTHER_SUBTYPE;
|
||||
|
||||
public class GeoDistanceRangeAggregatorFactory
|
||||
extends ValuesSourceAggregatorFactory {
|
||||
|
||||
|
@ -128,4 +130,9 @@ public class GeoDistanceRangeAggregatorFactory
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatsSubtype() {
|
||||
// GeoDistanceRangeAggregatorFactory doesn't register itself with ValuesSourceRegistry
|
||||
return OTHER_SUBTYPE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ import org.elasticsearch.search.internal.SearchContext;
|
|||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.search.aggregations.support.AggregationUsageService.OTHER_SUBTYPE;
|
||||
|
||||
public class DiversifiedAggregatorFactory extends ValuesSourceAggregatorFactory {
|
||||
|
||||
private final int shardSize;
|
||||
|
@ -98,4 +100,9 @@ public class DiversifiedAggregatorFactory extends ValuesSourceAggregatorFactory
|
|||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatsSubtype() {
|
||||
// DiversifiedAggregatorFactory doesn't register itself with ValuesSourceRegistry
|
||||
return OTHER_SUBTYPE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.elasticsearch.search.aggregations.support.MultiValuesSourceFieldConfi
|
|||
import org.elasticsearch.search.aggregations.support.MultiValuesSourceParseHelper;
|
||||
import org.elasticsearch.search.aggregations.support.ValueType;
|
||||
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;
|
||||
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
|
||||
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -57,6 +58,10 @@ public class WeightedAvgAggregationBuilder extends MultiValuesSourceAggregationB
|
|||
MultiValuesSourceParseHelper.declareField(WEIGHT_FIELD.getPreferredName(), PARSER, true, false, false);
|
||||
}
|
||||
|
||||
public static void registerUsage(ValuesSourceRegistry.Builder builder) {
|
||||
builder.registerUsage(NAME, CoreValuesSourceType.NUMERIC);
|
||||
}
|
||||
|
||||
public WeightedAvgAggregationBuilder(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ import org.elasticsearch.search.internal.SearchContext;
|
|||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.search.aggregations.metrics.WeightedAvgAggregationBuilder.VALUE_FIELD;
|
||||
|
||||
class WeightedAvgAggregatorFactory extends MultiValuesSourceAggregatorFactory {
|
||||
|
||||
WeightedAvgAggregatorFactory(String name, Map<String, ValuesSourceConfig> configs,
|
||||
|
@ -62,4 +64,9 @@ class WeightedAvgAggregatorFactory extends MultiValuesSourceAggregatorFactory {
|
|||
}
|
||||
return new WeightedAvgAggregator(name, numericMultiVS, format, searchContext, parent, metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatsSubtype() {
|
||||
return configs.get(VALUE_FIELD.getPreferredName()).valueSourceType().typeName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.search.aggregations.support;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
public class AggregationUsageService {
|
||||
private final Map<String, Map<String, LongAdder>> aggs;
|
||||
|
||||
public static final String OTHER_SUBTYPE = "other";
|
||||
|
||||
public static class Builder {
|
||||
private final Map<String, Map<String, LongAdder>> aggs;
|
||||
|
||||
public Builder() {
|
||||
aggs = new HashMap<>();
|
||||
}
|
||||
|
||||
public void registerAggregationUsage(String aggregationName) {
|
||||
registerAggregationUsage(aggregationName, OTHER_SUBTYPE);
|
||||
}
|
||||
|
||||
public void registerAggregationUsage(String aggregationName, String valuesSourceType) {
|
||||
Map<String, LongAdder> subAgg = aggs.computeIfAbsent(aggregationName, k -> new HashMap<>());
|
||||
if (subAgg.put(valuesSourceType, new LongAdder()) != null) {
|
||||
throw new IllegalArgumentException("stats for aggregation [" + aggregationName + "][" + valuesSourceType +
|
||||
"] already registered");
|
||||
}
|
||||
}
|
||||
|
||||
public AggregationUsageService build() {
|
||||
return new AggregationUsageService(this);
|
||||
}
|
||||
}
|
||||
|
||||
private AggregationUsageService(Builder builder) {
|
||||
this.aggs = builder.aggs;
|
||||
}
|
||||
|
||||
public void incAggregationUsage(String aggregationName, String valuesSourceType) {
|
||||
Map<String, LongAdder> valuesSourceMap = aggs.get(aggregationName);
|
||||
// Not all aggs register their usage at the moment we also don't register them in test context
|
||||
if (valuesSourceMap != null) {
|
||||
LongAdder adder = valuesSourceMap.get(valuesSourceType);
|
||||
if (adder != null) {
|
||||
adder.increment();
|
||||
}
|
||||
assert adder != null : "Unknown subtype [" + aggregationName + "][" + valuesSourceType + "]";
|
||||
}
|
||||
assert valuesSourceMap != null : "Unknown aggregation [" + aggregationName + "][" + valuesSourceType + "]";
|
||||
}
|
||||
|
||||
public Map<String, Object> getUsageStats() {
|
||||
Map<String, Object> aggsUsageMap = new HashMap<>();
|
||||
aggs.forEach((name, agg) -> {
|
||||
Map<String, Long> aggUsageMap = new HashMap<>();
|
||||
agg.forEach((k, v) -> {
|
||||
long val = v.longValue();
|
||||
if (val > 0) {
|
||||
aggUsageMap.put(k, val);
|
||||
}
|
||||
});
|
||||
if (aggUsageMap.isEmpty() == false) {
|
||||
aggsUsageMap.put(name, aggUsageMap);
|
||||
}
|
||||
});
|
||||
return aggsUsageMap;
|
||||
}
|
||||
}
|
|
@ -288,6 +288,11 @@ public enum CoreValuesSourceType implements ValuesSourceType {
|
|||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return value();
|
||||
}
|
||||
|
||||
/** List containing all members of the enumeration. */
|
||||
public static List<ValuesSourceType> ALL_CORE = Arrays.asList(CoreValuesSourceType.values());
|
||||
}
|
||||
|
|
|
@ -59,4 +59,8 @@ public abstract class ValuesSourceAggregatorFactory extends AggregatorFactory {
|
|||
boolean collectsFromSingleBucket,
|
||||
Map<String, Object> metadata) throws IOException;
|
||||
|
||||
@Override
|
||||
public String getStatsSubtype() {
|
||||
return config.valueSourceType().typeName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,14 @@ import java.util.Map;
|
|||
*
|
||||
*/
|
||||
public class ValuesSourceRegistry {
|
||||
|
||||
public static class Builder {
|
||||
private final AggregationUsageService.Builder usageServiceBuilder;
|
||||
|
||||
public Builder() {
|
||||
this.usageServiceBuilder = new AggregationUsageService.Builder();
|
||||
}
|
||||
|
||||
private final Map<String, List<Map.Entry<ValuesSourceType, AggregatorSupplier>>> aggregatorRegistry = new HashMap<>();
|
||||
|
||||
/**
|
||||
|
@ -59,6 +66,7 @@ public class ValuesSourceRegistry {
|
|||
aggregatorRegistry.put(aggregationName, new ArrayList<>());
|
||||
}
|
||||
aggregatorRegistry.get(aggregationName).add(new AbstractMap.SimpleEntry<>(valuesSourceType, aggregatorSupplier));
|
||||
registerUsage(aggregationName, valuesSourceType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,18 +84,29 @@ public class ValuesSourceRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
public void registerUsage(String aggregationName, ValuesSourceType valuesSourceType) {
|
||||
usageServiceBuilder.registerAggregationUsage(aggregationName, valuesSourceType.typeName());
|
||||
}
|
||||
|
||||
public void registerUsage(String aggregationName) {
|
||||
usageServiceBuilder.registerAggregationUsage(aggregationName);
|
||||
}
|
||||
|
||||
public ValuesSourceRegistry build() {
|
||||
return new ValuesSourceRegistry(aggregatorRegistry);
|
||||
return new ValuesSourceRegistry(aggregatorRegistry, usageServiceBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
/** Maps Aggregation names to (ValuesSourceType, Supplier) pairs, keyed by ValuesSourceType */
|
||||
private final AggregationUsageService usageService;
|
||||
private Map<String, List<Map.Entry<ValuesSourceType, AggregatorSupplier>>> aggregatorRegistry;
|
||||
|
||||
public ValuesSourceRegistry(Map<String, List<Map.Entry<ValuesSourceType, AggregatorSupplier>>> aggregatorRegistry) {
|
||||
public ValuesSourceRegistry(Map<String, List<Map.Entry<ValuesSourceType, AggregatorSupplier>>> aggregatorRegistry,
|
||||
AggregationUsageService usageService) {
|
||||
Map<String, List<Map.Entry<ValuesSourceType, AggregatorSupplier>>> tmp = new HashMap<>();
|
||||
aggregatorRegistry.forEach((key, value) -> tmp.put(key, Collections.unmodifiableList(value)));
|
||||
this.aggregatorRegistry = Collections.unmodifiableMap(tmp);
|
||||
this.usageService = usageService;
|
||||
}
|
||||
|
||||
private AggregatorSupplier findMatchingSuppier(ValuesSourceType valuesSourceType,
|
||||
|
@ -114,7 +133,7 @@ public class ValuesSourceRegistry {
|
|||
|
||||
public ValuesSourceType getValuesSourceType(MappedFieldType fieldType, String aggregationName,
|
||||
// TODO: the following arguments are only needed for the legacy case
|
||||
IndexFieldData indexFieldData,
|
||||
IndexFieldData<?> indexFieldData,
|
||||
ValueType valueType,
|
||||
ValuesSourceType defaultValuesSourceType) {
|
||||
if (aggregationName != null && aggregatorRegistry.containsKey(aggregationName)) {
|
||||
|
@ -144,4 +163,8 @@ public class ValuesSourceRegistry {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AggregationUsageService getUsageService() {
|
||||
return usageService;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,4 +99,10 @@ public interface ValuesSourceType {
|
|||
default DocValueFormat getFormatter(String format, ZoneId tz) {
|
||||
return DocValueFormat.RAW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the Values Source Type for stats purposes
|
||||
* @return the name of the Values Source Type
|
||||
*/
|
||||
String typeName();
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
package org.elasticsearch.usage;
|
||||
|
||||
import org.elasticsearch.action.admin.cluster.node.usage.NodeUsage;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
@ -53,18 +52,15 @@ import java.util.Objects;
|
|||
public class UsageService {
|
||||
|
||||
private final Map<String, BaseRestHandler> handlers;
|
||||
private final long sinceTime;
|
||||
|
||||
public UsageService() {
|
||||
this.handlers = new HashMap<>();
|
||||
this.sinceTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a REST handler to this service.
|
||||
*
|
||||
* @param handler
|
||||
* the {@link BaseRestHandler} to add to the usage service.
|
||||
* @param handler the {@link BaseRestHandler} to add to the usage service.
|
||||
*/
|
||||
public void addRestHandler(BaseRestHandler handler) {
|
||||
Objects.requireNonNull(handler);
|
||||
|
@ -92,28 +88,19 @@ public class UsageService {
|
|||
/**
|
||||
* Get the current usage statistics for this node.
|
||||
*
|
||||
* @param localNode
|
||||
* the {@link DiscoveryNode} for this node
|
||||
* @param restActions
|
||||
* whether to include rest action usage in the returned
|
||||
* statistics
|
||||
* @return the {@link NodeUsage} representing the usage statistics for this
|
||||
* node
|
||||
* node
|
||||
*/
|
||||
public NodeUsage getUsageStats(DiscoveryNode localNode, boolean restActions) {
|
||||
public Map<String, Long> getRestUsageStats() {
|
||||
Map<String, Long> restUsageMap;
|
||||
if (restActions) {
|
||||
restUsageMap = new HashMap<>();
|
||||
handlers.values().forEach(handler -> {
|
||||
long usageCount = handler.getUsageCount();
|
||||
if (usageCount > 0) {
|
||||
restUsageMap.put(handler.getName(), usageCount);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
restUsageMap = null;
|
||||
}
|
||||
return new NodeUsage(localNode, System.currentTimeMillis(), sinceTime, restUsageMap);
|
||||
restUsageMap = new HashMap<>();
|
||||
handlers.values().forEach(handler -> {
|
||||
long usageCount = handler.getUsageCount();
|
||||
if (usageCount > 0) {
|
||||
restUsageMap.put(handler.getName(), usageCount);
|
||||
}
|
||||
});
|
||||
return restUsageMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ import org.elasticsearch.script.ScriptModule;
|
|||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.ScriptType;
|
||||
import org.elasticsearch.search.aggregations.AggregatorTestCase;
|
||||
import org.elasticsearch.search.aggregations.support.AggregationUsageService;
|
||||
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -50,6 +52,8 @@ import java.util.Map;
|
|||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ScriptedMetricAggregatorTests extends AggregatorTestCase {
|
||||
|
||||
|
@ -426,8 +430,12 @@ public class ScriptedMetricAggregatorTests extends AggregatorTestCase {
|
|||
MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME, SCRIPTS, Collections.emptyMap());
|
||||
Map<String, ScriptEngine> engines = Collections.singletonMap(scriptEngine.getType(), scriptEngine);
|
||||
ScriptService scriptService = new ScriptService(Settings.EMPTY, engines, ScriptModule.CORE_CONTEXTS);
|
||||
ValuesSourceRegistry valuesSourceRegistry = mock(ValuesSourceRegistry.class);
|
||||
AggregationUsageService.Builder builder = new AggregationUsageService.Builder();
|
||||
builder.registerAggregationUsage(ScriptedMetricAggregationBuilder.NAME);
|
||||
when(valuesSourceRegistry.getUsageService()).thenReturn(builder.build());
|
||||
return new QueryShardContext(0, indexSettings, BigArrays.NON_RECYCLING_INSTANCE, null,
|
||||
null, mapperService, null, scriptService, xContentRegistry(), writableRegistry(),
|
||||
null, null, System::currentTimeMillis, null, null, () -> true, null);
|
||||
null, null, System::currentTimeMillis, null, null, () -> true, valuesSourceRegistry);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,26 +19,21 @@
|
|||
|
||||
package org.elasticsearch.usage;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.admin.cluster.node.usage.NodeUsage;
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.search.aggregations.support.AggregationUsageService;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.rest.FakeRestRequest;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.search.aggregations.support.AggregationUsageService.OTHER_SUBTYPE;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
|
||||
public class UsageServiceTests extends ESTestCase {
|
||||
|
||||
|
@ -95,8 +90,6 @@ public class UsageServiceTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testRestUsage() throws Exception {
|
||||
DiscoveryNode discoveryNode = new DiscoveryNode("foo", new TransportAddress(InetAddress.getByName("localhost"), 12345),
|
||||
Version.CURRENT);
|
||||
RestRequest restRequest = new FakeRestRequest();
|
||||
BaseRestHandler handlerA = new MockRestHandler("a");
|
||||
BaseRestHandler handlerB = new MockRestHandler("b");
|
||||
|
@ -125,9 +118,7 @@ public class UsageServiceTests extends ESTestCase {
|
|||
handlerF.handleRequest(restRequest, null, null);
|
||||
handlerC.handleRequest(restRequest, null, null);
|
||||
handlerD.handleRequest(restRequest, null, null);
|
||||
NodeUsage usage = usageService.getUsageStats(discoveryNode, true);
|
||||
assertThat(usage.getNode(), sameInstance(discoveryNode));
|
||||
Map<String, Long> restUsage = usage.getRestUsage();
|
||||
Map<String, Long> restUsage = usageService.getRestUsageStats();
|
||||
assertThat(restUsage, notNullValue());
|
||||
assertThat(restUsage.size(), equalTo(6));
|
||||
assertThat(restUsage.get("a"), equalTo(4L));
|
||||
|
@ -136,10 +127,48 @@ public class UsageServiceTests extends ESTestCase {
|
|||
assertThat(restUsage.get("d"), equalTo(2L));
|
||||
assertThat(restUsage.get("e"), equalTo(1L));
|
||||
assertThat(restUsage.get("f"), equalTo(1L));
|
||||
}
|
||||
|
||||
usage = usageService.getUsageStats(discoveryNode, false);
|
||||
assertThat(usage.getNode(), sameInstance(discoveryNode));
|
||||
assertThat(usage.getRestUsage(), nullValue());
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testAggsUsage() throws Exception {
|
||||
AggregationUsageService.Builder builder = new AggregationUsageService.Builder();
|
||||
|
||||
builder.registerAggregationUsage("a", "x");
|
||||
builder.registerAggregationUsage("a", "y");
|
||||
builder.registerAggregationUsage("b", "x");
|
||||
builder.registerAggregationUsage("c");
|
||||
builder.registerAggregationUsage("b", "y");
|
||||
builder.registerAggregationUsage("a", "z");
|
||||
|
||||
AggregationUsageService usageService = builder.build();
|
||||
|
||||
usageService.incAggregationUsage("a", "x");
|
||||
for (int i = 0; i < 2; i++) {
|
||||
usageService.incAggregationUsage("a", "y");
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
usageService.incAggregationUsage("a", "z");
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
usageService.incAggregationUsage("b", "x");
|
||||
}
|
||||
for (int i = 0; i < 5; i++) {
|
||||
usageService.incAggregationUsage("b", "y");
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
usageService.incAggregationUsage("c", OTHER_SUBTYPE);
|
||||
}
|
||||
|
||||
|
||||
Map<String, Object> aggsUsage = usageService.getUsageStats();
|
||||
assertThat(aggsUsage, notNullValue());
|
||||
assertThat(aggsUsage.size(), equalTo(3));
|
||||
assertThat(((Map<String, Object>) aggsUsage.get("a")).get("x"), equalTo(1L));
|
||||
assertThat(((Map<String, Object>) aggsUsage.get("a")).get("y"), equalTo(2L));
|
||||
assertThat(((Map<String, Object>) aggsUsage.get("a")).get("z"), equalTo(3L));
|
||||
assertThat(((Map<String, Object>) aggsUsage.get("b")).get("x"), equalTo(4L));
|
||||
assertThat(((Map<String, Object>) aggsUsage.get("b")).get("y"), equalTo(5L));
|
||||
assertThat(((Map<String, Object>) aggsUsage.get("c")).get(OTHER_SUBTYPE), equalTo(6L));
|
||||
}
|
||||
|
||||
private class MockRestHandler extends BaseRestHandler {
|
||||
|
|
|
@ -109,6 +109,7 @@ public class AnalyticsPlugin extends Plugin implements SearchPlugin, ActionPlugi
|
|||
TTestAggregationBuilder::new,
|
||||
usage.track(AnalyticsStatsAction.Item.T_TEST, checkLicense(TTestAggregationBuilder.PARSER)))
|
||||
.addResultReader(InternalTTest::new)
|
||||
.setAggregatorRegistrar(TTestAggregationBuilder::registerUsage)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -56,4 +56,9 @@ public enum AnalyticsValuesSourceType implements ValuesSourceType {
|
|||
public String value() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return value();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.elasticsearch.search.aggregations.support.MultiValuesSourceFieldConfi
|
|||
import org.elasticsearch.search.aggregations.support.MultiValuesSourceParseHelper;
|
||||
import org.elasticsearch.search.aggregations.support.ValueType;
|
||||
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;
|
||||
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
|
||||
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -54,6 +55,10 @@ public class TTestAggregationBuilder extends MultiValuesSourceAggregationBuilder
|
|||
|
||||
private int tails = 2;
|
||||
|
||||
public static void registerUsage(ValuesSourceRegistry.Builder builder) {
|
||||
builder.registerUsage(NAME, CoreValuesSourceType.NUMERIC);
|
||||
}
|
||||
|
||||
public TTestAggregationBuilder(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ import org.elasticsearch.search.internal.SearchContext;
|
|||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.xpack.analytics.ttest.TTestAggregationBuilder.A_FIELD;
|
||||
|
||||
class TTestAggregatorFactory extends MultiValuesSourceAggregatorFactory {
|
||||
|
||||
private final TTestType testType;
|
||||
|
@ -118,4 +120,9 @@ class TTestAggregatorFactory extends MultiValuesSourceAggregatorFactory {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatsSubtype() {
|
||||
return configs.get(A_FIELD.getPreferredName()).valueSourceType().typeName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.elasticsearch.index.mapper.NumberFieldMapper;
|
|||
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
|
||||
import org.elasticsearch.index.mapper.TextFieldMapper;
|
||||
import org.elasticsearch.indices.breaker.CircuitBreakerService;
|
||||
import org.elasticsearch.plugins.SearchPlugin;
|
||||
import org.elasticsearch.script.MockScriptEngine;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.ScriptEngine;
|
||||
|
@ -64,9 +65,11 @@ import org.elasticsearch.search.sort.ScriptSortBuilder.ScriptSortType;
|
|||
import org.elasticsearch.search.sort.SortBuilder;
|
||||
import org.elasticsearch.search.sort.SortOrder;
|
||||
import org.elasticsearch.search.sort.SortValue;
|
||||
import org.elasticsearch.xpack.analytics.AnalyticsPlugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -377,7 +380,7 @@ public class TopMetricsAggregatorTests extends AggregatorTestCase {
|
|||
* BigArrays allocates the new array before freeing the old one.
|
||||
* That causes us to trip when we're about 2/3 of the way to the
|
||||
* limit. And 2/3 of 190 is 126. Which is pretty much what we
|
||||
* expect. Sort of.
|
||||
* expect. Sort of.
|
||||
*/
|
||||
int bucketThatBreaks = 646;
|
||||
for (int b = 0; b < bucketThatBreaks; b++) {
|
||||
|
@ -584,4 +587,9 @@ public class TopMetricsAggregatorTests extends AggregatorTestCase {
|
|||
Map<String, ScriptEngine> engines = singletonMap(scriptEngine.getType(), scriptEngine);
|
||||
return new ScriptService(Settings.EMPTY, engines, ScriptModule.CORE_CONTEXTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<SearchPlugin> getSearchPlugins() {
|
||||
return Collections.singletonList(new AnalyticsPlugin(Settings.EMPTY));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.elasticsearch.index.mapper.KeywordFieldMapper;
|
|||
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||
import org.elasticsearch.index.mapper.NumberFieldMapper;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.plugins.SearchPlugin;
|
||||
import org.elasticsearch.script.MockScriptEngine;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.ScriptEngine;
|
||||
|
@ -35,10 +36,12 @@ import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogram;
|
|||
import org.elasticsearch.search.aggregations.support.AggregationInspectionHelper;
|
||||
import org.elasticsearch.search.aggregations.support.MultiValuesSourceFieldConfig;
|
||||
import org.elasticsearch.search.lookup.LeafDocLookup;
|
||||
import org.elasticsearch.xpack.analytics.AnalyticsPlugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
@ -651,4 +654,9 @@ public class TTestAggregatorTests extends AggregatorTestCase {
|
|||
}
|
||||
testCase(aggregationBuilder, query, buildIndex, verify, fieldType1, fieldType2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<SearchPlugin> getSearchPlugins() {
|
||||
return Collections.singletonList(new AnalyticsPlugin(Settings.EMPTY));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,4 +54,4 @@ public abstract class AbstractSerializingTransformTestCase<T extends ToXContent
|
|||
return namedXContentRegistry;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public class MlDeprecationChecksTests extends ESTestCase {
|
|||
qs.put("use_dis_max", true);
|
||||
Map<String, Object> query = Collections.singletonMap("query_string", qs);
|
||||
deprecatedDatafeed.setQuery(query);
|
||||
|
||||
|
||||
DeprecationIssue issue = MlDeprecationChecks.checkDataFeedQuery(deprecatedDatafeed.build());
|
||||
assertNotNull(issue);
|
||||
assertThat(issue.getDetails(), equalTo("[Deprecated field [use_dis_max] used, replaced by [Set [tie_breaker] to 1 instead]]"));
|
||||
|
|
|
@ -115,6 +115,11 @@ public class GeoShapeValuesSourceType implements Writeable, ValuesSourceType {
|
|||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return "geoshape";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ public class SqlQueryRequestTests extends AbstractWireSerializingTestCase<SqlQue
|
|||
randomBoolean(), randomBoolean()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Writeable.Reader<SqlQueryRequest> instanceReader() {
|
||||
return SqlQueryRequest::new;
|
||||
|
@ -92,7 +92,7 @@ public class SqlQueryRequestTests extends AbstractWireSerializingTestCase<SqlQue
|
|||
mutator.accept(newRequest);
|
||||
return newRequest;
|
||||
}
|
||||
|
||||
|
||||
private AbstractSqlQueryRequest mutateRequestInfo(SqlQueryRequest oldRequest, SqlQueryRequest newRequest) {
|
||||
RequestInfo requestInfo = randomValueOtherThan(newRequest.requestInfo(), this::randomRequestInfo);
|
||||
newRequest.requestInfo(requestInfo);
|
||||
|
@ -106,10 +106,10 @@ public class SqlQueryRequestTests extends AbstractWireSerializingTestCase<SqlQue
|
|||
param.hasExplicitType(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return newRequest;
|
||||
}
|
||||
|
||||
|
||||
public void testFromXContent() throws IOException {
|
||||
xContentTester(this::createParser, this::createTestInstance, SqlQueryRequestTests::toXContent, this::doParseInstance)
|
||||
.numberOfTestRuns(NUMBER_OF_TEST_RUNS)
|
||||
|
@ -126,11 +126,11 @@ public class SqlQueryRequestTests extends AbstractWireSerializingTestCase<SqlQue
|
|||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> sqlQueryRequest.zoneId(null));
|
||||
assertEquals("time zone may not be null.", e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
private RequestInfo randomRequestInfo() {
|
||||
return new RequestInfo(randomFrom(Mode.values()), randomFrom(randomFrom(CLIENT_IDS), requestInfo.clientId()));
|
||||
}
|
||||
|
||||
|
||||
private TimeValue randomTV() {
|
||||
return TimeValue.parseTimeValue(randomTimeValue(), null, "test");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue