Skip to content

Commit 34f4afd

Browse files
authored
Add xpack usage stats for the spatial plugin (#61946) (#65391)
This commit leverages the analytics plugin's aggregation usage framework and adopts it to the spatial plugin. This is just a skeleton, as there are no aggregations to be tracked just yet, but future ones will be introduced and their usage should be tracked here.
1 parent dcec746 commit 34f4afd

File tree

16 files changed

+476
-20
lines changed

16 files changed

+476
-20
lines changed

x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/AnalyticsUsage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import org.elasticsearch.cluster.node.DiscoveryNode;
1010
import org.elasticsearch.common.xcontent.ContextParser;
11-
import org.elasticsearch.xpack.core.analytics.EnumCounters;
11+
import org.elasticsearch.xpack.core.common.stats.EnumCounters;
1212
import org.elasticsearch.xpack.core.analytics.action.AnalyticsStatsAction;
1313

1414
/**

x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/action/AnalyticsStatsActionNodeResponseTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import org.elasticsearch.cluster.node.DiscoveryNode;
1111
import org.elasticsearch.common.io.stream.Writeable;
1212
import org.elasticsearch.test.AbstractWireSerializingTestCase;
13-
import org.elasticsearch.xpack.core.analytics.EnumCounters;
13+
import org.elasticsearch.xpack.core.common.stats.EnumCounters;
1414
import org.elasticsearch.xpack.core.analytics.action.AnalyticsStatsAction;
1515

1616
import static org.hamcrest.Matchers.equalTo;

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/analytics/action/AnalyticsStatsAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import org.elasticsearch.common.io.stream.Writeable;
2020
import org.elasticsearch.common.xcontent.ToXContentObject;
2121
import org.elasticsearch.common.xcontent.XContentBuilder;
22-
import org.elasticsearch.xpack.core.analytics.EnumCounters;
22+
import org.elasticsearch.xpack.core.common.stats.EnumCounters;
2323

2424
import java.io.IOException;
2525
import java.util.List;

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/analytics/EnumCounters.java renamed to x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/stats/EnumCounters.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
package org.elasticsearch.xpack.core.analytics;
7+
package org.elasticsearch.xpack.core.common.stats;
88

99
import org.elasticsearch.common.io.stream.StreamInput;
1010
import org.elasticsearch.common.io.stream.StreamOutput;

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/spatial/SpatialFeatureSetUsage.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,49 @@
1111
import org.elasticsearch.common.io.stream.StreamOutput;
1212
import org.elasticsearch.xpack.core.XPackFeatureSet;
1313
import org.elasticsearch.xpack.core.XPackField;
14+
import org.elasticsearch.xpack.core.spatial.action.SpatialStatsAction;
1415

1516
import java.io.IOException;
1617
import java.util.Objects;
1718

1819
public class SpatialFeatureSetUsage extends XPackFeatureSet.Usage {
1920

20-
public SpatialFeatureSetUsage(boolean available, boolean enabled) {
21+
private final SpatialStatsAction.Response statsResponse;
22+
23+
public SpatialFeatureSetUsage(boolean available, boolean enabled, SpatialStatsAction.Response statsResponse) {
2124
super(XPackField.SPATIAL, available, enabled);
25+
this.statsResponse = statsResponse;
2226
}
2327

2428
public SpatialFeatureSetUsage(StreamInput input) throws IOException {
2529
super(input);
30+
if (input.getVersion().onOrAfter(Version.V_7_11_0)) {
31+
this.statsResponse = new SpatialStatsAction.Response(input);
32+
} else {
33+
this.statsResponse = null;
34+
}
2635
}
2736

2837
@Override
2938
public Version getMinimalSupportedVersion() {
3039
return Version.V_7_4_0;
3140
}
3241

42+
SpatialStatsAction.Response statsResponse() {
43+
return statsResponse;
44+
}
45+
3346
@Override
3447
public void writeTo(StreamOutput out) throws IOException {
3548
super.writeTo(out);
49+
if (out.getVersion().onOrAfter(Version.V_7_11_0)) {
50+
this.statsResponse.writeTo(out);
51+
}
3652
}
3753

3854
@Override
3955
public int hashCode() {
40-
return Objects.hash(available, enabled);
56+
return Objects.hash(available, enabled, statsResponse);
4157
}
4258

4359
@Override
@@ -49,7 +65,8 @@ public boolean equals(Object obj) {
4965
return false;
5066
}
5167
SpatialFeatureSetUsage other = (SpatialFeatureSetUsage) obj;
52-
return Objects.equals(available, other.available) &&
53-
Objects.equals(enabled, other.enabled);
68+
return Objects.equals(available, other.available)
69+
&& Objects.equals(enabled, other.enabled)
70+
&& Objects.equals(statsResponse, other.statsResponse);
5471
}
5572
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.core.spatial.action;
7+
8+
import org.elasticsearch.action.ActionType;
9+
import org.elasticsearch.action.FailedNodeException;
10+
import org.elasticsearch.action.support.nodes.BaseNodeRequest;
11+
import org.elasticsearch.action.support.nodes.BaseNodeResponse;
12+
import org.elasticsearch.action.support.nodes.BaseNodesRequest;
13+
import org.elasticsearch.action.support.nodes.BaseNodesResponse;
14+
import org.elasticsearch.cluster.ClusterName;
15+
import org.elasticsearch.cluster.node.DiscoveryNode;
16+
import org.elasticsearch.common.io.stream.StreamInput;
17+
import org.elasticsearch.common.io.stream.StreamOutput;
18+
import org.elasticsearch.common.io.stream.Writeable;
19+
import org.elasticsearch.common.xcontent.ToXContentObject;
20+
import org.elasticsearch.common.xcontent.XContentBuilder;
21+
import org.elasticsearch.xpack.core.common.stats.EnumCounters;
22+
23+
import java.io.IOException;
24+
import java.util.List;
25+
import java.util.Locale;
26+
import java.util.Objects;
27+
import java.util.stream.Collectors;
28+
29+
public class SpatialStatsAction extends ActionType<SpatialStatsAction.Response> {
30+
public static final SpatialStatsAction INSTANCE = new SpatialStatsAction();
31+
public static final String NAME = "cluster:monitor/xpack/spatial/stats";
32+
33+
private SpatialStatsAction() {
34+
super(NAME, Response::new);
35+
}
36+
37+
/**
38+
* Items to track. Serialized by ordinals. Append only, don't remove or change order of items in this list.
39+
*/
40+
public enum Item {
41+
}
42+
43+
public static class Request extends BaseNodesRequest<Request> implements ToXContentObject {
44+
45+
public Request() {
46+
super((String[]) null);
47+
}
48+
49+
public Request(StreamInput in) throws IOException {
50+
super(in);
51+
}
52+
53+
@Override
54+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
55+
builder.startObject();
56+
builder.endObject();
57+
return builder;
58+
}
59+
60+
@Override
61+
public int hashCode() {
62+
// Nothing to hash atm, so just use the action name
63+
return Objects.hashCode(NAME);
64+
}
65+
66+
@Override
67+
public boolean equals(Object obj) {
68+
if (obj == null) {
69+
return false;
70+
}
71+
if (getClass() != obj.getClass()) {
72+
return false;
73+
}
74+
return true;
75+
}
76+
}
77+
78+
public static class NodeRequest extends BaseNodeRequest {
79+
public NodeRequest(StreamInput in) throws IOException {
80+
super(in);
81+
}
82+
83+
public NodeRequest(Request request) {
84+
85+
}
86+
}
87+
88+
public static class Response extends BaseNodesResponse<NodeResponse> implements Writeable, ToXContentObject {
89+
public Response(StreamInput in) throws IOException {
90+
super(in);
91+
}
92+
93+
public Response(ClusterName clusterName, List<NodeResponse> nodes, List<FailedNodeException> failures) {
94+
super(clusterName, nodes, failures);
95+
}
96+
97+
@Override
98+
protected List<NodeResponse> readNodesFrom(StreamInput in) throws IOException {
99+
return in.readList(NodeResponse::new);
100+
}
101+
102+
@Override
103+
protected void writeNodesTo(StreamOutput out, List<NodeResponse> nodes) throws IOException {
104+
out.writeList(nodes);
105+
}
106+
107+
public EnumCounters<Item> getStats() {
108+
List<EnumCounters<Item>> countersPerNode = getNodes()
109+
.stream()
110+
.map(SpatialStatsAction.NodeResponse::getStats)
111+
.collect(Collectors.toList());
112+
return EnumCounters.merge(Item.class, countersPerNode);
113+
}
114+
115+
@Override
116+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
117+
EnumCounters<Item> stats = getStats();
118+
builder.startObject("stats");
119+
for (Item item : Item.values()) {
120+
builder.field(item.name().toLowerCase(Locale.ROOT) + "_usage", stats.get(item));
121+
}
122+
builder.endObject();
123+
return builder;
124+
}
125+
126+
@Override
127+
public int hashCode() {
128+
return Objects.hash(getStats());
129+
}
130+
131+
@Override
132+
public boolean equals(Object o) {
133+
if (this == o) return true;
134+
if (o == null || getClass() != o.getClass()) return false;
135+
Response other = (Response) o;
136+
return Objects.equals(getStats(), other.getStats());
137+
}
138+
}
139+
140+
public static class NodeResponse extends BaseNodeResponse {
141+
private final EnumCounters<Item> counters;
142+
143+
public NodeResponse(DiscoveryNode node, EnumCounters<Item> counters) {
144+
super(node);
145+
this.counters = counters;
146+
}
147+
148+
public NodeResponse(StreamInput in) throws IOException {
149+
super(in);
150+
counters = new EnumCounters<>(in, Item.class);
151+
}
152+
153+
@Override
154+
public void writeTo(StreamOutput out) throws IOException {
155+
super.writeTo(out);
156+
counters.writeTo(out);
157+
}
158+
159+
public EnumCounters<Item> getStats() {
160+
return counters;
161+
}
162+
163+
@Override
164+
public boolean equals(Object o) {
165+
if (this == o) return true;
166+
if (o == null || getClass() != o.getClass()) return false;
167+
NodeResponse that = (NodeResponse) o;
168+
return counters.equals(that.counters) &&
169+
getNode().equals(that.getNode());
170+
}
171+
172+
@Override
173+
public int hashCode() {
174+
return Objects.hash(counters, getNode());
175+
}
176+
}
177+
}

