Add basic searchable snapshots usage information (#58828) (#59160)

Adds super basic usage information for searchable snapshots, to be extended later.

Backport of #58828
This commit is contained in:
Yannick Welsch 2020-07-08 13:09:29 +02:00 committed by GitHub
parent a6109063a2
commit 0b9eb210b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 321 additions and 4 deletions

View File

@ -111,6 +111,10 @@ Example response:
"available": true, "available": true,
"enabled": true "enabled": true
}, },
"searchable_snapshots" : {
"available" : true,
"enabled" : true
},
"security" : { "security" : {
"available" : true, "available" : true,
"enabled" : false "enabled" : false

View File

@ -262,6 +262,11 @@ GET /_xpack/usage
"available" : true, "available" : true,
"enabled" : true "enabled" : true
}, },
"searchable_snapshots" : {
"available" : true,
"enabled" : true,
"indices_count" : 0
},
"frozen_indices" : { "frozen_indices" : {
"available" : true, "available" : true,
"enabled" : true, "enabled" : true,

View File

@ -93,7 +93,9 @@ public class XPackLicenseState {
SPATIAL_GEO_GRID(OperationMode.GOLD, true), SPATIAL_GEO_GRID(OperationMode.GOLD, true),
ANALYTICS(OperationMode.MISSING, true); ANALYTICS(OperationMode.MISSING, true),
SEARCHABLE_SNAPSHOTS(OperationMode.PLATINUM, true);
final OperationMode minimumOperationMode; final OperationMode minimumOperationMode;
final boolean needsActive; final boolean needsActive;

View File

@ -192,6 +192,7 @@ import org.elasticsearch.xpack.core.rollup.job.RollupJobStatus;
import org.elasticsearch.xpack.core.async.DeleteAsyncResultAction; import org.elasticsearch.xpack.core.async.DeleteAsyncResultAction;
import org.elasticsearch.xpack.core.search.action.GetAsyncSearchAction; import org.elasticsearch.xpack.core.search.action.GetAsyncSearchAction;
import org.elasticsearch.xpack.core.search.action.SubmitAsyncSearchAction; import org.elasticsearch.xpack.core.search.action.SubmitAsyncSearchAction;
import org.elasticsearch.xpack.core.searchablesnapshots.SearchableSnapshotFeatureSetUsage;
import org.elasticsearch.xpack.core.security.SecurityFeatureSetUsage; import org.elasticsearch.xpack.core.security.SecurityFeatureSetUsage;
import org.elasticsearch.xpack.core.security.SecurityField; import org.elasticsearch.xpack.core.security.SecurityField;
import org.elasticsearch.xpack.core.security.SecuritySettings; import org.elasticsearch.xpack.core.security.SecuritySettings;
@ -631,7 +632,10 @@ public class XPackClientPlugin extends Plugin implements ActionPlugin, NetworkPl
// analytics // analytics
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.ANALYTICS, AnalyticsFeatureSetUsage::new), new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.ANALYTICS, AnalyticsFeatureSetUsage::new),
// Enrich // Enrich
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.ENRICH, EnrichFeatureSet.Usage::new) new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.ENRICH, EnrichFeatureSet.Usage::new),
// Searchable snapshots
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.SEARCHABLE_SNAPSHOTS,
SearchableSnapshotFeatureSetUsage::new)
).stream(), ).stream(),
MlEvaluationNamedXContentProvider.getNamedWriteables().stream() MlEvaluationNamedXContentProvider.getNamedWriteables().stream()
).collect(toList()); ).collect(toList());

View File

