Skip to content

Commit

Permalink
[Enterprise Search] xpack/usage analytics collections count (#96063)
Browse files Browse the repository at this point in the history
* add analytics collections count to xpack usage

* get analytics collections similarly to search apps

* update EnterpriseSearchFeatureSetUsageSerializingTests

* add analytics coll. counts to usage test

* spotless apply

* add analytics collection count assertions to usage test

* use COUNT static constant in serializing test

* conditionally read/write analytics collections usage

---------

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
Sloane Perrault and elasticmachine authored May 18, 2023
1 parent d4b37bb commit e45158f
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,46 @@
import org.elasticsearch.xpack.core.XPackField;

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

public class EnterpriseSearchFeatureSetUsage extends XPackFeatureSet.Usage {

public static final String SEARCH_APPLICATIONS = "search_applications";
private final Map<String, Object> searchApplicationsStats;
public static final String ANALYTICS_COLLECTIONS = "analytics_collections";
public static final String COUNT = "count";
private final Map<String, Object> searchApplicationsUsage;
private final Map<String, Object> analyticsCollectionsUsage;

public EnterpriseSearchFeatureSetUsage(boolean available, boolean enabled, Map<String, Object> searchApplicationsStats) {
public EnterpriseSearchFeatureSetUsage(
boolean available,
boolean enabled,
Map<String, Object> searchApplicationsUsage,
Map<String, Object> analyticsCollectionsUsage
) {
super(XPackField.ENTERPRISE_SEARCH, available, enabled);
this.searchApplicationsStats = Objects.requireNonNull(searchApplicationsStats);
this.searchApplicationsUsage = Objects.requireNonNull(searchApplicationsUsage);
this.analyticsCollectionsUsage = Objects.requireNonNull(analyticsCollectionsUsage);
}

public EnterpriseSearchFeatureSetUsage(StreamInput in) throws IOException {
super(in);
this.searchApplicationsStats = in.readMap();
this.searchApplicationsUsage = in.readMap();
if (in.getTransportVersion().onOrAfter(TransportVersion.V_8_9_0)) {
this.analyticsCollectionsUsage = in.readMap();
} else {
this.analyticsCollectionsUsage = Collections.emptyMap();
}
}

@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeGenericMap(searchApplicationsStats);
out.writeGenericMap(searchApplicationsUsage);
if (out.getTransportVersion().onOrAfter(TransportVersion.V_8_9_0)) {
out.writeGenericMap(analyticsCollectionsUsage);
}
}

@Override
Expand All @@ -47,23 +65,29 @@ public TransportVersion getMinimalSupportedVersion() {
@Override
protected void innerXContent(XContentBuilder builder, Params params) throws IOException {
super.innerXContent(builder, params);
builder.field(SEARCH_APPLICATIONS, searchApplicationsStats);
builder.field(SEARCH_APPLICATIONS, searchApplicationsUsage);
builder.field(ANALYTICS_COLLECTIONS, analyticsCollectionsUsage);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EnterpriseSearchFeatureSetUsage that = (EnterpriseSearchFeatureSetUsage) o;
return Objects.equals(searchApplicationsStats, that.searchApplicationsStats);
return Objects.equals(searchApplicationsUsage, that.searchApplicationsUsage)
&& Objects.equals(analyticsCollectionsUsage, that.analyticsCollectionsUsage);
}

@Override
public int hashCode() {
return Objects.hash(searchApplicationsStats);
return Objects.hash(searchApplicationsUsage, analyticsCollectionsUsage);
}

public Map<String, Object> getSearchApplicationsStats() {
return searchApplicationsStats;
public Map<String, Object> getSearchApplicationsUsage() {
return searchApplicationsUsage;
}

public Map<String, Object> getAnalyticsCollectionsUsage() {
return analyticsCollectionsUsage;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,25 @@ public class EnterpriseSearchFeatureSetUsageSerializingTests extends AbstractWir
@Override
protected EnterpriseSearchFeatureSetUsage createTestInstance() {
Map<String, Object> searchApplicationsStats = new HashMap<>();
searchApplicationsStats.put("count", randomLongBetween(0, 100000));
return new EnterpriseSearchFeatureSetUsage(true, true, searchApplicationsStats);
Map<String, Object> analyticsCollectionsStats = new HashMap<>();
searchApplicationsStats.put(EnterpriseSearchFeatureSetUsage.COUNT, randomLongBetween(0, 100000));
analyticsCollectionsStats.put(EnterpriseSearchFeatureSetUsage.COUNT, randomLongBetween(0, 100000));
return new EnterpriseSearchFeatureSetUsage(true, true, searchApplicationsStats, analyticsCollectionsStats);
}

@Override
protected EnterpriseSearchFeatureSetUsage mutateInstance(EnterpriseSearchFeatureSetUsage instance) throws IOException {
long searchApplicationsCount = (long) instance.getSearchApplicationsStats().get("count");
long searchApplicationsCount = (long) instance.getSearchApplicationsUsage().get(EnterpriseSearchFeatureSetUsage.COUNT);
searchApplicationsCount = randomValueOtherThan(searchApplicationsCount, () -> randomLongBetween(0, 100000));
long analyticsCollectionsCount = (long) instance.getAnalyticsCollectionsUsage().get(EnterpriseSearchFeatureSetUsage.COUNT);
analyticsCollectionsCount = randomValueOtherThan(analyticsCollectionsCount, () -> randomLongBetween(0, 100000));

Map<String, Object> searchApplicationsStats = new HashMap<>();
Map<String, Object> analyticsCollectionsStats = new HashMap<>();
searchApplicationsStats.put("count", searchApplicationsCount);
analyticsCollectionsStats.put("count", analyticsCollectionsCount);

return new EnterpriseSearchFeatureSetUsage(true, true, searchApplicationsStats);
return new EnterpriseSearchFeatureSetUsage(true, true, searchApplicationsStats, analyticsCollectionsStats);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ teardown:
enterprise_search: {
enabled: true,
available: true,
search_applications: { count: 0 }
search_applications: { count: 0 },
analytics_collections: { count: 0 }
}
}

Expand All @@ -51,7 +52,8 @@ teardown:
enterprise_search: {
enabled: true,
available: true,
search_applications: { count: 1 }
search_applications: { count: 1 },
analytics_collections: { count: 0 }
}
}

Expand All @@ -61,14 +63,19 @@ teardown:
body:
indices: [ "test-index1" ]

- do:
search_application.put_behavioral_analytics:
name: test-analytics-collection

- do:
xpack.usage: {}

- match: {
enterprise_search: {
enabled: true,
available: true,
search_applications: { count: 2 }
search_applications: { count: 2 },
analytics_collections: { count: 1 }
}
}

Expand All @@ -83,6 +90,23 @@ teardown:
enterprise_search: {
enabled: true,
available: true,
search_applications: { count: 1 }
search_applications: { count: 1 },
analytics_collections: { count: 1 }
}
}

- do:
search_application.delete_behavioral_analytics:
name: test-analytics-collection

- do:
xpack.usage: {}

- match: {
enterprise_search: {
enabled: true,
available: true,
search_applications: { count: 1 },
analytics_collections: { count: 0 }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.application.analytics.action.GetAnalyticsCollectionAction;
import org.elasticsearch.xpack.application.search.action.ListSearchApplicationAction;
import org.elasticsearch.xpack.application.utils.LicenseUtils;
import org.elasticsearch.xpack.core.XPackSettings;
Expand All @@ -33,6 +34,7 @@
import org.elasticsearch.xpack.core.application.EnterpriseSearchFeatureSetUsage;

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

import static org.elasticsearch.xpack.core.ClientHelper.ENT_SEARCH_ORIGIN;
Expand Down Expand Up @@ -79,34 +81,83 @@ protected void masterOperation(
EnterpriseSearchFeatureSetUsage usage = new EnterpriseSearchFeatureSetUsage(
LicenseUtils.LICENSED_ENT_SEARCH_FEATURE.checkWithoutTracking(licenseState),
enabled,
Collections.emptyMap(),
Collections.emptyMap()
);
listener.onResponse(new XPackUsageFeatureResponse(usage));
return;
}

try {
ListSearchApplicationAction.Response resp = clientWithOrigin.execute(
ListSearchApplicationAction.INSTANCE,
new ListSearchApplicationAction.Request(null, new PageParams(0, 0))
).get();
Map<String, Object> searchApplicationsUsage = new HashMap<>();
Map<String, Object> analyticsCollectionsUsage = new HashMap<>();

// Step 2: Fetch search applications count and return usage
ListSearchApplicationAction.Request searchApplicationsCountRequest = new ListSearchApplicationAction.Request(
"*",
new PageParams(0, 0)
);
ActionListener<ListSearchApplicationAction.Response> searchApplicationsCountListener = ActionListener.wrap(response -> {
addSearchApplicationsUsage(response, searchApplicationsUsage);
listener.onResponse(
new XPackUsageFeatureResponse(
new EnterpriseSearchFeatureSetUsage(
enabled,
LicenseUtils.LICENSED_ENT_SEARCH_FEATURE.checkWithoutTracking(licenseState),
Map.of("count", resp.queryPage().count())
enabled,
searchApplicationsUsage,
analyticsCollectionsUsage
)
)
);
} catch (Exception e) {
logger.warn("Failed to get search application count to include in Enterprise Search usage", e);
EnterpriseSearchFeatureSetUsage usage = new EnterpriseSearchFeatureSetUsage(
LicenseUtils.LICENSED_ENT_SEARCH_FEATURE.checkWithoutTracking(licenseState),
enabled,
Collections.emptyMap()
}, e -> {
listener.onResponse(
new XPackUsageFeatureResponse(
new EnterpriseSearchFeatureSetUsage(
LicenseUtils.LICENSED_ENT_SEARCH_FEATURE.checkWithoutTracking(licenseState),
enabled,
Collections.emptyMap(),
analyticsCollectionsUsage
)
)
);
listener.onResponse(new XPackUsageFeatureResponse(usage));
}
});

// Step 1: Fetch analytics collections count
GetAnalyticsCollectionAction.Request analyticsCollectionsCountRequest = new GetAnalyticsCollectionAction.Request(
new String[] { "*" }
);
ActionListener<GetAnalyticsCollectionAction.Response> analyticsCollectionsCountListener = ActionListener.wrap(response -> {
addAnalyticsCollectionsUsage(response, analyticsCollectionsUsage);
clientWithOrigin.execute(ListSearchApplicationAction.INSTANCE, searchApplicationsCountRequest, searchApplicationsCountListener);
},
e -> {
clientWithOrigin.execute(
ListSearchApplicationAction.INSTANCE,
searchApplicationsCountRequest,
searchApplicationsCountListener
);
}
);

// Step 0: Kick off requests
clientWithOrigin.execute(
GetAnalyticsCollectionAction.INSTANCE,
analyticsCollectionsCountRequest,
analyticsCollectionsCountListener
);
}

private void addSearchApplicationsUsage(ListSearchApplicationAction.Response response, Map<String, Object> searchApplicationsUsage) {
long count = response.queryPage().count();

searchApplicationsUsage.put(EnterpriseSearchFeatureSetUsage.COUNT, count);
}

private void addAnalyticsCollectionsUsage(
GetAnalyticsCollectionAction.Response response,
Map<String, Object> analyticsCollectionsUsage
) {
long count = response.getAnalyticsCollections().size();

analyticsCollectionsUsage.put(EnterpriseSearchFeatureSetUsage.COUNT, count);
}
}

0 comments on commit e45158f

Please sign in to comment.