x-pack/plugin/core/src/test/java/org/elasticsearch/analytics/EnumCountersTests.java renamed to x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/common/stats/EnumCountersTests.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
package org.elasticsearch.analytics;
7+
package org.elasticsearch.xpack.core.common.stats;
88

99
import org.elasticsearch.Version;
1010
import org.elasticsearch.common.io.stream.BytesStreamOutput;
1111
import org.elasticsearch.common.io.stream.StreamInput;
1212
import org.elasticsearch.common.io.stream.Writeable;
1313
import org.elasticsearch.test.AbstractWireTestCase;
14-
import org.elasticsearch.xpack.core.analytics.EnumCounters;
1514

1615
import java.io.IOException;
1716
import java.util.Map;

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/spatial/SpatialFeatureSetUsageTests.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,36 @@
55
*/
66
package org.elasticsearch.xpack.core.spatial;
77

8+
import org.elasticsearch.Version;
9+
import org.elasticsearch.cluster.ClusterName;
10+
import org.elasticsearch.cluster.node.DiscoveryNode;
811
import org.elasticsearch.common.io.stream.Writeable;
12+
import org.elasticsearch.common.transport.TransportAddress;
913
import org.elasticsearch.test.AbstractWireSerializingTestCase;
14+
import org.elasticsearch.xpack.core.common.stats.EnumCounters;
15+
import org.elasticsearch.xpack.core.spatial.action.SpatialStatsAction;
1016