@ -57,6 +57,8 @@ public final class XPackField {
public static final String ENRICH = "enrich"; public static final String ENRICH = "enrich";
/** Name constant for the constant-keyword plugin. */ /** Name constant for the constant-keyword plugin. */
public static final String CONSTANT_KEYWORD = "constant_keyword"; public static final String CONSTANT_KEYWORD = "constant_keyword";
/** Name constant for the searchable snapshots feature. */
public static final String SEARCHABLE_SNAPSHOTS = "searchable_snapshots";
private XPackField() {} private XPackField() {}

View File

@ -0,0 +1,74 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.core.searchablesnapshots;
import org.elasticsearch.Version;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.XPackFeatureSet;
import org.elasticsearch.xpack.core.XPackField;
import java.io.IOException;
import java.util.Objects;
public class SearchableSnapshotFeatureSetUsage extends XPackFeatureSet.Usage {
private final int numberOfSearchableSnapshotIndices;
public SearchableSnapshotFeatureSetUsage(StreamInput input) throws IOException {
super(input);
numberOfSearchableSnapshotIndices = input.readVInt();
}
@Override
public Version getMinimalSupportedVersion() {
return Version.V_7_9_0;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeVInt(numberOfSearchableSnapshotIndices);
}
public SearchableSnapshotFeatureSetUsage(boolean available,
int numberOfSearchableSnapshotIndices) {
super(XPackField.SEARCHABLE_SNAPSHOTS, available, true);
this.numberOfSearchableSnapshotIndices = numberOfSearchableSnapshotIndices;
}
@Override
protected void innerXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
super.innerXContent(builder, params);
builder.field("indices_count", numberOfSearchableSnapshotIndices);
}
public int getNumberOfSearchableSnapshotIndices() {
return numberOfSearchableSnapshotIndices;
}
@Override
public int hashCode() {
return Objects.hash(available, enabled, numberOfSearchableSnapshotIndices);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
SearchableSnapshotFeatureSetUsage other = (SearchableSnapshotFeatureSetUsage) obj;
return Objects.equals(available, other.available) &&
Objects.equals(enabled, other.enabled) &&
Objects.equals(numberOfSearchableSnapshotIndices, other.numberOfSearchableSnapshotIndices);
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.core.searchablesnapshots;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.xpack.core.XPackFeatureSet;
import org.elasticsearch.xpack.core.XPackField;
import org.elasticsearch.xpack.searchablesnapshots.SearchableSnapshotsConstants;
import java.util.Map;
public class SearchableSnapshotsFeatureSet implements XPackFeatureSet {
private final XPackLicenseState licenseState;
private final ClusterService clusterService;
@Inject
public SearchableSnapshotsFeatureSet(@Nullable XPackLicenseState licenseState, ClusterService clusterService) {
this.licenseState = licenseState;
this.clusterService = clusterService;
}
@Override
public String name() {
return XPackField.SEARCHABLE_SNAPSHOTS;
}
@Override
public boolean available() {
return licenseState.isAllowed(XPackLicenseState.Feature.SEARCHABLE_SNAPSHOTS);
}
@Override
public boolean enabled() {
return true;
}
@Override
public Map<String, Object> nativeCodeInfo() {
return null;
}
@Override
public void usage(ActionListener<XPackFeatureSet.Usage> listener) {
ClusterState state = clusterService.state();
int numSnapIndices = 0;
for (IndexMetadata indexMetadata : state.metadata()) {
if (SearchableSnapshotsConstants.isSearchableSnapshotStore(indexMetadata.getSettings())) {
numSnapIndices++;
}
}
listener.onResponse(
new SearchableSnapshotFeatureSetUsage(
available(),
numSnapIndices
)
);
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.core.searchablesnapshots;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.test.AbstractWireSerializingTestCase;
import java.io.IOException;
public class SearchableSnapshotsFeatureSetUsageTests extends AbstractWireSerializingTestCase<SearchableSnapshotFeatureSetUsage> {
@Override
protected SearchableSnapshotFeatureSetUsage createTestInstance() {
boolean available = randomBoolean();
return new SearchableSnapshotFeatureSetUsage(available, randomIntBetween(0, 100000));
}
@Override
protected SearchableSnapshotFeatureSetUsage mutateInstance(SearchableSnapshotFeatureSetUsage instance) throws IOException {
boolean available = instance.available();
int numSearchableSnapshotIndices = instance.getNumberOfSearchableSnapshotIndices();
switch (between(0, 1)) {
case 0:
available = available == false;
break;
case 1:
numSearchableSnapshotIndices = randomValueOtherThan(numSearchableSnapshotIndices, () -> randomIntBetween(0, 100000));
break;
default:
throw new AssertionError("Illegal randomisation branch");
}
return new SearchableSnapshotFeatureSetUsage(available, numSearchableSnapshotIndices);
}
@Override
protected Writeable.Reader<SearchableSnapshotFeatureSetUsage> instanceReader() {
return SearchableSnapshotFeatureSetUsage::new;
}
}

View File

@ -13,6 +13,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.routing.allocation.ExistingShardsAllocator; import org.elasticsearch.cluster.routing.allocation.ExistingShardsAllocator;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.IndexScopedSettings;
@ -31,7 +32,6 @@ import org.elasticsearch.index.engine.EngineFactory;
import org.elasticsearch.index.engine.ReadOnlyEngine; import org.elasticsearch.index.engine.ReadOnlyEngine;
import org.elasticsearch.index.store.SearchableSnapshotDirectory; import org.elasticsearch.index.store.SearchableSnapshotDirectory;
import org.elasticsearch.index.translog.TranslogStats; import org.elasticsearch.index.translog.TranslogStats;
import org.elasticsearch.license.License;
import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.LicenseUtils;
import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.ActionPlugin;
@ -47,7 +47,9 @@ import org.elasticsearch.threadpool.ExecutorBuilder;
import org.elasticsearch.threadpool.ScalingExecutorBuilder; import org.elasticsearch.threadpool.ScalingExecutorBuilder;
import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.core.XPackPlugin;
import org.elasticsearch.xpack.core.searchablesnapshots.MountSearchableSnapshotAction; import org.elasticsearch.xpack.core.searchablesnapshots.MountSearchableSnapshotAction;
import org.elasticsearch.xpack.core.searchablesnapshots.SearchableSnapshotsFeatureSet;
import org.elasticsearch.xpack.searchablesnapshots.action.ClearSearchableSnapshotsCacheAction; import org.elasticsearch.xpack.searchablesnapshots.action.ClearSearchableSnapshotsCacheAction;
import org.elasticsearch.xpack.searchablesnapshots.action.RepositoryStatsAction; import org.elasticsearch.xpack.searchablesnapshots.action.RepositoryStatsAction;
import org.elasticsearch.xpack.searchablesnapshots.action.SearchableSnapshotsStatsAction; import org.elasticsearch.xpack.searchablesnapshots.action.SearchableSnapshotsStatsAction;
@ -137,12 +139,15 @@ public class SearchableSnapshots extends Plugin implements IndexStorePlugin, Eng
private final SetOnce<ThreadPool> threadPool = new SetOnce<>(); private final SetOnce<ThreadPool> threadPool = new SetOnce<>();
private final Settings settings; private final Settings settings;
private final boolean transportClientMode;
public SearchableSnapshots(final Settings settings) { public SearchableSnapshots(final Settings settings) {
this.settings = settings; this.settings = settings;
this.transportClientMode = XPackPlugin.transportClientMode(settings);
} }
public static void ensureValidLicense(XPackLicenseState licenseState) { public static void ensureValidLicense(XPackLicenseState licenseState) {
if (licenseState.isAllowedByLicense(License.OperationMode.PLATINUM) == false) { if (licenseState.isAllowed(XPackLicenseState.Feature.SEARCHABLE_SNAPSHOTS) == false) {
throw LicenseUtils.newComplianceException("searchable-snapshots"); throw LicenseUtils.newComplianceException("searchable-snapshots");
} }
} }
@ -196,6 +201,19 @@ public class SearchableSnapshots extends Plugin implements IndexStorePlugin, Eng
} }
} }
@Override
public Collection<Module> createGuiceModules() {
if (SEARCHABLE_SNAPSHOTS_FEATURE_ENABLED) {
if (transportClientMode) {
return Collections.emptyList();
}
return Collections.singleton(b -> XPackPlugin.bindFeatureSet(b, SearchableSnapshotsFeatureSet.class));
} else {
return org.elasticsearch.common.collect.List.of();
}
}
@Override @Override
public void onIndexModule(IndexModule indexModule) { public void onIndexModule(IndexModule indexModule) {
if (SearchableSnapshotsConstants.isSearchableSnapshotStore(indexModule.getSettings())) { if (SearchableSnapshotsConstants.isSearchableSnapshotStore(indexModule.getSettings())) {

View File

@ -0,0 +1,96 @@
---
setup:
- do:
indices.create:
index: docs
body:
settings:
number_of_shards: 1
number_of_replicas: 0
- do:
bulk:
body:
- index:
_index: docs
_id: 1
- field: foo
- index:
_index: docs
_id: 2
- field: bar
- index:
_index: docs
_id: 3
- field: baz
- do:
snapshot.create_repository:
repository: repository-fs
body:
type: fs
settings:
location: "repository-fs"
# Remove the snapshot if a previous test failed to delete it.
# Useful for third party tests that runs the test against a real external service.
- do:
snapshot.delete:
repository: repository-fs
snapshot: snapshot
ignore: 404
- do:
snapshot.create:
repository: repository-fs
snapshot: snapshot
wait_for_completion: true
- do:
indices.delete:
index: docs
---
teardown:
- do:
snapshot.delete:
repository: repository-fs
snapshot: snapshot
ignore: 404
- do:
snapshot.delete_repository:
repository: repository-fs
---
"Tests searchable snapshots usage stats":
- skip:
version: " - 7.8.99"
reason: searchable snapshots usage stats introduced in 7.9.0
- do:
xpack.usage: {}
- match: { searchable_snapshots.available: true }
- match: { searchable_snapshots.enabled: true }
- match: { searchable_snapshots.indices_count: 0 }
- do:
searchable_snapshots.mount:
repository: repository-fs
snapshot: snapshot
wait_for_completion: true
body:
index: docs
- match: { snapshot.snapshot: snapshot }
- match: { snapshot.shards.failed: 0 }
- match: { snapshot.shards.successful: 1 }
- do:
xpack.usage: {}
- match: { searchable_snapshots.available: true }
- match: { searchable_snapshots.enabled: true }
- match: { searchable_snapshots.indices_count: 1 }