[ILM] make alias swapping atomic (#35972)

In the current implementation, there is a time between the
ShrinkStep and the ShrinkSetAliasStep that both the source and
target indices will be present with the same aliases. This means
that queries to during this time will query both and return
duplicates. This fixes that scenario by moving the alias inheritance
to the same aliases update request as is done in ShrinkSetAliasStep
This commit is contained in:
Tal Levy 2018-11-29 11:11:20 -08:00 committed by GitHub
parent ba3ee98943
commit fa5f551b80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 14 deletions

View File

@ -9,6 +9,7 @@ import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import java.util.Objects; import java.util.Objects;
@ -36,11 +37,20 @@ public class ShrinkSetAliasStep extends AsyncActionStep {
String index = indexMetaData.getIndex().getName(); String index = indexMetaData.getIndex().getName();
// get target shrink index // get target shrink index
String targetIndexName = shrunkIndexPrefix + index; String targetIndexName = shrunkIndexPrefix + index;
IndicesAliasesRequest aliasesRequest = new IndicesAliasesRequest() IndicesAliasesRequest aliasesRequest = new IndicesAliasesRequest()
.addAliasAction(IndicesAliasesRequest.AliasActions.removeIndex().index(index)) .addAliasAction(IndicesAliasesRequest.AliasActions.removeIndex().index(index))
.addAliasAction(IndicesAliasesRequest.AliasActions.add().index(targetIndexName).alias(index)); .addAliasAction(IndicesAliasesRequest.AliasActions.add().index(targetIndexName).alias(index));
// copy over other aliases from original index
indexMetaData.getAliases().values().spliterator().forEachRemaining(aliasMetaDataObjectCursor -> {
AliasMetaData aliasMetaDataToAdd = aliasMetaDataObjectCursor.value;
// inherit all alias properties except `is_write_index`
aliasesRequest.addAliasAction(IndicesAliasesRequest.AliasActions.add()
.index(targetIndexName).alias(aliasMetaDataToAdd.alias())
.indexRouting(aliasMetaDataToAdd.indexRouting())
.searchRouting(aliasMetaDataToAdd.searchRouting())
.filter(aliasMetaDataToAdd.filter() == null ? null : aliasMetaDataToAdd.filter().string())
.writeIndex(null));
});
getClient().admin().indices().aliases(aliasesRequest, ActionListener.wrap(response -> getClient().admin().indices().aliases(aliasesRequest, ActionListener.wrap(response ->
listener.onResponse(true), listener::onFailure)); listener.onResponse(true), listener::onFailure));
} }

View File