1117
import java.io.IOException;
18+
import java.net.InetAddress;
19+
20+
import static java.util.Collections.emptyList;
21+
import static java.util.Collections.singletonList;
1222

1323
public class SpatialFeatureSetUsageTests extends AbstractWireSerializingTestCase<SpatialFeatureSetUsage> {
1424

1525
@Override
1626
protected SpatialFeatureSetUsage createTestInstance() {
1727
boolean available = randomBoolean();
1828
boolean enabled = randomBoolean();
19-
return new SpatialFeatureSetUsage(available, enabled);
29+
SpatialStatsAction.Response statsResponse = randomStatsResponse();
30+
return new SpatialFeatureSetUsage(available, enabled, statsResponse);
2031
}
2132

2233
@Override
2334
protected SpatialFeatureSetUsage mutateInstance(SpatialFeatureSetUsage instance) throws IOException {
2435
boolean available = instance.available();
2536
boolean enabled = instance.enabled();
37+
SpatialStatsAction.Response statsResponse = instance.statsResponse();
2638
switch (between(0, 1)) {
2739
case 0:
2840
available = available == false;
@@ -33,12 +45,19 @@ protected SpatialFeatureSetUsage mutateInstance(SpatialFeatureSetUsage instance)
3345
default:
3446
throw new AssertionError("Illegal randomisation branch");
3547
}
36-
return new SpatialFeatureSetUsage(available, enabled);
48+
return new SpatialFeatureSetUsage(available, enabled, statsResponse);
3749
}
3850

3951
@Override
4052
protected Writeable.Reader<SpatialFeatureSetUsage> instanceReader() {
4153
return SpatialFeatureSetUsage::new;
4254
}
4355

56+
public static SpatialStatsAction.Response randomStatsResponse() {
57+
DiscoveryNode node = new DiscoveryNode("_node_id",
58+
new TransportAddress(InetAddress.getLoopbackAddress(), 9300), Version.CURRENT);
59+
EnumCounters<SpatialStatsAction.Item> counters = new EnumCounters<>(SpatialStatsAction.Item.class);
60+
SpatialStatsAction.NodeResponse nodeResponse = new SpatialStatsAction.NodeResponse(node, counters);
61+
return new SpatialStatsAction.Response(new ClusterName("cluster_name"), singletonList(nodeResponse), emptyList());
62+
}
4463
}

