From 0d19ba16f6c88fa81d6bb52f94442f1c4c1b1c32 Mon Sep 17 00:00:00 2001 From: Sloane Perrault Date: Thu, 1 Jun 2023 14:05:23 -0400 Subject: [PATCH] [8.8] [Enterprise Search] xpack/usage analytics collections count (#96063) (#96495) * [Enterprise Search] xpack/usage analytics collections count (#96063) * 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 (cherry picked from commit e45158f501d43366d40087cf58305edd715688c4) * lower version for analytics usage to 8.8.1 --- .../EnterpriseSearchFeatureSetUsage.java | 44 +++++++--- ...SearchFeatureSetUsageSerializingTests.java | 14 +++- .../test/entsearch/100_usage.yml | 32 +++++++- .../EnterpriseSearchUsageTransportAction.java | 81 +++++++++++++++---- 4 files changed, 138 insertions(+), 33 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/application/EnterpriseSearchFeatureSetUsage.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/application/EnterpriseSearchFeatureSetUsage.java index d58365e5a9fc8..1d8332eec01cc 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/application/EnterpriseSearchFeatureSetUsage.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/application/EnterpriseSearchFeatureSetUsage.java @@ -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 searchApplicationsStats; + public static final String ANALYTICS_COLLECTIONS = "analytics_collections"; + public static final String COUNT = "count"; + private final Map searchApplicationsUsage; + private final Map analyticsCollectionsUsage; - public EnterpriseSearchFeatureSetUsage(boolean available, boolean enabled, Map searchApplicationsStats) { + public EnterpriseSearchFeatureSetUsage( + boolean available, + boolean enabled, + Map searchApplicationsUsage, + Map 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_8_1)) { + 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_8_1)) { + out.writeGenericMap(analyticsCollectionsUsage); + } } @Override @@ -47,7 +65,8 @@ 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 @@ -55,15 +74,20 @@ 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 getSearchApplicationsStats() { - return searchApplicationsStats; + public Map getSearchApplicationsUsage() { + return searchApplicationsUsage; + } + + public Map getAnalyticsCollectionsUsage() { + return analyticsCollectionsUsage; } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/application/EnterpriseSearchFeatureSetUsageSerializingTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/application/EnterpriseSearchFeatureSetUsageSerializingTests.java index 3eb9e3d099f93..036c83212dbbd 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/application/EnterpriseSearchFeatureSetUsageSerializingTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/application/EnterpriseSearchFeatureSetUsageSerializingTests.java @@ -19,19 +19,25 @@ public class EnterpriseSearchFeatureSetUsageSerializingTests extends AbstractWir @Override protected EnterpriseSearchFeatureSetUsage createTestInstance() { Map searchApplicationsStats = new HashMap<>(); - searchApplicationsStats.put("count", randomLongBetween(0, 100000)); - return new EnterpriseSearchFeatureSetUsage(true, true, searchApplicationsStats); + Map 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 searchApplicationsStats = new HashMap<>(); + Map 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 diff --git a/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/100_usage.yml b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/100_usage.yml index 636452cb55899..a1ad336b343ea 100644 --- a/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/100_usage.yml +++ b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/100_usage.yml @@ -34,7 +34,8 @@ teardown: enterprise_search: { enabled: true, available: true, - search_applications: { count: 0 } + search_applications: { count: 0 }, + analytics_collections: { count: 0 } } } @@ -51,7 +52,8 @@ teardown: enterprise_search: { enabled: true, available: true, - search_applications: { count: 1 } + search_applications: { count: 1 }, + analytics_collections: { count: 0 } } } @@ -61,6 +63,10 @@ teardown: body: indices: [ "test-index1" ] + - do: + search_application.put_behavioral_analytics: + name: test-analytics-collection + - do: xpack.usage: {} @@ -68,7 +74,8 @@ teardown: enterprise_search: { enabled: true, available: true, - search_applications: { count: 2 } + search_applications: { count: 2 }, + analytics_collections: { count: 1 } } } @@ -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 } } } diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearchUsageTransportAction.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearchUsageTransportAction.java index 303bd50f4dfe9..485beb8ba5945 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearchUsageTransportAction.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearchUsageTransportAction.java @@ -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; @@ -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; @@ -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 searchApplicationsUsage = new HashMap<>(); + Map analyticsCollectionsUsage = new HashMap<>(); + + // Step 2: Fetch search applications count and return usage + ListSearchApplicationAction.Request searchApplicationsCountRequest = new ListSearchApplicationAction.Request( + "*", + new PageParams(0, 0) + ); + ActionListener 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 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 searchApplicationsUsage) { + long count = response.queryPage().count(); + + searchApplicationsUsage.put(EnterpriseSearchFeatureSetUsage.COUNT, count); + } + + private void addAnalyticsCollectionsUsage( + GetAnalyticsCollectionAction.Response response, + Map analyticsCollectionsUsage + ) { + long count = response.getAnalyticsCollections().size(); + + analyticsCollectionsUsage.put(EnterpriseSearchFeatureSetUsage.COUNT, count); } }