add more force-merge related steps and tests

The force-merge is an a TODO state due to the
unresolved issue around best_compression.

- updated ReadOnlyStep with tests
- implemented an update to the ForceMergeAction
- added UpdateBestCompressionSettingsStep
- added tests for SegmentCountStep
This commit is contained in:
Tal Levy 2018-04-09 16:25:56 -07:00
parent 06e969b430
commit f5b23df349
14 changed files with 727 additions and 411 deletions

View File

@ -5,39 +5,20 @@
*/
package org.elasticsearch.xpack.core.indexlifecycle;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequestBuilder;
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeResponse;
import org.elasticsearch.action.admin.indices.segments.IndicesSegmentResponse;
import org.elasticsearch.action.admin.indices.segments.IndicesSegmentsRequest;
import org.elasticsearch.action.admin.indices.segments.IndicesSegmentsRequestBuilder;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.Index;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.LongSupplier;
import java.util.stream.StreamSupport;
/**
* A {@link LifecycleAction} which force-merges the index.
@ -45,42 +26,53 @@ import java.util.stream.StreamSupport;
public class ForceMergeAction implements LifecycleAction {
public static final String NAME = "forcemerge";
public static final ParseField MAX_NUM_SEGMENTS_FIELD = new ParseField("max_num_segments");
public static final ParseField BEST_COMPRESSION_FIELD = new ParseField("best_compression");
private static final ConstructingObjectParser<ForceMergeAction, Void> PARSER = new ConstructingObjectParser<>(NAME,
false, a -> {
int maxNumSegments = (Integer) a[0];
return new ForceMergeAction(maxNumSegments);
boolean bestCompression = (Boolean) a[1];
return new ForceMergeAction(maxNumSegments, bestCompression);
});
static {
PARSER.declareInt(ConstructingObjectParser.constructorArg(), MAX_NUM_SEGMENTS_FIELD);
PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), BEST_COMPRESSION_FIELD);
}
private final Integer maxNumSegments;
private final int maxNumSegments;
private final boolean bestCompression;
public static ForceMergeAction parse(XContentParser parser) {
return PARSER.apply(parser, null);
}
public ForceMergeAction(int maxNumSegments) {
public ForceMergeAction(int maxNumSegments, boolean bestCompression) {
if (maxNumSegments <= 0) {
throw new IllegalArgumentException("[" + MAX_NUM_SEGMENTS_FIELD.getPreferredName()
+ "] must be a positive integer");
}
this.maxNumSegments = maxNumSegments;
this.bestCompression = bestCompression;
}
public ForceMergeAction(StreamInput in) throws IOException {
this.maxNumSegments = in.readVInt();
this.bestCompression = in.readBoolean();
}
public int getMaxNumSegments() {
return maxNumSegments;
}
public boolean isBestCompression() {
return bestCompression;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(maxNumSegments);
out.writeBoolean(bestCompression);
}
@Override
@ -92,34 +84,28 @@ public class ForceMergeAction implements LifecycleAction {
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(MAX_NUM_SEGMENTS_FIELD.getPreferredName(), maxNumSegments);
builder.field(BEST_COMPRESSION_FIELD.getPreferredName(), bestCompression);
builder.endObject();
return builder;
}
@Override
public List<Step> toSteps(Client client, String phase, Step.StepKey nextStepKey) {
// ClientStep<IndicesSegmentsRequestBuilder, IndicesSegmentResponse> segmentCount = new ClientStep<>( "segment_count",
// NAME, phase, index.getName(),
// client.admin().indices().prepareSegments(index.getName()),
// currentState -> false, response -> {
// // check if has too many segments
// return StreamSupport.stream(response.getIndices().get(index.getName()).spliterator(), false)
// .anyMatch(iss -> Arrays.stream(iss.getShards()).anyMatch(p -> p.getSegments().size() > maxNumSegments));
// });
//
// ClientStep forceMerge = new ClientStep<ForceMergeRequestBuilder, ForceMergeResponse>( "force_merge",
// NAME, phase, index.getName(),
// client.admin().indices().prepareForceMerge(index.getName()).setMaxNumSegments(maxNumSegments),
// currentState -> false, response -> RestStatus.OK.equals(response.getStatus()));
// just forceMerge... then wait to see whether segmentCount matches condition
return Arrays.asList();
StepKey updateCompressionKey = new StepKey(phase, NAME, UpdateBestCompressionSettingsStep.NAME);
StepKey forceMergeKey = new StepKey(phase, NAME, ForceMergeStep.NAME);
StepKey countKey = new StepKey(phase, NAME, SegmentCountStep.NAME);
ForceMergeStep forceMergeStep = new ForceMergeStep(forceMergeKey, countKey, client, maxNumSegments);
SegmentCountStep segmentCountStep = new SegmentCountStep(countKey, nextStepKey, client, maxNumSegments, bestCompression);
if (bestCompression) {
UpdateBestCompressionSettingsStep updateBestCompression = new UpdateBestCompressionSettingsStep(updateCompressionKey, forceMergeKey);
return Arrays.asList(updateBestCompression, forceMergeStep, segmentCountStep);
}
return Arrays.asList(forceMergeStep, segmentCountStep);
}
@Override
public int hashCode() {
return Objects.hash(maxNumSegments);
return Objects.hash(maxNumSegments, bestCompression);
}
@Override
@ -131,7 +117,8 @@ public class ForceMergeAction implements LifecycleAction {
return false;
}
ForceMergeAction other = (ForceMergeAction) obj;
return Objects.equals(maxNumSegments, other.maxNumSegments);
return Objects.equals(maxNumSegments, other.maxNumSegments)
&& Objects.equals(bestCompression, other.bestCompression);
}
@Override

View File

@ -0,0 +1,56 @@
/*
* 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.indexlifecycle;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.Index;
import org.elasticsearch.rest.RestStatus;
import java.util.Objects;
public class ForceMergeStep extends AsyncActionStep {
public static final String NAME = "forcemerge";
private final int maxNumSegments;
public ForceMergeStep(StepKey key, StepKey nextStepKey, Client client, int maxNumSegments) {
super(key, nextStepKey, client);
this.maxNumSegments = maxNumSegments;
}
public int getMaxNumSegments() {
return maxNumSegments;
}
@Override
public void performAction(Index index, Listener listener) {
ForceMergeRequest request = new ForceMergeRequest();
request.maxNumSegments(maxNumSegments);
getClient().admin().indices()
.forceMerge(request, ActionListener.wrap(response -> listener.onResponse(true),
listener::onFailure));
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), maxNumSegments);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ForceMergeStep other = (ForceMergeStep) obj;
return super.equals(obj) &&
Objects.equals(maxNumSegments, other.maxNumSegments);
}
}

View File

@ -10,12 +10,14 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* A {@link LifecycleAction} which force-merges the index.
@ -24,8 +26,7 @@ public class ReadOnlyAction implements LifecycleAction {
public static final String NAME = "readonly";
public static final ReadOnlyAction INSTANCE = new ReadOnlyAction();
private static final ConstructingObjectParser<ReadOnlyAction, Void> PARSER = new ConstructingObjectParser<>(NAME,
false, a -> new ReadOnlyAction());
private static final ObjectParser<ReadOnlyAction, Void> PARSER = new ObjectParser<>(NAME, false, ReadOnlyAction::new);
public static ReadOnlyAction parse(XContentParser parser) {
return PARSER.apply(parser, null);
@ -55,10 +56,26 @@ public class ReadOnlyAction implements LifecycleAction {
@Override
public List<Step> toSteps(Client client, String phase, Step.StepKey nextStepKey) {
Step.StepKey key = new Step.StepKey(phase, NAME, "readonly-step");
Step.StepKey key = new Step.StepKey(phase, NAME, ReadOnlyStep.NAME);
return Collections.singletonList(new ReadOnlyStep(key, nextStepKey));
}
@Override
public int hashCode() {
return ReadOnlyAction.class.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != getClass()) {
return false;
}
return true;
}
@Override
public String toString() {
return Strings.toString(this);

View File

@ -11,7 +11,10 @@ import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import java.util.Objects;
public class ReadOnlyStep extends ClusterStateActionStep {
public static final String NAME = "read-only";
ReadOnlyStep(StepKey key, StepKey nextStepKey) {
super(key, nextStepKey);

View File

@ -5,27 +5,77 @@
*/
package org.elasticsearch.xpack.core.indexlifecycle;
import org.apache.lucene.codecs.lucene50.Lucene50StoredFieldsFormat;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.segments.IndicesSegmentsRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.Index;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.StreamSupport;
/**
* This {@link Step} evaluates whether force_merge was successful
*/
public class SegmentCountStep extends AsyncWaitStep {
public static final String NAME = "segment-count";
private final int maxNumSegments;
private final boolean bestCompression;
public SegmentCountStep(StepKey key, StepKey nextStepKey, Client client, int maxNumSegments) {
public SegmentCountStep(StepKey key, StepKey nextStepKey, Client client, int maxNumSegments, boolean bestCompression) {
super(key, nextStepKey, client);
this.maxNumSegments = maxNumSegments;
this.bestCompression = bestCompression;
}
public int getMaxNumSegments() {
return maxNumSegments;
}
public boolean isBestCompression() {
return bestCompression;
}
@Override
public void evaluateCondition(Index index, Listener listener) {
getClient().admin().indices().prepareSegments(index.getName()).execute(ActionListener.wrap(response -> {
getClient().admin().indices().segments(new IndicesSegmentsRequest(index.getName()), ActionListener.wrap(response -> {
listener.onResponse(StreamSupport.stream(response.getIndices().get(index.getName()).spliterator(), false)
.anyMatch(iss -> Arrays.stream(iss.getShards()).anyMatch(p -> p.getSegments().size() > maxNumSegments)));
.anyMatch(iss -> Arrays.stream(iss.getShards()).anyMatch(p -> {
boolean hasRightAmountOfSegments = p.getSegments().size() <= maxNumSegments;
if (bestCompression) {
// // TODO(talevy): discuss
// boolean allUsingCorrectCompression = p.getSegments().stream().anyMatch(s ->
// Lucene50StoredFieldsFormat.Mode.BEST_COMPRESSION.equals(
// Lucene50StoredFieldsFormat.Mode.BEST_COMPRESSION.toString().equals(
// s.getAttributes().get(Lucene50StoredFieldsFormat.MODE_KEY)))
// );
boolean allUsingCorrectCompression = true;
return hasRightAmountOfSegments && allUsingCorrectCompression;
} else {
return hasRightAmountOfSegments;
}
})));
}, listener::onFailure));
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), maxNumSegments, bestCompression);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
SegmentCountStep other = (SegmentCountStep) obj;
return super.equals(obj)
&& Objects.equals(maxNumSegments, other.maxNumSegments)
&& Objects.equals(bestCompression, other.bestCompression);
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.indexlifecycle;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.codec.CodecService;
import org.elasticsearch.index.engine.EngineConfig;
import java.util.Objects;
public class UpdateBestCompressionSettingsStep extends ClusterStateActionStep {
public static final String NAME = "update-best-compression";
public UpdateBestCompressionSettingsStep(StepKey key, StepKey nextStepKey) {
super(key, nextStepKey);
}
@Override
public ClusterState performAction(Index index, ClusterState clusterState) {
IndexMetaData idxMeta = clusterState.metaData().index(index);
if (idxMeta == null) {
return clusterState;
}
Settings.Builder newSettings = Settings.builder()
.put(EngineConfig.INDEX_CODEC_SETTING.getKey(), CodecService.BEST_COMPRESSION_CODEC);
return ClusterState.builder(clusterState)
.metaData(MetaData.builder(clusterState.metaData())
.updateSettings(newSettings.build(), index.getName())).build();
}
}

View File

@ -91,7 +91,6 @@ public class DeleteStepTests extends ESTestCase {
assertEquals(1, request.indices().length);
assertEquals(index.getName(), request.indices()[0]);
listener.onFailure(exception);
;
return null;
}

View File

@ -5,63 +5,43 @@
*/
package org.elasticsearch.xpack.core.indexlifecycle;
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeResponse;
import org.elasticsearch.action.admin.indices.segments.IndexSegments;
import org.elasticsearch.action.admin.indices.segments.IndexShardSegments;
import org.elasticsearch.action.admin.indices.segments.IndicesSegmentResponse;
import org.elasticsearch.action.admin.indices.segments.ShardSegments;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse;
import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.io.stream.Writeable.Reader;
import org.elasticsearch.common.xcontent.DeprecationHandler;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.engine.Segment;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.AbstractSerializingTestCase;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Spliterator;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Matchers.any;
public class ForceMergeActionTests extends AbstractSerializingTestCase<ForceMergeAction> {
@Override
protected ForceMergeAction doParseInstance(XContentParser parser) throws IOException {
protected ForceMergeAction doParseInstance(XContentParser parser) {
return ForceMergeAction.parse(parser);
}
@Override
protected ForceMergeAction createTestInstance() {
return new ForceMergeAction(randomIntBetween(1, 100));
return new ForceMergeAction(randomIntBetween(1, 100), randomBoolean());
}
@Override
protected ForceMergeAction mutateInstance(ForceMergeAction instance) {
return new ForceMergeAction(instance.getMaxNumSegments() + randomIntBetween(1, 10));
int maxNumSegments = instance.getMaxNumSegments();
boolean bestCompression = instance.isBestCompression();
if (randomBoolean()) {
maxNumSegments = maxNumSegments + randomIntBetween(1, 10);
} else {
bestCompression = !bestCompression;
}
return new ForceMergeAction(maxNumSegments, bestCompression);
}
@Override
@ -78,336 +58,30 @@ public class ForceMergeActionTests extends AbstractSerializingTestCase<ForceMerg
}
public void testInvalidNegativeSegmentNumber() {
Exception r = expectThrows(IllegalArgumentException.class, () -> new ForceMergeAction(randomIntBetween(-10, 0)));
Exception r = expectThrows(IllegalArgumentException.class, () -> new ForceMergeAction(randomIntBetween(-10, 0), false));
assertThat(r.getMessage(), equalTo("[max_num_segments] must be a positive integer"));
}
// public void testExecuteSuccessfully() throws Exception {
// Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
//
// ClusterService clusterService = Mockito.mock(ClusterService.class);
// IndexMetaData indexMetadata = IndexMetaData.builder(index.getName())
// .settings(settings(Version.CURRENT))
// .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
// ImmutableOpenMap.Builder<String, IndexMetaData> indices = ImmutableOpenMap.<String, IndexMetaData> builder().fPut(index.getName(),
// indexMetadata);
// ClusterState clusterState = ClusterState.builder(ClusterState.EMPTY_STATE).metaData(MetaData.builder().indices(indices.build()))
// .build();
// Mockito.when(clusterService.state()).thenReturn(clusterState);
// Client client = Mockito.mock(Client.class);
// AdminClient adminClient = Mockito.mock(AdminClient.class);
// IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class);
// UpdateSettingsResponse updateSettingsResponse = Mockito.mock(UpdateSettingsResponse.class);
// IndicesSegmentResponse indicesSegmentResponse = Mockito.mock(IndicesSegmentResponse.class);
// IndexSegments indexSegments = Mockito.mock(IndexSegments.class);
// IndexShardSegments indexShardSegments = Mockito.mock(IndexShardSegments.class);
// Map<Integer, IndexShardSegments> indexShards = Collections.singletonMap(0, indexShardSegments);
// ShardSegments shardSegmentsOne = Mockito.mock(ShardSegments.class);
// ShardSegments[] shardSegmentsArray = new ShardSegments[] { shardSegmentsOne };
// Spliterator<IndexShardSegments> iss = indexShards.values().spliterator();
// List<Segment> segments = Arrays.asList(null, null);
// ForceMergeResponse forceMergeResponse = Mockito.mock(ForceMergeResponse.class);
// Mockito.when(forceMergeResponse.getStatus()).thenReturn(RestStatus.OK);
// Mockito.when(indicesSegmentResponse.getStatus()).thenReturn(RestStatus.OK);
// Mockito.when(indicesSegmentResponse.getIndices()).thenReturn(Collections.singletonMap(index.getName(), indexSegments));
// Mockito.when(indexSegments.spliterator()).thenReturn(iss);
// Mockito.when(indexShardSegments.getShards()).thenReturn(shardSegmentsArray);
// Mockito.when(shardSegmentsOne.getSegments()).thenReturn(segments);
// int maxNumSegments = 1;
//
// Mockito.when(client.admin()).thenReturn(adminClient);
// Mockito.when(adminClient.indices()).thenReturn(indicesClient);
//
// Mockito.doAnswer(invocationOnMock -> {
// @SuppressWarnings("unchecked")
// ActionListener<IndicesSegmentResponse> listener = (ActionListener<IndicesSegmentResponse>) invocationOnMock.getArguments()[1];
// listener.onResponse(indicesSegmentResponse);
// return null;
// }).when(indicesClient).segments(any(), any());
//
// Mockito.doAnswer(invocationOnMock -> {
// @SuppressWarnings("unchecked")
// ActionListener<UpdateSettingsResponse> listener = (ActionListener<UpdateSettingsResponse>) invocationOnMock.getArguments()[1];
// listener.onResponse(updateSettingsResponse);
// return null;
// }).when(indicesClient).updateSettings(any(), any());
//
// Mockito.doAnswer(invocationOnMock -> {
// ForceMergeRequest request = (ForceMergeRequest) invocationOnMock.getArguments()[0];
// assertThat(request.maxNumSegments(), equalTo(maxNumSegments));
// @SuppressWarnings("unchecked")
// ActionListener<ForceMergeResponse> listener = (ActionListener<ForceMergeResponse>) invocationOnMock.getArguments()[1];
// listener.onResponse(forceMergeResponse);
// return null;
// }).when(indicesClient).forceMerge(any(), any());
//
// SetOnce<Boolean> actionCompleted = new SetOnce<>();
// ForceMergeAction action = new ForceMergeAction(maxNumSegments);
// action.execute(index, client, clusterService, new Listener() {
//
// @Override
// public void onSuccess(boolean completed) {
// actionCompleted.set(completed);
// }
//
// @Override
// public void onFailure(Exception e) {
// throw new AssertionError("Unexpected method call", e);
// }
// });
//
// assertEquals(true, actionCompleted.get());
//
// Mockito.verify(client, Mockito.atLeast(1)).admin();
// Mockito.verify(adminClient, Mockito.atLeast(1)).indices();
// Mockito.verify(indicesClient, Mockito.atLeast(1)).segments(any(), any());
// Mockito.verify(indicesClient, Mockito.atLeast(1)).updateSettings(any(), any());
// Mockito.verify(indicesClient, Mockito.atLeast(1)).forceMerge(any(), any());
// Mockito.verify(clusterService, Mockito.atLeast(1)).state();
// }
//
// public void testExecuteWhenReadOnlyAlready() {
// Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
// boolean isReadOnlyAlready = true;
//
// ClusterService clusterService = Mockito.mock(ClusterService.class);
// IndexMetaData indexMetadata = IndexMetaData.builder(index.getName())
// .settings(settings(Version.CURRENT).put(IndexMetaData.SETTING_BLOCKS_WRITE, isReadOnlyAlready))
// .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
// ImmutableOpenMap.Builder<String, IndexMetaData> indices = ImmutableOpenMap.<String, IndexMetaData> builder().fPut(index.getName(),
// indexMetadata);
// ClusterState clusterState = ClusterState.builder(ClusterState.EMPTY_STATE).metaData(MetaData.builder().indices(indices.build()))
// .build();
// Mockito.when(clusterService.state()).thenReturn(clusterState);
//
// Client client = Mockito.mock(Client.class);
// AdminClient adminClient = Mockito.mock(AdminClient.class);
// IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class);
// IndicesSegmentResponse indicesSegmentResponse = Mockito.mock(IndicesSegmentResponse.class);
// IndexSegments indexSegments = Mockito.mock(IndexSegments.class);
// IndexShardSegments indexShardSegments = Mockito.mock(IndexShardSegments.class);
// Map<Integer, IndexShardSegments> indexShards = Collections.singletonMap(0, indexShardSegments);
// ShardSegments shardSegmentsOne = Mockito.mock(ShardSegments.class);
// ShardSegments[] shardSegmentsArray = new ShardSegments[] { shardSegmentsOne };
// Spliterator<IndexShardSegments> iss = indexShards.values().spliterator();
// List<Segment> segments = Arrays.asList(null, null);
//
// Mockito.when(indicesSegmentResponse.getStatus()).thenReturn(RestStatus.OK);
// Mockito.when(indicesSegmentResponse.getIndices()).thenReturn(Collections.singletonMap(index.getName(), indexSegments));
// Mockito.when(indexSegments.spliterator()).thenReturn(iss);
// Mockito.when(indexShardSegments.getShards()).thenReturn(shardSegmentsArray);
// Mockito.when(shardSegmentsOne.getSegments()).thenReturn(segments);
// int maxNumSegments = Integer.MAX_VALUE;
//
// Mockito.when(client.admin()).thenReturn(adminClient);
// Mockito.when(adminClient.indices()).thenReturn(indicesClient);
//
//
// Mockito.doAnswer(invocationOnMock -> {
// @SuppressWarnings("unchecked")
// ActionListener<IndicesSegmentResponse> listener = (ActionListener<IndicesSegmentResponse>) invocationOnMock.getArguments()[1];
// listener.onResponse(indicesSegmentResponse);
// return null;
// }).when(indicesClient).segments(any(), any());
//
//
// SetOnce<Boolean> actionCompleted = new SetOnce<>();
// ForceMergeAction action = new ForceMergeAction(maxNumSegments);
// action.execute(index, client, clusterService, new Listener() {
//
// @Override
// public void onSuccess(boolean completed) {
// actionCompleted.set(completed);
// }
//
// @Override
// public void onFailure(Exception e) {
// throw new AssertionError("Unexpected method call", e);
// }
// });
//
// assertEquals(null, actionCompleted.get());
//
// InOrder inOrder = Mockito.inOrder(clusterService, client, adminClient, indicesClient);
// inOrder.verify(clusterService).state();
// inOrder.verify(client).admin();
// inOrder.verify(adminClient).indices();
// inOrder.verify(indicesClient).segments(any(), any());
// inOrder.verify(indicesClient).updateSettings(any(), any());
// Mockito.verify(indicesClient, Mockito.never()).forceMerge(any(), any());
// }
//
// public void testExecuteWithNoNeedToForceMerge() {
// Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
// boolean isReadOnlyAlready = false;
//
// ClusterService clusterService = Mockito.mock(ClusterService.class);
// IndexMetaData indexMetadata = IndexMetaData.builder(index.getName())
// .settings(settings(Version.CURRENT).put(IndexMetaData.SETTING_BLOCKS_WRITE, isReadOnlyAlready))
// .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
// ImmutableOpenMap.Builder<String, IndexMetaData> indices = ImmutableOpenMap.<String, IndexMetaData> builder().fPut(index.getName(),
// indexMetadata);
// ClusterState clusterState = ClusterState.builder(ClusterState.EMPTY_STATE).metaData(MetaData.builder().indices(indices.build()))
// .build();
// Mockito.when(clusterService.state()).thenReturn(clusterState);
//
// Client client = Mockito.mock(Client.class);
// AdminClient adminClient = Mockito.mock(AdminClient.class);
// IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class);
// IndicesSegmentResponse indicesSegmentResponse = Mockito.mock(IndicesSegmentResponse.class);
// IndexSegments indexSegments = Mockito.mock(IndexSegments.class);
// IndexShardSegments indexShardSegments = Mockito.mock(IndexShardSegments.class);
// Map<Integer, IndexShardSegments> indexShards = Collections.singletonMap(0, indexShardSegments);
// ShardSegments shardSegmentsOne = Mockito.mock(ShardSegments.class);
// ShardSegments[] shardSegmentsArray = new ShardSegments[] { shardSegmentsOne };
// Spliterator<IndexShardSegments> iss = indexShards.values().spliterator();
// List<Segment> segments = Arrays.asList(null, null);
//
// Mockito.when(indicesSegmentResponse.getStatus()).thenReturn(RestStatus.OK);
// Mockito.when(indicesSegmentResponse.getIndices()).thenReturn(Collections.singletonMap(index.getName(), indexSegments));
// Mockito.when(indexSegments.spliterator()).thenReturn(iss);
// Mockito.when(indexShardSegments.getShards()).thenReturn(shardSegmentsArray);
// Mockito.when(shardSegmentsOne.getSegments()).thenReturn(segments);
// int maxNumSegments = Integer.MAX_VALUE;
//
// Mockito.when(client.admin()).thenReturn(adminClient);
// Mockito.when(adminClient.indices()).thenReturn(indicesClient);
//
//
// Mockito.doAnswer(invocationOnMock -> {
// @SuppressWarnings("unchecked")
// ActionListener<IndicesSegmentResponse> listener = (ActionListener<IndicesSegmentResponse>) invocationOnMock.getArguments()[1];
// listener.onResponse(indicesSegmentResponse);
// return null;
// }).when(indicesClient).segments(any(), any());
//
//
// SetOnce<Boolean> actionCompleted = new SetOnce<>();
// ForceMergeAction action = new ForceMergeAction(maxNumSegments);
// action.execute(index, client, clusterService, new Listener() {
//
// @Override
// public void onSuccess(boolean completed) {
// actionCompleted.set(completed);
// }
//
// @Override
// public void onFailure(Exception e) {
// throw new AssertionError("Unexpected method call", e);
// }
// });
//
// assertEquals(true, actionCompleted.get());
//
// InOrder inOrder = Mockito.inOrder(clusterService, client, adminClient, indicesClient);
// inOrder.verify(clusterService).state();
// inOrder.verify(client).admin();
// inOrder.verify(adminClient).indices();
// inOrder.verify(indicesClient).segments(any(), any());
// Mockito.verify(indicesClient, Mockito.never()).updateSettings(any(), any());
// Mockito.verify(indicesClient, Mockito.never()).forceMerge(any(), any());
// }
//
// public void testCheckSegmentsNeedsMerging() {
// Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
// Client client = Mockito.mock(Client.class);
// AdminClient adminClient = Mockito.mock(AdminClient.class);
// IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class);
// IndicesSegmentResponse indicesSegmentResponse = Mockito.mock(IndicesSegmentResponse.class);
// IndexSegments indexSegments = Mockito.mock(IndexSegments.class);
// IndexShardSegments indexShardSegments = Mockito.mock(IndexShardSegments.class);
// Map<Integer, IndexShardSegments> indexShards = Collections.singletonMap(0, indexShardSegments);
// ShardSegments shardSegmentsOne = Mockito.mock(ShardSegments.class);
// ShardSegments[] shardSegmentsArray = new ShardSegments[] { shardSegmentsOne };
// Spliterator<IndexShardSegments> iss = indexShards.values().spliterator();
// List<Segment> segments = Arrays.asList(null, null);
// Mockito.when(indicesSegmentResponse.getStatus()).thenReturn(RestStatus.OK);
// Mockito.when(indicesSegmentResponse.getIndices()).thenReturn(Collections.singletonMap(index.getName(), indexSegments));
// Mockito.when(indexSegments.spliterator()).thenReturn(iss);
// Mockito.when(indexShardSegments.getShards()).thenReturn(shardSegmentsArray);
// Mockito.when(shardSegmentsOne.getSegments()).thenReturn(segments);
// int maxNumSegments = 1;
//
// Mockito.when(client.admin()).thenReturn(adminClient);
// Mockito.when(adminClient.indices()).thenReturn(indicesClient);
//
// Mockito.doAnswer(invocationOnMock -> {
// @SuppressWarnings("unchecked")
// ActionListener<IndicesSegmentResponse> listener = (ActionListener<IndicesSegmentResponse>) invocationOnMock.getArguments()[1];
// listener.onResponse(indicesSegmentResponse);
// return null;
// }).when(indicesClient).segments(any(), any());
//
// SetOnce<Boolean> nextActionCalled = new SetOnce<>();
// ForceMergeAction action = new ForceMergeAction(maxNumSegments);
// action.checkSegments(index, client, new Listener() {
//
// @Override
// public void onSuccess(boolean completed) {
// throw new AssertionError("Unexpected method call to onSuccess");
// }
//
// @Override
// public void onFailure(Exception e) {
// throw new AssertionError("Unexpected method call", e);
// }
// }, r -> nextActionCalled.set(true), r2 -> {throw new AssertionError("unexpected call to action");});
//
// assertEquals(true, nextActionCalled.get());
// Mockito.verify(client, Mockito.only()).admin();
// Mockito.verify(adminClient, Mockito.only()).indices();
// Mockito.verify(indicesClient, Mockito.only()).segments(any(), any());
// }
//
// public void testCheckSegmentsNoNeedToMerge() {
// Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
// Client client = Mockito.mock(Client.class);
// AdminClient adminClient = Mockito.mock(AdminClient.class);
// IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class);
// IndicesSegmentResponse indicesSegmentResponse = Mockito.mock(IndicesSegmentResponse.class);
// IndexSegments indexSegments = Mockito.mock(IndexSegments.class);
// IndexShardSegments indexShardSegments = Mockito.mock(IndexShardSegments.class);
// Map<Integer, IndexShardSegments> indexShards = Collections.singletonMap(0, indexShardSegments);
// ShardSegments shardSegmentsOne = Mockito.mock(ShardSegments.class);
// ShardSegments[] shardSegmentsArray = new ShardSegments[] { shardSegmentsOne };
// Spliterator<IndexShardSegments> iss = indexShards.values().spliterator();
// List<Segment> segments = Arrays.asList(null, null);
// Mockito.when(indicesSegmentResponse.getStatus()).thenReturn(RestStatus.OK);
// Mockito.when(indicesSegmentResponse.getIndices()).thenReturn(Collections.singletonMap(index.getName(), indexSegments));
// Mockito.when(indexSegments.spliterator()).thenReturn(iss);
// Mockito.when(indexShardSegments.getShards()).thenReturn(shardSegmentsArray);
// Mockito.when(shardSegmentsOne.getSegments()).thenReturn(segments);
// int maxNumSegments = randomIntBetween(2, Integer.MAX_VALUE);
//
// Mockito.when(client.admin()).thenReturn(adminClient);
// Mockito.when(adminClient.indices()).thenReturn(indicesClient);
//
// Mockito.doAnswer(invocationOnMock -> {
// @SuppressWarnings("unchecked")
// ActionListener<IndicesSegmentResponse> listener = (ActionListener<IndicesSegmentResponse>) invocationOnMock.getArguments()[1];
// listener.onResponse(indicesSegmentResponse);
// return null;
// }).when(indicesClient).segments(any(), any());
//
// SetOnce<Boolean> skipActionCalled = new SetOnce<>();
// ForceMergeAction action = new ForceMergeAction(maxNumSegments);
// action.checkSegments(index, client, new Listener() {
//
// @Override
// public void onSuccess(boolean completed) {
// throw new AssertionError("Unexpected method call to onSuccess");
// }
//
// @Override
// public void onFailure(Exception e) {
// throw new AssertionError("Unexpected method call", e);
// }
// }, r -> { throw new AssertionError("next action should not be called"); },
// r2 -> skipActionCalled.set(true));
//
// assertTrue(skipActionCalled.get());
// Mockito.verify(client, Mockito.only()).admin();
// Mockito.verify(adminClient, Mockito.only()).indices();
// Mockito.verify(indicesClient, Mockito.only()).segments(any(), any());
// }
public void testToSteps() {
ForceMergeAction instance = createTestInstance();
String phase = randomAlphaOfLength(5);
StepKey nextStepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
List<Step> steps = instance.toSteps(null, phase, nextStepKey);
assertNotNull(steps);
int segmentCountIndex = 1;
if (instance.isBestCompression()) {
assertEquals(3, steps.size());
UpdateBestCompressionSettingsStep firstStep = (UpdateBestCompressionSettingsStep) steps.get(0);
assertThat(firstStep.getKey(), equalTo(new StepKey(phase, ForceMergeAction.NAME, UpdateBestCompressionSettingsStep.NAME)));
segmentCountIndex = 2;
} else {
assertEquals(2, steps.size());
}
ForceMergeStep firstStep = (ForceMergeStep) steps.get(0);
SegmentCountStep secondStep = (SegmentCountStep) steps.get(segmentCountIndex);
assertThat(firstStep.getKey(), equalTo(new StepKey(phase, ForceMergeAction.NAME, ForceMergeStep.NAME)));
assertThat(firstStep.getNextStepKey(), equalTo(secondStep.getKey()));
assertThat(secondStep.getKey(), equalTo(new StepKey(phase, ForceMergeAction.NAME, SegmentCountStep.NAME)));
assertThat(secondStep.getNextStepKey(), equalTo(nextStepKey));
}
}

View File

@ -0,0 +1,147 @@
/*
* 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.indexlifecycle;
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeResponse;
import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.index.Index;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.EqualsHashCodeTestUtils;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
import org.mockito.Mockito;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.when;
public class ForceMergeStepTests extends ESTestCase {
public ForceMergeStep createRandomInstance() {
Step.StepKey stepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
StepKey nextStepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
int maxNumSegments = randomIntBetween(1, 10);
return new ForceMergeStep(stepKey, nextStepKey, null, maxNumSegments);
}
public ForceMergeStep mutateInstance(ForceMergeStep instance) {
StepKey key = instance.getKey();
StepKey nextKey = instance.getNextStepKey();
int maxNumSegments = instance.getMaxNumSegments();
switch (between(0, 2)) {
case 0:
key = new StepKey(key.getPhase(), key.getAction(), key.getName() + randomAlphaOfLength(5));
break;
case 1:
nextKey = new StepKey(key.getPhase(), key.getAction(), key.getName() + randomAlphaOfLength(5));
break;
case 2:
maxNumSegments += 1;
break;
default:
throw new AssertionError("Illegal randomisation branch");
}
return new ForceMergeStep(key, nextKey, null, maxNumSegments);
}
public void testHashcodeAndEquals() {
EqualsHashCodeTestUtils.checkEqualsAndHashCode(createRandomInstance(),
instance -> new ForceMergeStep(instance.getKey(), instance.getNextStepKey(),
null, instance.getMaxNumSegments()), this::mutateInstance);
}
public void testPerformActionComplete() {
Step.StepKey stepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
StepKey nextStepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
int maxNumSegments = randomIntBetween(1, 10);
Client client = mock(Client.class);
AdminClient adminClient = mock(AdminClient.class);
IndicesAdminClient indicesClient = mock(IndicesAdminClient.class);
when(client.admin()).thenReturn(adminClient);
when(adminClient.indices()).thenReturn(indicesClient);
ForceMergeResponse forceMergeResponse = Mockito.mock(ForceMergeResponse.class);
Mockito.when(forceMergeResponse.getStatus()).thenReturn(RestStatus.OK);
Mockito.doAnswer(invocationOnMock -> {
@SuppressWarnings("unchecked")
ForceMergeRequest request = (ForceMergeRequest) invocationOnMock.getArguments()[0];
assertThat(request.maxNumSegments(), equalTo(maxNumSegments));
@SuppressWarnings("unchecked")
ActionListener<ForceMergeResponse> listener = (ActionListener<ForceMergeResponse>) invocationOnMock.getArguments()[1];
listener.onResponse(forceMergeResponse);
return null;
}).when(indicesClient).forceMerge(any(), any());
ForceMergeStep step = new ForceMergeStep(stepKey, nextStepKey, client, maxNumSegments);
Index index = new Index(randomAlphaOfLength(5), randomAlphaOfLength(5));
SetOnce<Boolean> completed = new SetOnce<>();
step.performAction(index, new AsyncActionStep.Listener() {
@Override
public void onResponse(boolean complete) {
completed.set(complete);
}
@Override
public void onFailure(Exception e) {
throw new AssertionError("unexpected method call", e);
}
});
assertThat(completed.get(), equalTo(true));
}
public void testPerformActionThrowsException() {
Exception exception = new RuntimeException("error");
Step.StepKey stepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
StepKey nextStepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
int maxNumSegments = randomIntBetween(1, 10);
Client client = mock(Client.class);
AdminClient adminClient = mock(AdminClient.class);
IndicesAdminClient indicesClient = mock(IndicesAdminClient.class);
when(client.admin()).thenReturn(adminClient);
when(adminClient.indices()).thenReturn(indicesClient);
ForceMergeResponse forceMergeResponse = Mockito.mock(ForceMergeResponse.class);
Mockito.when(forceMergeResponse.getStatus()).thenReturn(RestStatus.OK);
Mockito.doAnswer(invocationOnMock -> {
@SuppressWarnings("unchecked")
ForceMergeRequest request = (ForceMergeRequest) invocationOnMock.getArguments()[0];
assertThat(request.maxNumSegments(), equalTo(maxNumSegments));
@SuppressWarnings("unchecked")
ActionListener<ForceMergeResponse> listener = (ActionListener<ForceMergeResponse>) invocationOnMock.getArguments()[1];
listener.onFailure(exception);
return null;
}).when(indicesClient).forceMerge(any(), any());
ForceMergeStep step = new ForceMergeStep(stepKey, nextStepKey, client, maxNumSegments);
Index index = new Index(randomAlphaOfLength(5), randomAlphaOfLength(5));
SetOnce<Boolean> exceptionThrown = new SetOnce<>();
step.performAction(index, new AsyncActionStep.Listener() {
@Override
public void onResponse(boolean complete) {
throw new AssertionError("unexpected method call");
}
@Override
public void onFailure(Exception e) {
assertEquals(exception, e);
exceptionThrown.set(true);
}
});
assertThat(exceptionThrown.get(), equalTo(true));
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.indexlifecycle;
import org.elasticsearch.common.io.stream.Writeable.Reader;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractSerializingTestCase;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.equalTo;
public class ReadOnlyActionTests extends AbstractSerializingTestCase<ReadOnlyAction> {
@Override
protected ReadOnlyAction doParseInstance(XContentParser parser) {
return ReadOnlyAction.parse(parser);
}
@Override
protected ReadOnlyAction createTestInstance() {
return new ReadOnlyAction();
}
@Override
protected Reader<ReadOnlyAction> instanceReader() {
return ReadOnlyAction::new;
}
public void testToSteps() {
ReadOnlyAction action = createTestInstance();
String phase = randomAlphaOfLengthBetween(1, 10);
StepKey nextStepKey = new StepKey(randomAlphaOfLengthBetween(1, 10), randomAlphaOfLengthBetween(1, 10),
randomAlphaOfLengthBetween(1, 10));
List<Step> steps = action.toSteps(null, phase, nextStepKey);
assertNotNull(steps);
assertEquals(1, steps.size());
StepKey expectedFirstStepKey = new StepKey(phase, ReadOnlyAction.NAME, ReadOnlyStep.NAME);
ReadOnlyStep firstStep = (ReadOnlyStep) steps.get(0);
assertThat(firstStep.getKey(), equalTo(expectedFirstStepKey));
assertThat(firstStep.getNextStepKey(), equalTo(nextStepKey));
}
}

View File

@ -14,11 +14,24 @@ import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.EqualsHashCodeTestUtils;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
import static org.hamcrest.Matchers.equalTo;
public class ReadOnlyStepTests extends ESTestCase {
public ReadOnlyStep createRandomInstance() {
StepKey stepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
StepKey nextStepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
return new ReadOnlyStep(stepKey, nextStepKey);
}
public void testHashcodeAndEquals() {
EqualsHashCodeTestUtils.checkEqualsAndHashCode(createRandomInstance(),
instance -> new ReadOnlyStep(instance.getKey(), instance.getNextStepKey()));
}
public void testPerformAction() {
Settings.Builder indexSettingsBuilder = settings(Version.CURRENT);
if (randomBoolean()) {

View File

@ -5,11 +5,224 @@
*/
package org.elasticsearch.xpack.core.indexlifecycle;
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.segments.IndexSegments;
import org.elasticsearch.action.admin.indices.segments.IndexShardSegments;
import org.elasticsearch.action.admin.indices.segments.IndicesSegmentResponse;
import org.elasticsearch.action.admin.indices.segments.ShardSegments;
import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.engine.Segment;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.EqualsHashCodeTestUtils;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
import org.elasticsearch.test.ESTestCase;
import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Spliterator;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Matchers.any;
public class SegmentCountStepTests extends ESTestCase {
public void test() {
public SegmentCountStep createRandomInstance() {
Step.StepKey stepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
StepKey nextStepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
int maxNumSegments = randomIntBetween(1, 10);
boolean bestCompression = randomBoolean();
return new SegmentCountStep(stepKey, nextStepKey, null, maxNumSegments, bestCompression);
}
public SegmentCountStep mutateInstance(SegmentCountStep instance) {
StepKey key = instance.getKey();
StepKey nextKey = instance.getNextStepKey();
int maxNumSegments = instance.getMaxNumSegments();
boolean bestCompression = instance.isBestCompression();
switch (between(0, 3)) {
case 0:
key = new StepKey(key.getPhase(), key.getAction(), key.getName() + randomAlphaOfLength(5));
break;
case 1:
nextKey = new StepKey(key.getPhase(), key.getAction(), key.getName() + randomAlphaOfLength(5));
break;
case 2:
maxNumSegments += 1;
break;
case 3:
bestCompression = !bestCompression;
break;
default:
throw new AssertionError("Illegal randomisation branch");
}
return new SegmentCountStep(key, nextKey, null, maxNumSegments, bestCompression);
}
public void testHashcodeAndEquals() {
EqualsHashCodeTestUtils.checkEqualsAndHashCode(createRandomInstance(),
instance -> new SegmentCountStep(instance.getKey(), instance.getNextStepKey(),
null, instance.getMaxNumSegments(), instance.isBestCompression()), this::mutateInstance);
}
public void testIsConditionMet() {
int maxNumSegments = randomIntBetween(3, 10);
Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
Client client = Mockito.mock(Client.class);
AdminClient adminClient = Mockito.mock(AdminClient.class);
IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class);
IndicesSegmentResponse indicesSegmentResponse = Mockito.mock(IndicesSegmentResponse.class);
IndexSegments indexSegments = Mockito.mock(IndexSegments.class);
IndexShardSegments indexShardSegments = Mockito.mock(IndexShardSegments.class);
Map<Integer, IndexShardSegments> indexShards = Collections.singletonMap(0, indexShardSegments);
ShardSegments shardSegmentsOne = Mockito.mock(ShardSegments.class);
ShardSegments[] shardSegmentsArray = new ShardSegments[] { shardSegmentsOne };
Spliterator<IndexShardSegments> iss = indexShards.values().spliterator();
List<Segment> segments = new ArrayList<>();
for (int i = 0; i < maxNumSegments - randomIntBetween(0, 3); i++) {
segments.add(null);
}
Mockito.when(indicesSegmentResponse.getStatus()).thenReturn(RestStatus.OK);
Mockito.when(indicesSegmentResponse.getIndices()).thenReturn(Collections.singletonMap(index.getName(), indexSegments));
Mockito.when(indexSegments.spliterator()).thenReturn(iss);
Mockito.when(indexShardSegments.getShards()).thenReturn(shardSegmentsArray);
Mockito.when(shardSegmentsOne.getSegments()).thenReturn(segments);
Mockito.when(client.admin()).thenReturn(adminClient);
Mockito.when(adminClient.indices()).thenReturn(indicesClient);
Step.StepKey stepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
StepKey nextStepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
boolean bestCompression = randomBoolean();
Mockito.doAnswer(invocationOnMock -> {
@SuppressWarnings("unchecked")
ActionListener<IndicesSegmentResponse> listener = (ActionListener<IndicesSegmentResponse>) invocationOnMock.getArguments()[1];
listener.onResponse(indicesSegmentResponse);
return null;
}).when(indicesClient).segments(any(), any());
SetOnce<Boolean> conditionMetResult = new SetOnce<>();
SegmentCountStep step = new SegmentCountStep(stepKey, nextStepKey, client, maxNumSegments, bestCompression);
step.evaluateCondition(index, new AsyncWaitStep.Listener() {
@Override
public void onResponse(boolean conditionMet) {
conditionMetResult.set(conditionMet);
}
@Override
public void onFailure(Exception e) {
throw new AssertionError("unexpected method call");
}
});
assertTrue(conditionMetResult.get());
}
public void testIsConditionFails() {
int maxNumSegments = randomIntBetween(3, 10);
Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
Client client = Mockito.mock(Client.class);
AdminClient adminClient = Mockito.mock(AdminClient.class);
IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class);
IndicesSegmentResponse indicesSegmentResponse = Mockito.mock(IndicesSegmentResponse.class);
IndexSegments indexSegments = Mockito.mock(IndexSegments.class);
IndexShardSegments indexShardSegments = Mockito.mock(IndexShardSegments.class);
Map<Integer, IndexShardSegments> indexShards = Collections.singletonMap(0, indexShardSegments);
ShardSegments shardSegmentsOne = Mockito.mock(ShardSegments.class);
ShardSegments[] shardSegmentsArray = new ShardSegments[] { shardSegmentsOne };
Spliterator<IndexShardSegments> iss = indexShards.values().spliterator();
List<Segment> segments = new ArrayList<>();
for (int i = 0; i < maxNumSegments + randomIntBetween(1, 3); i++) {
segments.add(null);
}
Mockito.when(indicesSegmentResponse.getStatus()).thenReturn(RestStatus.OK);
Mockito.when(indicesSegmentResponse.getIndices()).thenReturn(Collections.singletonMap(index.getName(), indexSegments));
Mockito.when(indexSegments.spliterator()).thenReturn(iss);
Mockito.when(indexShardSegments.getShards()).thenReturn(shardSegmentsArray);
Mockito.when(shardSegmentsOne.getSegments()).thenReturn(segments);
Mockito.when(client.admin()).thenReturn(adminClient);
Mockito.when(adminClient.indices()).thenReturn(indicesClient);
Step.StepKey stepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
StepKey nextStepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
boolean bestCompression = randomBoolean();
Mockito.doAnswer(invocationOnMock -> {
@SuppressWarnings("unchecked")
ActionListener<IndicesSegmentResponse> listener = (ActionListener<IndicesSegmentResponse>) invocationOnMock.getArguments()[1];
listener.onResponse(indicesSegmentResponse);
return null;
}).when(indicesClient).segments(any(), any());
SetOnce<Boolean> conditionMetResult = new SetOnce<>();
SegmentCountStep step = new SegmentCountStep(stepKey, nextStepKey, client, maxNumSegments, bestCompression);
step.evaluateCondition(index, new AsyncWaitStep.Listener() {
@Override
public void onResponse(boolean conditionMet) {
conditionMetResult.set(conditionMet);
}
@Override
public void onFailure(Exception e) {
throw new AssertionError("unexpected method call");
}
});
assertFalse(conditionMetResult.get());
}
public void testThrowsException() {
Exception exception = new RuntimeException("error");
Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
Client client = Mockito.mock(Client.class);
AdminClient adminClient = Mockito.mock(AdminClient.class);
IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class);
Mockito.when(client.admin()).thenReturn(adminClient);
Mockito.when(adminClient.indices()).thenReturn(indicesClient);
Step.StepKey stepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
StepKey nextStepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
int maxNumSegments = randomIntBetween(3, 10);
boolean bestCompression = randomBoolean();
Mockito.doAnswer(invocationOnMock -> {
@SuppressWarnings("unchecked")
ActionListener<IndicesSegmentResponse> listener = (ActionListener<IndicesSegmentResponse>) invocationOnMock.getArguments()[1];
listener.onFailure(exception);
return null;
}).when(indicesClient).segments(any(), any());
SetOnce<Boolean> exceptionThrown = new SetOnce<>();
SegmentCountStep step = new SegmentCountStep(stepKey, nextStepKey, client, maxNumSegments, bestCompression);
step.evaluateCondition(index, new AsyncWaitStep.Listener() {
@Override
public void onResponse(boolean conditionMet) {
throw new AssertionError("unexpected method call");
}
@Override
public void onFailure(Exception e) {
assertThat(e, equalTo(exception));
exceptionThrown.set(true);
}
});
assertTrue(exceptionThrown.get());
}
}

View File

@ -31,7 +31,7 @@ public class TimeseriesLifecycleTypeTests extends ESTestCase {
private static final AllocateAction TEST_ALLOCATE_ACTION = new AllocateAction(Collections.singletonMap("node", "node1"),null, null);
private static final DeleteAction TEST_DELETE_ACTION = new DeleteAction();
private static final ForceMergeAction TEST_FORCE_MERGE_ACTION = new ForceMergeAction(1);
private static final ForceMergeAction TEST_FORCE_MERGE_ACTION = new ForceMergeAction(1, true);
private static final ReplicasAction TEST_REPLICAS_ACTION = new ReplicasAction(1);
private static final RolloverAction TEST_ROLLOVER_ACTION = new RolloverAction("", new ByteSizeValue(1), null, null);
private static final ShrinkAction TEST_SHRINK_ACTION = new ShrinkAction(1);

View File

@ -0,0 +1,68 @@
/*
* 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.indexlifecycle;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.codec.CodecService;
import org.elasticsearch.index.engine.EngineConfig;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.EqualsHashCodeTestUtils;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
public class UpdateBestCompressionSettingsStepTests extends ESTestCase {
public UpdateBestCompressionSettingsStep createRandomInstance() {
StepKey stepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
StepKey nextStepKey = new StepKey(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10));
return new UpdateBestCompressionSettingsStep(stepKey, nextStepKey);
}
public void testHashcodeAndEquals() {
EqualsHashCodeTestUtils.checkEqualsAndHashCode(createRandomInstance(),
instance -> new UpdateBestCompressionSettingsStep(instance.getKey(), instance.getNextStepKey()));
}
public void testPerformAction() {
IndexMetaData indexMetadata = IndexMetaData.builder(randomAlphaOfLength(5))
.settings(settings(Version.CURRENT))
.numberOfShards(1)
.numberOfReplicas(0).build();
MetaData metaData = MetaData.builder()
.persistentSettings(settings(Version.CURRENT).build())
.put(IndexMetaData.builder(indexMetadata))
.build();
Index index = indexMetadata.getIndex();
ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT).metaData(metaData).build();
UpdateBestCompressionSettingsStep step = createRandomInstance();
ClusterState newState = step.performAction(index, clusterState);
assertNotSame(clusterState, newState);
IndexMetaData newIndexMetadata = newState.metaData().index(index);
assertNotNull(newIndexMetadata);
assertNotSame(indexMetadata, newIndexMetadata);
assertTrue(EngineConfig.INDEX_CODEC_SETTING.exists(newIndexMetadata.getSettings()));
assertTrue(CodecService.BEST_COMPRESSION_CODEC.equals(
newIndexMetadata.getSettings().get(EngineConfig.INDEX_CODEC_SETTING.getKey())));
}
public void testPerformActionNoIndex() {
MetaData metaData = MetaData.builder().persistentSettings(settings(Version.CURRENT).build()).build();
Index index = new Index("invalid_index", "invalid_index_id");
ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT).metaData(metaData).build();
UpdateBestCompressionSettingsStep step = createRandomInstance();
ClusterState newState = step.performAction(index, clusterState);
assertSame(clusterState, newState);
}
}