@ -6,7 +6,6 @@
package org.elasticsearch.xpack.core.indexlifecycle; package org.elasticsearch.xpack.core.indexlifecycle;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
@ -58,13 +57,9 @@ public class ShrinkStep extends AsyncActionStep {
String shrunkenIndexName = shrunkIndexPrefix + indexMetaData.getIndex().getName(); String shrunkenIndexName = shrunkIndexPrefix + indexMetaData.getIndex().getName();
ResizeRequest resizeRequest = new ResizeRequest(shrunkenIndexName, indexMetaData.getIndex().getName()); ResizeRequest resizeRequest = new ResizeRequest(shrunkenIndexName, indexMetaData.getIndex().getName());
indexMetaData.getAliases().values().spliterator().forEachRemaining(aliasMetaDataObjectCursor -> {
resizeRequest.getTargetIndexRequest().alias(new Alias(aliasMetaDataObjectCursor.value.alias()));
});
resizeRequest.getTargetIndexRequest().settings(relevantTargetSettings); resizeRequest.getTargetIndexRequest().settings(relevantTargetSettings);
getClient().admin().indices().resizeIndex(resizeRequest, ActionListener.wrap(response -> { getClient().admin().indices().resizeIndex(resizeRequest, ActionListener.wrap(response -> {
// TODO(talevy): when is this not acknowledged?
listener.onResponse(response.isAcknowledged()); listener.onResponse(response.isAcknowledged());
}, listener::onFailure)); }, listener::onFailure));

View File

@ -14,6 +14,7 @@ import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.AdminClient; import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.client.IndicesAdminClient; import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.xpack.core.indexlifecycle.AsyncActionStep.Listener; import org.elasticsearch.xpack.core.indexlifecycle.AsyncActionStep.Listener;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey; import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
@ -71,15 +72,33 @@ public class ShrinkSetAliasStepTests extends AbstractStepTestCase<ShrinkSetAlias
} }
public void testPerformAction() { public void testPerformAction() {
IndexMetaData indexMetaData = IndexMetaData.builder(randomAlphaOfLength(10)).settings(settings(Version.CURRENT)) IndexMetaData.Builder indexMetaDataBuilder = IndexMetaData.builder(randomAlphaOfLength(10)).settings(settings(Version.CURRENT))
.numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5));
AliasMetaData.Builder aliasBuilder = AliasMetaData.builder(randomAlphaOfLengthBetween(3, 10));
if (randomBoolean()) {
aliasBuilder.routing(randomAlphaOfLengthBetween(3, 10));
}
if (randomBoolean()) {
aliasBuilder.searchRouting(randomAlphaOfLengthBetween(3, 10));
}
if (randomBoolean()) {
aliasBuilder.indexRouting(randomAlphaOfLengthBetween(3, 10));
}
String aliasMetaDataFilter = randomBoolean() ? null : "{\"term\":{\"year\":2016}}";
aliasBuilder.filter(aliasMetaDataFilter);
aliasBuilder.writeIndex(randomBoolean());
AliasMetaData aliasMetaData = aliasBuilder.build();
IndexMetaData indexMetaData = indexMetaDataBuilder.putAlias(aliasMetaData).build();
ShrinkSetAliasStep step = createRandomInstance(); ShrinkSetAliasStep step = createRandomInstance();
String sourceIndex = indexMetaData.getIndex().getName(); String sourceIndex = indexMetaData.getIndex().getName();
String shrunkenIndex = step.getShrunkIndexPrefix() + sourceIndex; String shrunkenIndex = step.getShrunkIndexPrefix() + sourceIndex;
List<AliasActions> expectedAliasActions = Arrays.asList( List<AliasActions> expectedAliasActions = Arrays.asList(
IndicesAliasesRequest.AliasActions.removeIndex().index(sourceIndex), IndicesAliasesRequest.AliasActions.removeIndex().index(sourceIndex),
IndicesAliasesRequest.AliasActions.add().index(shrunkenIndex).alias(sourceIndex)); IndicesAliasesRequest.AliasActions.add().index(shrunkenIndex).alias(sourceIndex),
IndicesAliasesRequest.AliasActions.add().index(shrunkenIndex).alias(aliasMetaData.alias())
.searchRouting(aliasMetaData.searchRouting()).indexRouting(aliasMetaData.indexRouting())
.filter(aliasMetaDataFilter).writeIndex(null));
AdminClient adminClient = Mockito.mock(AdminClient.class); AdminClient adminClient = Mockito.mock(AdminClient.class);
IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class); IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class);

View File

@ -8,7 +8,6 @@ package org.elasticsearch.xpack.core.indexlifecycle;
import org.apache.lucene.util.SetOnce; import org.apache.lucene.util.SetOnce;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse; import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
@ -112,7 +111,7 @@ public class ShrinkStepTests extends AbstractStepTestCase<ShrinkStep> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
ActionListener<ResizeResponse> listener = (ActionListener<ResizeResponse>) invocation.getArguments()[1]; ActionListener<ResizeResponse> listener = (ActionListener<ResizeResponse>) invocation.getArguments()[1];
assertThat(request.getSourceIndex(), equalTo(sourceIndexMetaData.getIndex().getName())); assertThat(request.getSourceIndex(), equalTo(sourceIndexMetaData.getIndex().getName()));
assertThat(request.getTargetIndexRequest().aliases(), equalTo(Collections.singleton(new Alias("my_alias")))); assertThat(request.getTargetIndexRequest().aliases(), equalTo(Collections.emptySet()));
assertThat(request.getTargetIndexRequest().settings(), equalTo(Settings.builder() assertThat(request.getTargetIndexRequest().settings(), equalTo(Settings.builder()
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, step.getNumberOfShards()) .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, step.getNumberOfShards())
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, sourceIndexMetaData.getNumberOfReplicas()) .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, sourceIndexMetaData.getNumberOfReplicas())