x-pack/plugin/spatial/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dependencies {
2020
restResources {
2121
restApi {
2222
includeCore '_common', 'bulk', 'indices', 'index', 'search'
23+
includeXpack 'xpack'
2324
}
2425
restTests {
2526
includeCore 'geo_shape'

x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/SpatialFeatureSet.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,26 @@
66
package org.elasticsearch.xpack.spatial;
77

88
import org.elasticsearch.action.ActionListener;
9+
import org.elasticsearch.client.Client;
910
import org.elasticsearch.common.Nullable;
1011
import org.elasticsearch.common.inject.Inject;
11-
import org.elasticsearch.common.settings.Settings;
1212
import org.elasticsearch.license.XPackLicenseState;
1313
import org.elasticsearch.xpack.core.XPackFeatureSet;
1414
import org.elasticsearch.xpack.core.XPackField;
1515
import org.elasticsearch.xpack.core.spatial.SpatialFeatureSetUsage;
16+
import org.elasticsearch.xpack.core.spatial.action.SpatialStatsAction;
1617

1718
import java.util.Map;
1819

1920
public class SpatialFeatureSet implements XPackFeatureSet {
21+
2022
private final XPackLicenseState licenseState;
23+
private Client client;
2124

2225
@Inject
23-
public SpatialFeatureSet(Settings settings, @Nullable XPackLicenseState licenseState) {
26+
public SpatialFeatureSet(@Nullable XPackLicenseState licenseState, Client client) {
2427
this.licenseState = licenseState;
28+
this.client = client;
2529
}
2630

2731
@Override
@@ -46,6 +50,9 @@ public Map<String, Object> nativeCodeInfo() {
4650

4751
@Override
4852
public void usage(ActionListener<XPackFeatureSet.Usage> listener) {
49-
listener.onResponse(new SpatialFeatureSetUsage(available(), enabled()));
53+
SpatialStatsAction.Request request = new SpatialStatsAction.Request();
54+
client.execute(SpatialStatsAction.INSTANCE, request,
55+
ActionListener.wrap(r -> listener.onResponse(new SpatialFeatureSetUsage(available(), enabled(), r)),
56+
listener::onFailure));
5057
}
5158
}

0 commit comments

Comments
 (0)