From f4a37a9517769194cf8b2667fef5e130a1ec6bb1 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 20 Aug 2020 16:44:17 +0100 Subject: [PATCH 01/34] Enable the _tier attribute as a node filter --- .../org/elasticsearch/cluster/node/DiscoveryNodeFilters.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java index aacda43864e51..ee9f124306cff 100644 --- a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java +++ b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java @@ -181,6 +181,9 @@ public boolean match(DiscoveryNode node) { } } } + } else if ("_tier".equals(attr)) { + // Always allow _tier as an attribute, will be handled elsewhere + return true; } else { String nodeAttributeValue = node.getAttributes().get(attr); if (nodeAttributeValue == null) { From 024a0de407f25e77870f792e3b023e8825ee0cb2 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 20 Aug 2020 16:45:56 +0100 Subject: [PATCH 02/34] ILM: inject a migrate step to migrate data between data tiers --- .../xpack/core/XPackClientPlugin.java | 2 + .../xpack/core/ilm/AllocationRoutedStep.java | 26 +++- .../xpack/core/ilm/MigrateAction.java | 112 ++++++++++++++++++ .../xpack/core/ilm/RolloverAction.java | 2 +- .../core/ilm/TimeseriesLifecycleType.java | 112 +++++++++++++----- .../ilm/TimeseriesLifecycleTypeTests.java | 81 ++++++++++++- .../xpack/ilm/IndexLifecycle.java | 5 +- 7 files changed, 301 insertions(+), 39 deletions(-) create mode 100644 x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java index 7f599d3bc89f6..2b6ecfad8b5b5 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java @@ -53,6 +53,7 @@ import org.elasticsearch.xpack.core.ilm.IndexLifecycleMetadata; import org.elasticsearch.xpack.core.ilm.LifecycleAction; import org.elasticsearch.xpack.core.ilm.LifecycleType; +import org.elasticsearch.xpack.core.ilm.MigrateAction; import org.elasticsearch.xpack.core.ilm.ReadOnlyAction; import org.elasticsearch.xpack.core.ilm.RolloverAction; import org.elasticsearch.xpack.core.ilm.SearchableSnapshotAction; @@ -484,6 +485,7 @@ public List getNamedWriteables() { new NamedWriteableRegistry.Entry(LifecycleAction.class, UnfollowAction.NAME, UnfollowAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, WaitForSnapshotAction.NAME, WaitForSnapshotAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, SearchableSnapshotAction.NAME, SearchableSnapshotAction::new), + new NamedWriteableRegistry.Entry(LifecycleAction.class, MigrateAction.NAME, MigrateAction::new), // Transforms new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.TRANSFORM, TransformFeatureSetUsage::new), new NamedWriteableRegistry.Entry(PersistentTaskParams.class, TransformField.TASK_NAME, TransformTaskParams::new), diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStep.java index fa3257d3f9c12..f460dca8c6b97 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStep.java @@ -6,7 +6,6 @@ package org.elasticsearch.xpack.core.ilm; import com.carrotsearch.hppc.cursors.ObjectCursor; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.elasticsearch.action.support.ActiveShardCount; @@ -22,15 +21,19 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.ImmutableOpenIntMap; import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.Index; +import org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider; import java.io.IOException; -import java.util.Collections; +import java.util.HashSet; +import java.util.List; import java.util.Objects; +import java.util.Set; /** * Checks whether all shards have been correctly routed in response to an update to the allocation rules for an index. @@ -40,8 +43,23 @@ public class AllocationRoutedStep extends ClusterStateWaitStep { private static final Logger logger = LogManager.getLogger(AllocationRoutedStep.class); - private static final AllocationDeciders ALLOCATION_DECIDERS = new AllocationDeciders(Collections.singletonList( - new FilterAllocationDecider(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)))); + private static final Set> ALL_CLUSTER_SETTINGS; + + static { + Set> allSettings = new HashSet<>(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + allSettings.add(DataTierAllocationDecider.CLUSTER_ROUTING_REQUIRE_SETTING); + allSettings.add(DataTierAllocationDecider.CLUSTER_ROUTING_INCLUDE_SETTING); + allSettings.add(DataTierAllocationDecider.CLUSTER_ROUTING_EXCLUDE_SETTING); + ALL_CLUSTER_SETTINGS = allSettings; + } + + private static final AllocationDeciders ALLOCATION_DECIDERS = new AllocationDeciders( + List.of( + new FilterAllocationDecider(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, + ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)), + new DataTierAllocationDecider(new ClusterSettings(Settings.EMPTY, ALL_CLUSTER_SETTINGS)) + ) + ); AllocationRoutedStep(StepKey key, StepKey nextStepKey) { super(key, nextStepKey); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java new file mode 100644 index 0000000000000..b9b729feda8aa --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java @@ -0,0 +1,112 @@ +/* + * 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.ilm; + +import org.elasticsearch.client.Client; +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.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.xpack.core.ilm.Step.StepKey; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * A {@link LifecycleAction} which enables or disables the automatic migration of data between + * {@link org.elasticsearch.xpack.core.DataTier}s. + */ +public class MigrateAction implements LifecycleAction { + public static final String NAME = "migrate"; + public static final ParseField ENABLED_FIELD = new ParseField("enabled"); + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(NAME, + a -> new MigrateAction((boolean) a[0])); + + static { + PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), ENABLED_FIELD); + } + + private final boolean enabled; + + public static MigrateAction parse(XContentParser parser) { + return PARSER.apply(parser, null); + } + + public MigrateAction(boolean enabled) { + this.enabled = enabled; + } + + public MigrateAction(StreamInput in) throws IOException { + this(in.readBoolean()); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeBoolean(enabled); + } + + @Override + public String getWriteableName() { + return NAME; + } + + public boolean isEnabled() { + return enabled; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(ENABLED_FIELD.getPreferredName(), enabled); + builder.endObject(); + return builder; + } + + @Override + public boolean isSafeAction() { + return true; + } + + @Override + public List toSteps(Client client, String phase, StepKey nextStepKey) { + if (enabled) { + Map include = Map.of("_tier", "data_" + phase); + AllocateAction migrateDataAction = new AllocateAction(null, include, null, null); + return migrateDataAction.toSteps(client, phase, nextStepKey); + } else { + return List.of(); + } + } + + @Override + public int hashCode() { + return Objects.hash(enabled); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj.getClass() != getClass()) { + return false; + } + MigrateAction other = (MigrateAction) obj; + return Objects.equals(enabled, other.enabled); + } + + @Override + public String toString() { + return Strings.toString(this); + } + +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/RolloverAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/RolloverAction.java index bf1cd9e2b9fd8..6fa1114817130 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/RolloverAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/RolloverAction.java @@ -27,7 +27,7 @@ import java.util.Objects; /** - * A {@link LifecycleAction} which deletes the index. + * A {@link LifecycleAction} which rolls over the index. */ public class RolloverAction implements LifecycleAction { public static final String NAME = "rollover"; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java index c23143aecda0d..4f7a3b21b99ed 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.core.ilm; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.util.set.Sets; @@ -19,6 +20,8 @@ import java.util.Set; import java.util.stream.Collectors; +import static java.util.stream.Collectors.toList; + /** * Represents the lifecycle of an index from creation to deletion. A * {@link TimeseriesLifecycleType} is made up of a set of {@link Phase}s which it will @@ -40,18 +43,18 @@ public class TimeseriesLifecycleType implements LifecycleType { static final List ORDERED_VALID_HOT_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, RolloverAction.NAME, ForceMergeAction.NAME); static final List ORDERED_VALID_WARM_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, ReadOnlyAction.NAME, - AllocateAction.NAME, ShrinkAction.NAME, ForceMergeAction.NAME); - static final List ORDERED_VALID_COLD_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, AllocateAction.NAME, - FreezeAction.NAME, SearchableSnapshotAction.NAME); - static final List ORDERED_VALID_FROZEN_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, AllocateAction.NAME, - FreezeAction.NAME, SearchableSnapshotAction.NAME); + MigrateAction.NAME, AllocateAction.NAME, ShrinkAction.NAME, ForceMergeAction.NAME); + static final List ORDERED_VALID_COLD_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, + MigrateAction.NAME, AllocateAction.NAME, FreezeAction.NAME, SearchableSnapshotAction.NAME); + static final List ORDERED_VALID_FROZEN_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, + MigrateAction.NAME, AllocateAction.NAME, FreezeAction.NAME, SearchableSnapshotAction.NAME); static final List ORDERED_VALID_DELETE_ACTIONS = Arrays.asList(WaitForSnapshotAction.NAME, DeleteAction.NAME); static final Set VALID_HOT_ACTIONS = Sets.newHashSet(ORDERED_VALID_HOT_ACTIONS); static final Set VALID_WARM_ACTIONS = Sets.newHashSet(ORDERED_VALID_WARM_ACTIONS); static final Set VALID_COLD_ACTIONS = Sets.newHashSet(ORDERED_VALID_COLD_ACTIONS); static final Set VALID_FROZEN_ACTIONS = Sets.newHashSet(ORDERED_VALID_FROZEN_ACTIONS); static final Set VALID_DELETE_ACTIONS = Sets.newHashSet(ORDERED_VALID_DELETE_ACTIONS); - private static Map> ALLOWED_ACTIONS = new HashMap<>(); + private static final Map> ALLOWED_ACTIONS = new HashMap<>(); static { ALLOWED_ACTIONS.put(HOT_PHASE, VALID_HOT_ACTIONS); @@ -86,12 +89,38 @@ public List getOrderedPhases(Map phases) { actionMap.put(UnfollowAction.NAME, new UnfollowAction()); phase = new Phase(phase.getName(), phase.getMinimumAge(), actionMap); } + + if (shouldMigrateDataToTiers(phase)) { + Map actionMap = new HashMap<>(phase.getActions()); + actionMap.put(MigrateAction.NAME, new MigrateAction(true)); + phase = new Phase(phase.getName(), phase.getMinimumAge(), actionMap); + } + orderedPhases.add(phase); } } return orderedPhases; } + boolean shouldMigrateDataToTiers(Phase phase) { + AllocateAction allocateAction = (AllocateAction) phase.getActions().get(AllocateAction.NAME); + if (allocateAction != null) { + if (allocateAction.getExclude().isEmpty() == false || + allocateAction.getInclude().isEmpty() == false || + allocateAction.getRequire().isEmpty() == false) { + // we won't automatically migrate the data if an allocate action that defines any allocation rule is present + return false; + } + } + + MigrateAction migrateAction = (MigrateAction) phase.getActions().get(MigrateAction.NAME); + if (migrateAction != null) { + // user configured the {@link MigrateAction} already so we won't automatically configure it + return false; + } + return true; + } + @Override public String getNextPhaseName(String currentPhaseName, Map phases) { int index = VALID_PHASES.indexOf(currentPhaseName); @@ -121,7 +150,7 @@ public String getPreviousPhaseName(String currentPhaseName, Map p throw new IllegalArgumentException("[" + currentPhaseName + "] is not a valid phase for lifecycle type [" + TYPE + "]"); } else { // Find the previous phase before `index` that exists in `phases` and return it - while (--index >=0) { + while (--index >= 0) { String phaseName = VALID_PHASES.get(index); if (phases.containsKey(phaseName)) { return phaseName; @@ -139,19 +168,19 @@ public List getOrderedActions(Phase phase) { switch (phase.getName()) { case HOT_PHASE: return ORDERED_VALID_HOT_ACTIONS.stream().map(a -> actions.getOrDefault(a, null)) - .filter(Objects::nonNull).collect(Collectors.toList()); + .filter(Objects::nonNull).collect(toList()); case WARM_PHASE: - return ORDERED_VALID_WARM_ACTIONS.stream() .map(a -> actions.getOrDefault(a, null)) - .filter(Objects::nonNull).collect(Collectors.toList()); + return ORDERED_VALID_WARM_ACTIONS.stream().map(a -> actions.getOrDefault(a, null)) + .filter(Objects::nonNull).collect(toList()); case COLD_PHASE: return ORDERED_VALID_COLD_ACTIONS.stream().map(a -> actions.getOrDefault(a, null)) - .filter(Objects::nonNull).collect(Collectors.toList()); + .filter(Objects::nonNull).collect(toList()); case FROZEN_PHASE: return ORDERED_VALID_FROZEN_ACTIONS.stream().map(a -> actions.getOrDefault(a, null)) - .filter(Objects::nonNull).collect(Collectors.toList()); + .filter(Objects::nonNull).collect(toList()); case DELETE_PHASE: return ORDERED_VALID_DELETE_ACTIONS.stream().map(a -> actions.getOrDefault(a, null)) - .filter(Objects::nonNull).collect(Collectors.toList()); + .filter(Objects::nonNull).collect(toList()); default: throw new IllegalArgumentException("lifecycle type[" + TYPE + "] does not support phase[" + phase.getName() + "]"); } @@ -161,29 +190,29 @@ public List getOrderedActions(Phase phase) { public String getNextActionName(String currentActionName, Phase phase) { List orderedActionNames; switch (phase.getName()) { - case HOT_PHASE: - orderedActionNames = ORDERED_VALID_HOT_ACTIONS; - break; - case WARM_PHASE: - orderedActionNames = ORDERED_VALID_WARM_ACTIONS; - break; - case COLD_PHASE: - orderedActionNames = ORDERED_VALID_COLD_ACTIONS; - break; - case FROZEN_PHASE: - orderedActionNames = ORDERED_VALID_FROZEN_ACTIONS; - break; - case DELETE_PHASE: - orderedActionNames = ORDERED_VALID_DELETE_ACTIONS; - break; - default: - throw new IllegalArgumentException("lifecycle type [" + TYPE + "] does not support phase [" + phase.getName() + "]"); + case HOT_PHASE: + orderedActionNames = ORDERED_VALID_HOT_ACTIONS; + break; + case WARM_PHASE: + orderedActionNames = ORDERED_VALID_WARM_ACTIONS; + break; + case COLD_PHASE: + orderedActionNames = ORDERED_VALID_COLD_ACTIONS; + break; + case FROZEN_PHASE: + orderedActionNames = ORDERED_VALID_FROZEN_ACTIONS; + break; + case DELETE_PHASE: + orderedActionNames = ORDERED_VALID_DELETE_ACTIONS; + break; + default: + throw new IllegalArgumentException("lifecycle type [" + TYPE + "] does not support phase [" + phase.getName() + "]"); } int index = orderedActionNames.indexOf(currentActionName); if (index < 0) { throw new IllegalArgumentException("[" + currentActionName + "] is not a valid action for phase [" + phase.getName() - + "] in lifecycle type [" + TYPE + "]"); + + "] in lifecycle type [" + TYPE + "]"); } else { // Find the next action after `index` that exists in the phase and return it while (++index < orderedActionNames.size()) { @@ -208,7 +237,7 @@ public void validate(Collection phases) { phase.getActions().forEach((actionName, action) -> { if (ALLOWED_ACTIONS.get(phase.getName()).contains(actionName) == false) { throw new IllegalArgumentException("invalid action [" + actionName + "] " + - "defined in phase [" + phase.getName() +"]"); + "defined in phase [" + phase.getName() + "]"); } }); }); @@ -226,5 +255,24 @@ public void validate(Collection phases) { "] action may not be used in the [" + HOT_PHASE + "] phase without an accompanying [" + RolloverAction.NAME + "] action"); } + + // look for phases that have the migrate action enabled and also specify allocation rules via the AllocateAction + String phasesWithConflictingMigrationActions = phases.stream() + .filter(phase -> phase.getActions().containsKey(MigrateAction.NAME) && + ((MigrateAction) phase.getActions().get(MigrateAction.NAME)).isEnabled() && + phase.getActions().containsKey(AllocateAction.NAME) && + definesAllocationRules((AllocateAction) phase.getActions().get(AllocateAction.NAME)) + ) + .map(Phase::getName) + .collect(Collectors.joining(",")); + if (Strings.hasText(phasesWithConflictingMigrationActions)) { + throw new IllegalArgumentException("phases [" + phasesWithConflictingMigrationActions + "] specify an enabled " + + MigrateAction.NAME + " action and the " + AllocateAction.NAME + " action. specify only one data migration action in these" + + " phases"); + } + } + + private boolean definesAllocationRules(AllocateAction action) { + return action.getRequire() != null || action.getInclude() != null || action.getExclude() != null; } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java index 9096bf242bfc2..4ff5bcd28c002 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java @@ -20,6 +20,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.COLD_PHASE; +import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.HOT_PHASE; import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.ORDERED_VALID_COLD_ACTIONS; import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.ORDERED_VALID_DELETE_ACTIONS; import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.ORDERED_VALID_FROZEN_ACTIONS; @@ -31,8 +33,11 @@ import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.VALID_HOT_ACTIONS; import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.VALID_PHASES; import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.VALID_WARM_ACTIONS; +import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.WARM_PHASE; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; public class TimeseriesLifecycleTypeTests extends ESTestCase { @@ -48,6 +53,9 @@ public class TimeseriesLifecycleTypeTests extends ESTestCase { private static final SetPriorityAction TEST_PRIORITY_ACTION = new SetPriorityAction(0); private static final UnfollowAction TEST_UNFOLLOW_ACTION = new UnfollowAction(); private static final SearchableSnapshotAction TEST_SEARCHABLE_SNAPSHOT_ACTION = new SearchableSnapshotAction("repo"); + // keeping the migrate action disabled as otherwise it could conflict with the allocate action if both are randomly selected for the + // same phase + private static final MigrateAction TEST_MIGRATE_ACTION = new MigrateAction(false); public void testValidatePhases() { boolean invalid = randomBoolean(); @@ -186,6 +194,26 @@ public void testValidateDeletePhase() { } } + public void testValidateConflictingDataMigrationConfigurations() { + Map actions = new HashMap<>(); + actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(true)); + actions.put(TEST_ALLOCATE_ACTION.getWriteableName(), TEST_ALLOCATE_ACTION); + List phases = List.of(new Phase(WARM_PHASE, TimeValue.ZERO, actions), new Phase(COLD_PHASE, TimeValue.ZERO, actions)); + + Exception validationException = expectThrows(IllegalArgumentException.class, + () -> TimeseriesLifecycleType.INSTANCE.validate(phases)); + assertThat(validationException.getMessage(), equalTo("phases [warm,cold] specify an enabled migrate action and the allocate" + + " action. specify only one data migration action in these phases")); + + // disabling the migrate action makes the phases definition valid as only the allocate action will perform data migration + actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(false)); + try { + TimeseriesLifecycleType.INSTANCE.validate(phases); + } catch (Exception e) { + fail("not expecting a failure for phases that specify one action that migrates data" + e); + } + } + public void testGetOrderedPhases() { Map phaseMap = new HashMap<>(); for (String phaseName : randomSubsetOf(randomIntBetween(0, VALID_PHASES.size()), VALID_PHASES)) { @@ -196,6 +224,18 @@ public void testGetOrderedPhases() { assertTrue(isSorted(TimeseriesLifecycleType.INSTANCE.getOrderedPhases(phaseMap), Phase::getName, VALID_PHASES)); } + public void testGetOrderedPhasesInsertsMigrateAction() { + Map phaseMap = new HashMap<>(); + phaseMap.put(HOT_PHASE, new Phase(HOT_PHASE, TimeValue.ZERO, Map.of())); + phaseMap.put(WARM_PHASE, new Phase(WARM_PHASE, TimeValue.ZERO, Map.of())); + + List orderedPhases = TimeseriesLifecycleType.INSTANCE.getOrderedPhases(phaseMap); + assertTrue(isSorted(orderedPhases, Phase::getName, VALID_PHASES)); + Phase warmPhase = orderedPhases.get(1); + assertThat(warmPhase, is(notNullValue())); + assertThat(warmPhase.getActions().get(MigrateAction.NAME), is(notNullValue())); + } + public void testUnfollowInjections() { assertTrue(isUnfollowInjected("hot", RolloverAction.NAME)); assertTrue(isUnfollowInjected("warm", ShrinkAction.NAME)); @@ -590,6 +630,41 @@ public void testGetNextActionName() { exception.getMessage()); } + public void testShouldMigrateDataToTiers() { + { + // the allocate action contain allocation rules + Map actions = new HashMap<>(); + actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(false)); + actions.put(TEST_ALLOCATE_ACTION.getWriteableName(), TEST_ALLOCATE_ACTION); + Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); + assertThat(TimeseriesLifecycleType.INSTANCE.shouldMigrateDataToTiers(phase), is(false)); + } + + { + // the allocate action only specifies the number of replicas + Map actions = new HashMap<>(); + actions.put(TEST_ALLOCATE_ACTION.getWriteableName(), new AllocateAction(2, null, null, null)); + Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); + assertThat(TimeseriesLifecycleType.INSTANCE.shouldMigrateDataToTiers(phase), is(true)); + } + + { + // there's an enabled migrate action specified + Map actions = new HashMap<>(); + actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(true)); + Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); + assertThat(TimeseriesLifecycleType.INSTANCE.shouldMigrateDataToTiers(phase), is(false)); + } + + { + // there's a disabled migrate action specified + Map actions = new HashMap<>(); + actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(false)); + Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); + assertThat(TimeseriesLifecycleType.INSTANCE.shouldMigrateDataToTiers(phase), is(false)); + } + } + private void assertNextActionName(String phaseName, String currentAction, String expectedNextAction, String... availableActionNames) { Map availableActions = convertActionNamesToActions(availableActionNames); Phase phase = new Phase(phaseName, TimeValue.ZERO, availableActions); @@ -626,7 +701,9 @@ private ConcurrentMap convertActionNamesToActions(Strin case SetPriorityAction.NAME: return new SetPriorityAction(0); case UnfollowAction.NAME: - return new UnfollowAction(); + return new UnfollowAction(); + case MigrateAction.NAME: + return new MigrateAction(true); } return new DeleteAction(); }).collect(Collectors.toConcurrentMap(LifecycleAction::getWriteableName, Function.identity())); @@ -698,6 +775,8 @@ private LifecycleAction getTestAction(String actionName) { return TEST_UNFOLLOW_ACTION; case SearchableSnapshotAction.NAME: return TEST_SEARCHABLE_SNAPSHOT_ACTION; + case MigrateAction.NAME: + return TEST_MIGRATE_ACTION; default: throw new IllegalArgumentException("unsupported timeseries phase action [" + actionName + "]"); } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java index ed3f97bb19e83..cb078bb3fd7ef 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java @@ -46,6 +46,7 @@ import org.elasticsearch.xpack.core.ilm.LifecycleAction; import org.elasticsearch.xpack.core.ilm.LifecycleSettings; import org.elasticsearch.xpack.core.ilm.LifecycleType; +import org.elasticsearch.xpack.core.ilm.MigrateAction; import org.elasticsearch.xpack.core.ilm.ReadOnlyAction; import org.elasticsearch.xpack.core.ilm.RolloverAction; import org.elasticsearch.xpack.core.ilm.SearchableSnapshotAction; @@ -232,7 +233,9 @@ public List getNa new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(WaitForSnapshotAction.NAME), WaitForSnapshotAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(SearchableSnapshotAction.NAME), - SearchableSnapshotAction::parse) + SearchableSnapshotAction::parse), + new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(MigrateAction.NAME), + MigrateAction::parse) ); } From 07dc8de3a23d5a57bd4c4d25a4c4c98c69558afc Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 3 Sep 2020 19:13:55 +0100 Subject: [PATCH 03/34] Add a DataTierMigrationRoutedStep and cleanup --- .../xpack/core/ilm/AllocationRoutedStep.java | 152 ++++-------------- .../core/ilm/DataTierMigrationRoutedStep.java | 127 +++++++++++++++ .../xpack/core/ilm/MigrateAction.java | 23 ++- .../core/ilm/TimeseriesLifecycleType.java | 23 ++- .../core/ilm/step/info/AllocationInfo.java | 113 +++++++++++++ .../core/ilm/AllocationRoutedStepTests.java | 19 ++- .../ilm/TimeseriesLifecycleTypeTests.java | 8 +- .../info}/AllocationRoutedStepInfoTests.java | 26 +-- 8 files changed, 323 insertions(+), 168 deletions(-) create mode 100644 x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java create mode 100644 x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java rename x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/{ => step/info}/AllocationRoutedStepInfoTests.java (59%) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStep.java index f460dca8c6b97..32be0f6ca679c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStep.java @@ -17,23 +17,15 @@ import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders; import org.elasticsearch.cluster.routing.allocation.decider.Decision; import org.elasticsearch.cluster.routing.allocation.decider.FilterAllocationDecider; -import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.ImmutableOpenIntMap; import org.elasticsearch.common.settings.ClusterSettings; -import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.ConstructingObjectParser; -import org.elasticsearch.common.xcontent.ToXContentObject; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.Index; -import org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider; -import java.io.IOException; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.util.Collections; + +import static org.elasticsearch.xpack.core.ilm.step.info.AllocationInfo.allShardsActiveAllocationInfo; +import static org.elasticsearch.xpack.core.ilm.step.info.AllocationInfo.waitingForActiveShardsAllocationInfo; /** * Checks whether all shards have been correctly routed in response to an update to the allocation rules for an index. @@ -43,23 +35,8 @@ public class AllocationRoutedStep extends ClusterStateWaitStep { private static final Logger logger = LogManager.getLogger(AllocationRoutedStep.class); - private static final Set> ALL_CLUSTER_SETTINGS; - - static { - Set> allSettings = new HashSet<>(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); - allSettings.add(DataTierAllocationDecider.CLUSTER_ROUTING_REQUIRE_SETTING); - allSettings.add(DataTierAllocationDecider.CLUSTER_ROUTING_INCLUDE_SETTING); - allSettings.add(DataTierAllocationDecider.CLUSTER_ROUTING_EXCLUDE_SETTING); - ALL_CLUSTER_SETTINGS = allSettings; - } - - private static final AllocationDeciders ALLOCATION_DECIDERS = new AllocationDeciders( - List.of( - new FilterAllocationDecider(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, - ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)), - new DataTierAllocationDecider(new ClusterSettings(Settings.EMPTY, ALL_CLUSTER_SETTINGS)) - ) - ); + private static final AllocationDeciders ALLOCATION_DECIDERS = new AllocationDeciders(Collections.singletonList( + new FilterAllocationDecider(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)))); AllocationRoutedStep(StepKey key, StepKey nextStepKey) { super(key, nextStepKey); @@ -75,13 +52,26 @@ public Result isConditionMet(Index index, ClusterState clusterState) { } if (ActiveShardCount.ALL.enoughShardsActive(clusterState, index.getName()) == false) { logger.debug("[{}] lifecycle action for index [{}] cannot make progress because not all shards are active", - getKey().getAction(), index.getName()); - return new Result(false, new Info(idxMeta.getNumberOfReplicas(), -1, false)); + getKey().getAction(), index.getName()); + return new Result(false, waitingForActiveShardsAllocationInfo(idxMeta.getNumberOfReplicas())); + } + int allocationPendingAllShards = getPendingAllocations(index, ALLOCATION_DECIDERS, clusterState); + + if (allocationPendingAllShards > 0) { + logger.debug("{} lifecycle action [{}] waiting for [{}] shards to be allocated to nodes matching the given filters", + index, getKey().getAction(), allocationPendingAllShards); + return new Result(false, allShardsActiveAllocationInfo(idxMeta.getNumberOfReplicas(), allocationPendingAllShards)); + } else { + logger.debug("{} lifecycle action for [{}] complete", index, getKey().getAction()); + return new Result(true, null); } + } + + static int getPendingAllocations(Index index, AllocationDeciders allocationDeciders, ClusterState clusterState) { // All the allocation attributes are already set so just need to check // if the allocation has happened - RoutingAllocation allocation = new RoutingAllocation(ALLOCATION_DECIDERS, clusterState.getRoutingNodes(), clusterState, null, - System.nanoTime()); + RoutingAllocation allocation = new RoutingAllocation(allocationDeciders, clusterState.getRoutingNodes(), clusterState, null, + System.nanoTime()); int allocationPendingAllShards = 0; @@ -89,23 +79,15 @@ public Result isConditionMet(Index index, ClusterState clusterState) { for (ObjectCursor shardRoutingTable : allShards.values()) { for (ShardRouting shardRouting : shardRoutingTable.value.shards()) { String currentNodeId = shardRouting.currentNodeId(); - boolean canRemainOnCurrentNode = ALLOCATION_DECIDERS - .canRemain(shardRouting, clusterState.getRoutingNodes().node(currentNodeId), allocation) - .type() == Decision.Type.YES; + boolean canRemainOnCurrentNode = allocationDeciders + .canRemain(shardRouting, clusterState.getRoutingNodes().node(currentNodeId), allocation) + .type() == Decision.Type.YES; if (canRemainOnCurrentNode == false || shardRouting.started() == false) { allocationPendingAllShards++; } } } - - if (allocationPendingAllShards > 0) { - logger.debug("{} lifecycle action [{}] waiting for [{}] shards to be allocated to nodes matching the given filters", - index, getKey().getAction(), allocationPendingAllShards); - return new Result(false, new Info(idxMeta.getNumberOfReplicas(), allocationPendingAllShards, true)); - } else { - logger.debug("{} lifecycle action for [{}] complete", index, getKey().getAction()); - return new Result(true, null); - } + return allocationPendingAllShards; } @Override @@ -123,84 +105,4 @@ public boolean equals(Object obj) { } return super.equals(obj); } - - public static final class Info implements ToXContentObject { - - private final long actualReplicas; - private final long numberShardsLeftToAllocate; - private final boolean allShardsActive; - private final String message; - - static final ParseField ACTUAL_REPLICAS = new ParseField("actual_replicas"); - static final ParseField SHARDS_TO_ALLOCATE = new ParseField("shards_left_to_allocate"); - static final ParseField ALL_SHARDS_ACTIVE = new ParseField("all_shards_active"); - static final ParseField MESSAGE = new ParseField("message"); - static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("allocation_routed_step_info", - a -> new Info((long) a[0], (long) a[1], (boolean) a[2])); - static { - PARSER.declareLong(ConstructingObjectParser.constructorArg(), ACTUAL_REPLICAS); - PARSER.declareLong(ConstructingObjectParser.constructorArg(), SHARDS_TO_ALLOCATE); - PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), ALL_SHARDS_ACTIVE); - PARSER.declareString((i, s) -> {}, MESSAGE); - } - - public Info(long actualReplicas, long numberShardsLeftToAllocate, boolean allShardsActive) { - this.actualReplicas = actualReplicas; - this.numberShardsLeftToAllocate = numberShardsLeftToAllocate; - this.allShardsActive = allShardsActive; - if (allShardsActive == false) { - message = "Waiting for all shard copies to be active"; - } else { - message = "Waiting for [" + numberShardsLeftToAllocate + "] shards " - + "to be allocated to nodes matching the given filters"; - } - } - - public long getActualReplicas() { - return actualReplicas; - } - - public long getNumberShardsLeftToAllocate() { - return numberShardsLeftToAllocate; - } - - public boolean allShardsActive() { - return allShardsActive; - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - builder.field(MESSAGE.getPreferredName(), message); - builder.field(SHARDS_TO_ALLOCATE.getPreferredName(), numberShardsLeftToAllocate); - builder.field(ALL_SHARDS_ACTIVE.getPreferredName(), allShardsActive); - builder.field(ACTUAL_REPLICAS.getPreferredName(), actualReplicas); - builder.endObject(); - return builder; - } - - @Override - public int hashCode() { - return Objects.hash(actualReplicas, numberShardsLeftToAllocate, allShardsActive); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Info other = (Info) obj; - return Objects.equals(actualReplicas, other.actualReplicas) && - Objects.equals(numberShardsLeftToAllocate, other.numberShardsLeftToAllocate) && - Objects.equals(allShardsActive, other.allShardsActive); - } - - @Override - public String toString() { - return Strings.toString(this); - } - } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java new file mode 100644 index 0000000000000..2d5d70c5a172b --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java @@ -0,0 +1,127 @@ +/* + * 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.ilm; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.support.ActiveShardCount; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodeRole; +import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.Index; +import org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider; +import org.elasticsearch.xpack.core.DataTier; +import org.elasticsearch.xpack.core.ilm.step.info.AllocationInfo; + +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import static org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider.INDEX_ROUTING_INCLUDE_SETTING; +import static org.elasticsearch.xpack.core.ilm.AllocationRoutedStep.getPendingAllocations; + +/** + * Checks whether all shards have been correctly routed in response to updating the allocation rules for an index in order + * to migrate the index to a new tier. + */ +public class DataTierMigrationRoutedStep extends ClusterStateWaitStep { + public static final String NAME = "check-migration"; + + private static final Logger logger = LogManager.getLogger(DataTierMigrationRoutedStep.class); + + private static final Set> ALL_CLUSTER_SETTINGS; + + static { + Set> allSettings = new HashSet<>(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + allSettings.add(DataTierAllocationDecider.CLUSTER_ROUTING_REQUIRE_SETTING); + allSettings.add(DataTierAllocationDecider.CLUSTER_ROUTING_INCLUDE_SETTING); + allSettings.add(DataTierAllocationDecider.CLUSTER_ROUTING_EXCLUDE_SETTING); + ALL_CLUSTER_SETTINGS = allSettings; + } + + private static final AllocationDeciders ALLOCATION_DECIDERS = new AllocationDeciders( + List.of( + new DataTierAllocationDecider(new ClusterSettings(Settings.EMPTY, ALL_CLUSTER_SETTINGS)) + ) + ); + + DataTierMigrationRoutedStep(StepKey key, StepKey nextStepKey) { + super(key, nextStepKey); + } + + @Override + public boolean isRetryable() { + return true; + } + + @Override + public Result isConditionMet(Index index, ClusterState clusterState) { + IndexMetadata idxMeta = clusterState.metadata().index(index); + if (idxMeta == null) { + // Index must have been since deleted, ignore it + logger.debug("[{}] lifecycle action for index [{}] executed but index no longer exists", getKey().getAction(), index.getName()); + return new Result(false, null); + } + if (ActiveShardCount.ALL.enoughShardsActive(clusterState, index.getName()) == false) { + logger.debug("[{}] lifecycle action for index [{}] cannot make progress because not all shards are active", + getKey().getAction(), index.getName()); + return new Result(false, AllocationInfo.waitingForActiveShardsAllocationInfo(idxMeta.getNumberOfReplicas())); + } + + int allocationPendingAllShards = getPendingAllocations(index, ALLOCATION_DECIDERS, clusterState); + + if (allocationPendingAllShards > 0) { + String tier = INDEX_ROUTING_INCLUDE_SETTING.get(idxMeta.getSettings()); + if (DataTier.validTierName(tier) == false) { + String errorMessage = String.format(Locale.ROOT, "[%s] lifecycle action for index [%s] is waiting for [%s] shards to be " + + "allocated but the configured data tier [%s] is invalid", getKey().getAction(), index.getName(), + allocationPendingAllShards, tier); + logger.debug(errorMessage); + throw new IllegalStateException(errorMessage); + } + + boolean targetTierRoleFound = false; + for (DiscoveryNode node : clusterState.nodes()) { + for (DiscoveryNodeRole role : node.getRoles()) { + if (role.roleName().equals(tier)) { + targetTierRoleFound = true; + break; + } + } + } + String statusMessage = String.format(Locale.ROOT, "%s lifecycle action [%s] waiting for [%s] shards to be moved to the [%s] " + + "tier" + (targetTierRoleFound ? "" : " but there are currently no [%s] nodes in the cluster"), + index, getKey().getAction(), allocationPendingAllShards, tier, tier); + logger.debug(statusMessage); + return new Result(false, new AllocationInfo(idxMeta.getNumberOfReplicas(), allocationPendingAllShards, true, statusMessage)); + } else { + logger.debug("{} lifecycle action for [{}] complete", index, getKey().getAction()); + return new Result(true, null); + } + } + + @Override + public int hashCode() { + return 711; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + return super.equals(obj); + } +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java index b9b729feda8aa..5f3940325b2a8 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java @@ -10,14 +10,17 @@ 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.xpack.cluster.routing.allocation.DataTierAllocationDecider; +import org.elasticsearch.xpack.core.DataTier; import org.elasticsearch.xpack.core.ilm.Step.StepKey; import java.io.IOException; +import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Objects; /** @@ -29,10 +32,10 @@ public class MigrateAction implements LifecycleAction { public static final ParseField ENABLED_FIELD = new ParseField("enabled"); private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(NAME, - a -> new MigrateAction((boolean) a[0])); + a -> new MigrateAction(a[0] == null ? true : (boolean) a[0])); static { - PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), ENABLED_FIELD); + PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), ENABLED_FIELD); } private final boolean enabled; @@ -79,9 +82,17 @@ public boolean isSafeAction() { @Override public List toSteps(Client client, String phase, StepKey nextStepKey) { if (enabled) { - Map include = Map.of("_tier", "data_" + phase); - AllocateAction migrateDataAction = new AllocateAction(null, include, null, null); - return migrateDataAction.toSteps(client, phase, nextStepKey); + StepKey migrationKey = new StepKey(phase, NAME, NAME); + StepKey migrationRoutedKey = new StepKey(phase, NAME, DataTierMigrationRoutedStep.NAME); + + Settings.Builder migrationSettings = Settings.builder(); + String dataTierName = "data_" + phase; + assert DataTier.validTierName(dataTierName) : "invalid data tier name:" + dataTierName; + migrationSettings.put(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE, dataTierName); + UpdateSettingsStep updateMigrationSettingStep = new UpdateSettingsStep(migrationKey, migrationRoutedKey, client, + migrationSettings.build()); + DataTierMigrationRoutedStep migrationRoutedStep = new DataTierMigrationRoutedStep(migrationRoutedKey, nextStepKey); + return Arrays.asList(updateMigrationSettingStep, migrationRoutedStep); } else { return List.of(); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java index 4f7a3b21b99ed..704cb4a477392 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java @@ -40,8 +40,8 @@ public class TimeseriesLifecycleType implements LifecycleType { static final String FROZEN_PHASE = "frozen"; static final String DELETE_PHASE = "delete"; static final List VALID_PHASES = Arrays.asList(HOT_PHASE, WARM_PHASE, COLD_PHASE, FROZEN_PHASE, DELETE_PHASE); - static final List ORDERED_VALID_HOT_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, RolloverAction.NAME, - ForceMergeAction.NAME); + static final List ORDERED_VALID_HOT_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, MigrateAction.NAME, + RolloverAction.NAME, ForceMergeAction.NAME); static final List ORDERED_VALID_WARM_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, ReadOnlyAction.NAME, MigrateAction.NAME, AllocateAction.NAME, ShrinkAction.NAME, ForceMergeAction.NAME); static final List ORDERED_VALID_COLD_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, @@ -90,7 +90,7 @@ public List getOrderedPhases(Map phases) { phase = new Phase(phase.getName(), phase.getMinimumAge(), actionMap); } - if (shouldMigrateDataToTiers(phase)) { + if (shouldInjectMigrateStepForPhase(phase)) { Map actionMap = new HashMap<>(phase.getActions()); actionMap.put(MigrateAction.NAME, new MigrateAction(true)); phase = new Phase(phase.getName(), phase.getMinimumAge(), actionMap); @@ -102,23 +102,18 @@ public List getOrderedPhases(Map phases) { return orderedPhases; } - boolean shouldMigrateDataToTiers(Phase phase) { + boolean shouldInjectMigrateStepForPhase(Phase phase) { AllocateAction allocateAction = (AllocateAction) phase.getActions().get(AllocateAction.NAME); if (allocateAction != null) { - if (allocateAction.getExclude().isEmpty() == false || - allocateAction.getInclude().isEmpty() == false || - allocateAction.getRequire().isEmpty() == false) { + if (definesAllocationRules(allocateAction)) { // we won't automatically migrate the data if an allocate action that defines any allocation rule is present return false; } } MigrateAction migrateAction = (MigrateAction) phase.getActions().get(MigrateAction.NAME); - if (migrateAction != null) { - // user configured the {@link MigrateAction} already so we won't automatically configure it - return false; - } - return true; + // if the user configured the {@link MigrateAction} already we won't automatically configure it + return migrateAction == null; } @Override @@ -272,7 +267,7 @@ public void validate(Collection phases) { } } - private boolean definesAllocationRules(AllocateAction action) { - return action.getRequire() != null || action.getInclude() != null || action.getExclude() != null; + private static boolean definesAllocationRules(AllocateAction action) { + return action.getRequire().isEmpty() == false || action.getInclude().isEmpty() == false || action.getExclude().isEmpty() == false; } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java new file mode 100644 index 0000000000000..01bf23ed659a9 --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java @@ -0,0 +1,113 @@ +/* + * 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.ilm.step.info; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Objects; + +public class AllocationInfo implements ToXContentObject { + private final long actualReplicas; + private final long numberShardsLeftToAllocate; + private final boolean allShardsActive; + private final String message; + + static final ParseField ACTUAL_REPLICAS = new ParseField("actual_replicas"); + static final ParseField SHARDS_TO_ALLOCATE = new ParseField("shards_left_to_allocate"); + static final ParseField ALL_SHARDS_ACTIVE = new ParseField("all_shards_active"); + static final ParseField MESSAGE = new ParseField("message"); + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("allocation_routed_step_info", + a -> new AllocationInfo((long) a[0], (long) a[1], (boolean) a[2], (String) a[3])); + + static { + PARSER.declareLong(ConstructingObjectParser.constructorArg(), ACTUAL_REPLICAS); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), SHARDS_TO_ALLOCATE); + PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), ALL_SHARDS_ACTIVE); + PARSER.declareString(ConstructingObjectParser.constructorArg(), MESSAGE); + } + + public AllocationInfo(long actualReplicas, long numberShardsLeftToAllocate, boolean allShardsActive, String message) { + this.actualReplicas = actualReplicas; + this.numberShardsLeftToAllocate = numberShardsLeftToAllocate; + this.allShardsActive = allShardsActive; + this.message = message; + } + + /** + * Builds the AllocationInfo representing a cluster state with a routing table that does not have enough shards active for a + * particular index. + */ + public static AllocationInfo waitingForActiveShardsAllocationInfo(long actualReplicas) { + return new AllocationInfo(actualReplicas, -1, false, + "Waiting for all shard copies to be active"); + } + + /** + * Builds the AllocationInfo representing a cluster state with a routing table that has all the shards active for a particular index + * but there are still {@link #numberShardsLeftToAllocate} left to be allocated. + */ + public static AllocationInfo allShardsActiveAllocationInfo(long actualReplicas, long numberShardsLeftToAllocate) { + return new AllocationInfo(actualReplicas, numberShardsLeftToAllocate, true, "Waiting for [" + numberShardsLeftToAllocate + + "] shards to be allocated to nodes matching the given filters"); + } + + public long getActualReplicas() { + return actualReplicas; + } + + public long getNumberShardsLeftToAllocate() { + return numberShardsLeftToAllocate; + } + + public boolean allShardsActive() { + return allShardsActive; + } + + public String getMessage() { + return message; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(MESSAGE.getPreferredName(), message); + builder.field(SHARDS_TO_ALLOCATE.getPreferredName(), numberShardsLeftToAllocate); + builder.field(ALL_SHARDS_ACTIVE.getPreferredName(), allShardsActive); + builder.field(ACTUAL_REPLICAS.getPreferredName(), actualReplicas); + builder.endObject(); + return builder; + } + + @Override + public int hashCode() { + return Objects.hash(actualReplicas, numberShardsLeftToAllocate, allShardsActive); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + AllocationInfo other = (AllocationInfo) obj; + return Objects.equals(actualReplicas, other.actualReplicas) && + Objects.equals(numberShardsLeftToAllocate, other.numberShardsLeftToAllocate) && + Objects.equals(allShardsActive, other.allShardsActive); + } + + @Override + public String toString() { + return Strings.toString(this); + } +} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStepTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStepTests.java index 2061514aaafcf..62512f9b91d08 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStepTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStepTests.java @@ -31,6 +31,9 @@ import java.util.Collections; import java.util.Map; +import static org.elasticsearch.xpack.core.ilm.step.info.AllocationInfo.allShardsActiveAllocationInfo; +import static org.elasticsearch.xpack.core.ilm.step.info.AllocationInfo.waitingForActiveShardsAllocationInfo; + public class AllocationRoutedStepTests extends AbstractStepTestCase { @Override @@ -117,7 +120,7 @@ public void testRequireConditionMetOnlyOneCopyAllocated() { AllocationRoutedStep step = new AllocationRoutedStep(randomStepKey(), randomStepKey()); assertAllocateStatus(index, 1, 0, step, existingSettings, node1Settings, Settings.builder(), indexRoutingTable, - new ClusterStateWaitStep.Result(false, new AllocationRoutedStep.Info(0, 1, true))); + new ClusterStateWaitStep.Result(false, allShardsActiveAllocationInfo(0, 1))); } public void testExcludeConditionMetOnlyOneCopyAllocated() { @@ -139,7 +142,7 @@ public void testExcludeConditionMetOnlyOneCopyAllocated() { AllocationRoutedStep step = new AllocationRoutedStep(randomStepKey(), randomStepKey()); assertAllocateStatus(index, 1, 0, step, existingSettings, node1Settings, Settings.builder(), indexRoutingTable, - new ClusterStateWaitStep.Result(false, new AllocationRoutedStep.Info(0, 1, true))); + new ClusterStateWaitStep.Result(false, allShardsActiveAllocationInfo(0, 1))); } public void testIncludeConditionMetOnlyOneCopyAllocated() { @@ -161,7 +164,7 @@ public void testIncludeConditionMetOnlyOneCopyAllocated() { AllocationRoutedStep step = new AllocationRoutedStep(randomStepKey(), randomStepKey()); assertAllocateStatus(index, 1, 0, step, existingSettings, node1Settings, Settings.builder(), indexRoutingTable, - new ClusterStateWaitStep.Result(false, new AllocationRoutedStep.Info(0, 1, true))); + new ClusterStateWaitStep.Result(false, allShardsActiveAllocationInfo(0, 1))); } public void testConditionNotMetDueToRelocation() { @@ -190,7 +193,7 @@ public void testConditionNotMetDueToRelocation() { AllocationRoutedStep step = new AllocationRoutedStep(randomStepKey(), randomStepKey()); assertAllocateStatus(index, 1, 0, step, existingSettings, node1Settings, node2Settings, indexRoutingTable, - new ClusterStateWaitStep.Result(false, new AllocationRoutedStep.Info(0, 2, true))); + new ClusterStateWaitStep.Result(false, allShardsActiveAllocationInfo(0, 2))); } public void testExecuteAllocateNotComplete() throws Exception { @@ -227,7 +230,7 @@ public void testExecuteAllocateNotComplete() throws Exception { AllocationRoutedStep step = createRandomInstance(); assertAllocateStatus(index, 2, 0, step, existingSettings, node1Settings, node2Settings, indexRoutingTable, - new ClusterStateWaitStep.Result(false, new AllocationRoutedStep.Info(0, 1, true))); + new ClusterStateWaitStep.Result(false, allShardsActiveAllocationInfo(0, 1))); } public void testExecuteAllocateNotCompleteOnlyOneCopyAllocated() throws Exception { @@ -266,7 +269,7 @@ public void testExecuteAllocateNotCompleteOnlyOneCopyAllocated() throws Exceptio AllocationRoutedStep step = new AllocationRoutedStep(randomStepKey(), randomStepKey()); assertAllocateStatus(index, 2, 0, step, existingSettings, node1Settings, node2Settings, indexRoutingTable, - new ClusterStateWaitStep.Result(false, new AllocationRoutedStep.Info(0, 1, true))); + new ClusterStateWaitStep.Result(false, allShardsActiveAllocationInfo(0, 1))); } public void testExecuteAllocateUnassigned() throws Exception { @@ -304,7 +307,7 @@ public void testExecuteAllocateUnassigned() throws Exception { AllocationRoutedStep step = createRandomInstance(); assertAllocateStatus(index, 2, 0, step, existingSettings, node1Settings, node2Settings, indexRoutingTable, - new ClusterStateWaitStep.Result(false, new AllocationRoutedStep.Info(0, -1, false))); + new ClusterStateWaitStep.Result(false, waitingForActiveShardsAllocationInfo(0))); } /** @@ -343,7 +346,7 @@ public void testExecuteReplicasNotAllocatedOnSingleNode() { AllocationRoutedStep step = createRandomInstance(); assertAllocateStatus(index, 1, 1, step, existingSettings, node1Settings, node2Settings, indexRoutingTable, - new ClusterStateWaitStep.Result(false, new AllocationRoutedStep.Info(1, -1, false))); + new ClusterStateWaitStep.Result(false, waitingForActiveShardsAllocationInfo(1))); } public void testExecuteIndexMissing() throws Exception { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java index 4ff5bcd28c002..65931fbd318e1 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java @@ -637,7 +637,7 @@ public void testShouldMigrateDataToTiers() { actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(false)); actions.put(TEST_ALLOCATE_ACTION.getWriteableName(), TEST_ALLOCATE_ACTION); Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); - assertThat(TimeseriesLifecycleType.INSTANCE.shouldMigrateDataToTiers(phase), is(false)); + assertThat(TimeseriesLifecycleType.INSTANCE.shouldInjectMigrateStepForPhase(phase), is(false)); } { @@ -645,7 +645,7 @@ public void testShouldMigrateDataToTiers() { Map actions = new HashMap<>(); actions.put(TEST_ALLOCATE_ACTION.getWriteableName(), new AllocateAction(2, null, null, null)); Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); - assertThat(TimeseriesLifecycleType.INSTANCE.shouldMigrateDataToTiers(phase), is(true)); + assertThat(TimeseriesLifecycleType.INSTANCE.shouldInjectMigrateStepForPhase(phase), is(true)); } { @@ -653,7 +653,7 @@ public void testShouldMigrateDataToTiers() { Map actions = new HashMap<>(); actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(true)); Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); - assertThat(TimeseriesLifecycleType.INSTANCE.shouldMigrateDataToTiers(phase), is(false)); + assertThat(TimeseriesLifecycleType.INSTANCE.shouldInjectMigrateStepForPhase(phase), is(false)); } { @@ -661,7 +661,7 @@ public void testShouldMigrateDataToTiers() { Map actions = new HashMap<>(); actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(false)); Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); - assertThat(TimeseriesLifecycleType.INSTANCE.shouldMigrateDataToTiers(phase), is(false)); + assertThat(TimeseriesLifecycleType.INSTANCE.shouldInjectMigrateStepForPhase(phase), is(false)); } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStepInfoTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationRoutedStepInfoTests.java similarity index 59% rename from x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStepInfoTests.java rename to x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationRoutedStepInfoTests.java index 763f69214911a..b72b979ad5d7f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/AllocationRoutedStepInfoTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationRoutedStepInfoTests.java @@ -4,25 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.core.ilm; +package org.elasticsearch.xpack.core.ilm.step.info; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.test.AbstractXContentTestCase; import org.elasticsearch.test.EqualsHashCodeTestUtils; -import org.elasticsearch.xpack.core.ilm.AllocationRoutedStep.Info; import java.io.IOException; -public class AllocationRoutedStepInfoTests extends AbstractXContentTestCase { +public class AllocationRoutedStepInfoTests extends AbstractXContentTestCase { @Override - protected Info createTestInstance() { - return new Info(randomNonNegativeLong(), randomNonNegativeLong(), randomBoolean()); + protected AllocationInfo createTestInstance() { + return new AllocationInfo(randomNonNegativeLong(), randomNonNegativeLong(), randomBoolean(), randomAlphaOfLengthBetween(5, 10)); } @Override - protected Info doParseInstance(XContentParser parser) throws IOException { - return Info.PARSER.apply(parser, null); + protected AllocationInfo doParseInstance(XContentParser parser) throws IOException { + return AllocationInfo.PARSER.apply(parser, null); } @Override @@ -36,14 +35,16 @@ public final void testEqualsAndHashcode() { } } - protected final Info copyInstance(Info instance) throws IOException { - return new Info(instance.getActualReplicas(), instance.getNumberShardsLeftToAllocate(), instance.allShardsActive()); + protected final AllocationInfo copyInstance(AllocationInfo instance) { + return new AllocationInfo(instance.getActualReplicas(), instance.getNumberShardsLeftToAllocate(), instance.allShardsActive(), + instance.getMessage()); } - protected Info mutateInstance(Info instance) throws IOException { + protected AllocationInfo mutateInstance(AllocationInfo instance) throws IOException { long actualReplicas = instance.getActualReplicas(); long shardsToAllocate = instance.getNumberShardsLeftToAllocate(); boolean allShardsActive = instance.allShardsActive(); + var message = instance.getMessage(); switch (between(0, 2)) { case 0: shardsToAllocate += between(1, 20); @@ -54,10 +55,13 @@ protected Info mutateInstance(Info instance) throws IOException { case 2: actualReplicas += between(1, 20); break; + case 3: + message = randomValueOtherThan(message, () -> randomAlphaOfLengthBetween(5, 10)); + break; default: throw new AssertionError("Illegal randomisation branch"); } - return new Info(actualReplicas, shardsToAllocate, allShardsActive); + return new AllocationInfo(actualReplicas, shardsToAllocate, allShardsActive, message); } } From 42900b26f322189b57ecb776f344c9df2936acb3 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Fri, 4 Sep 2020 10:26:49 +0100 Subject: [PATCH 04/34] Fix tests --- .../xpack/core/ilm/LifecyclePolicyMetadataTests.java | 2 ++ .../elasticsearch/xpack/core/ilm/LifecyclePolicyTests.java | 6 ++++++ .../xpack/core/ilm/action/PutLifecycleRequestTests.java | 3 +++ 3 files changed, 11 insertions(+) diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyMetadataTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyMetadataTests.java index 684b32a19a277..2b048b51d743b 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyMetadataTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyMetadataTests.java @@ -48,6 +48,7 @@ protected NamedWriteableRegistry getNamedWriteableRegistry() { new NamedWriteableRegistry.Entry(LifecycleAction.class, ShrinkAction.NAME, ShrinkAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, FreezeAction.NAME, FreezeAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, SetPriorityAction.NAME, SetPriorityAction::new), + new NamedWriteableRegistry.Entry(LifecycleAction.class, MigrateAction.NAME, MigrateAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, UnfollowAction.NAME, UnfollowAction::new) )); } @@ -70,6 +71,7 @@ protected NamedXContentRegistry xContentRegistry() { new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(ShrinkAction.NAME), ShrinkAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(FreezeAction.NAME), FreezeAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(SetPriorityAction.NAME), SetPriorityAction::parse), + new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(MigrateAction.NAME), MigrateAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(UnfollowAction.NAME), UnfollowAction::parse) )); return new NamedXContentRegistry(entries); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyTests.java index 5e9142c0520ba..97b6ee601fa2f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/LifecyclePolicyTests.java @@ -57,6 +57,7 @@ protected NamedWriteableRegistry getNamedWriteableRegistry() { new NamedWriteableRegistry.Entry(LifecycleAction.class, FreezeAction.NAME, FreezeAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, SetPriorityAction.NAME, SetPriorityAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, UnfollowAction.NAME, UnfollowAction::new), + new NamedWriteableRegistry.Entry(LifecycleAction.class, MigrateAction.NAME, MigrateAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, SearchableSnapshotAction.NAME, SearchableSnapshotAction::new) )); } @@ -78,6 +79,7 @@ protected NamedXContentRegistry xContentRegistry() { new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(FreezeAction.NAME), FreezeAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(SetPriorityAction.NAME), SetPriorityAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(UnfollowAction.NAME), UnfollowAction::parse), + new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(MigrateAction.NAME), MigrateAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(SearchableSnapshotAction.NAME), SearchableSnapshotAction::parse) )); @@ -136,6 +138,8 @@ public static LifecyclePolicy randomTimeseriesLifecyclePolicyWithAllPhases(@Null return new UnfollowAction(); case SearchableSnapshotAction.NAME: return new SearchableSnapshotAction(randomAlphaOfLengthBetween(1, 10)); + case MigrateAction.NAME: + return new MigrateAction(false); default: throw new IllegalArgumentException("invalid action [" + action + "]"); }}; @@ -194,6 +198,8 @@ public static LifecyclePolicy randomTimeseriesLifecyclePolicy(@Nullable String l return new UnfollowAction(); case SearchableSnapshotAction.NAME: return new SearchableSnapshotAction(randomAlphaOfLengthBetween(1, 10)); + case MigrateAction.NAME: + return new MigrateAction(false); default: throw new IllegalArgumentException("invalid action [" + action + "]"); }}; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/action/PutLifecycleRequestTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/action/PutLifecycleRequestTests.java index 6b1e20c5551df..54a34217a8e4d 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/action/PutLifecycleRequestTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/action/PutLifecycleRequestTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; import org.elasticsearch.xpack.core.ilm.LifecyclePolicyTests; import org.elasticsearch.xpack.core.ilm.LifecycleType; +import org.elasticsearch.xpack.core.ilm.MigrateAction; import org.elasticsearch.xpack.core.ilm.ReadOnlyAction; import org.elasticsearch.xpack.core.ilm.RolloverAction; import org.elasticsearch.xpack.core.ilm.SearchableSnapshotAction; @@ -75,6 +76,7 @@ protected NamedWriteableRegistry getNamedWriteableRegistry() { new NamedWriteableRegistry.Entry(LifecycleAction.class, FreezeAction.NAME, FreezeAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, SetPriorityAction.NAME, SetPriorityAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, UnfollowAction.NAME, UnfollowAction::new), + new NamedWriteableRegistry.Entry(LifecycleAction.class, MigrateAction.NAME, MigrateAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, SearchableSnapshotAction.NAME, SearchableSnapshotAction::new) )); } @@ -97,6 +99,7 @@ protected NamedXContentRegistry xContentRegistry() { new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(SetPriorityAction.NAME), SetPriorityAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(SearchableSnapshotAction.NAME), SearchableSnapshotAction::parse), + new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(MigrateAction.NAME), MigrateAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(UnfollowAction.NAME), UnfollowAction::parse) )); return new NamedXContentRegistry(entries); From 5d6729b4eb18c09ab29a8a1f79dfb2b0a64ff63f Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Fri, 4 Sep 2020 10:51:59 +0100 Subject: [PATCH 05/34] Fix test --- .../elasticsearch/xpack/ilm/IndexLifecycleMetadataTests.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleMetadataTests.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleMetadataTests.java index 4121d73ae8034..aeb1e2f69263b 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleMetadataTests.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleMetadataTests.java @@ -28,6 +28,7 @@ import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; import org.elasticsearch.xpack.core.ilm.LifecyclePolicyMetadata; import org.elasticsearch.xpack.core.ilm.LifecycleType; +import org.elasticsearch.xpack.core.ilm.MigrateAction; import org.elasticsearch.xpack.core.ilm.OperationMode; import org.elasticsearch.xpack.core.ilm.Phase; import org.elasticsearch.xpack.core.ilm.ReadOnlyAction; @@ -92,6 +93,7 @@ protected NamedWriteableRegistry getNamedWriteableRegistry() { new NamedWriteableRegistry.Entry(LifecycleAction.class, FreezeAction.NAME, FreezeAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, SetPriorityAction.NAME, SetPriorityAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, UnfollowAction.NAME, UnfollowAction::new), + new NamedWriteableRegistry.Entry(LifecycleAction.class, MigrateAction.NAME, MigrateAction::new), new NamedWriteableRegistry.Entry(LifecycleAction.class, SearchableSnapshotAction.NAME, SearchableSnapshotAction::new) )); } @@ -113,6 +115,7 @@ protected NamedXContentRegistry xContentRegistry() { new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(FreezeAction.NAME), FreezeAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(SetPriorityAction.NAME), SetPriorityAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(UnfollowAction.NAME), UnfollowAction::parse), + new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(MigrateAction.NAME), MigrateAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(SearchableSnapshotAction.NAME), SearchableSnapshotAction::parse) )); From 28ccee9ec0fe0e3f8eca8d473940ef7e4f1e6624 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Fri, 4 Sep 2020 17:20:53 +0100 Subject: [PATCH 06/34] Remove invalid tier name setting validation from ILM step We already validate the data tier setting values as part of the index settings validation. --- .../xpack/core/ilm/DataTierMigrationRoutedStep.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java index 2d5d70c5a172b..0af8855358faa 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java @@ -81,14 +81,6 @@ public Result isConditionMet(Index index, ClusterState clusterState) { if (allocationPendingAllShards > 0) { String tier = INDEX_ROUTING_INCLUDE_SETTING.get(idxMeta.getSettings()); - if (DataTier.validTierName(tier) == false) { - String errorMessage = String.format(Locale.ROOT, "[%s] lifecycle action for index [%s] is waiting for [%s] shards to be " + - "allocated but the configured data tier [%s] is invalid", getKey().getAction(), index.getName(), - allocationPendingAllShards, tier); - logger.debug(errorMessage); - throw new IllegalStateException(errorMessage); - } - boolean targetTierRoleFound = false; for (DiscoveryNode node : clusterState.nodes()) { for (DiscoveryNodeRole role : node.getRoles()) { From 45d2eb38d227799f7d6c78578fe4941ab7b2c2ed Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Fri, 4 Sep 2020 17:47:23 +0100 Subject: [PATCH 07/34] Add test for DataTierMigrationRoutedStep --- .../core/ilm/DataTierMigrationRoutedStep.java | 6 +- .../core/ilm/step/info/AllocationInfo.java | 1 + .../ilm/DataTierMigrationRoutedStepTests.java | 185 ++++++++++++++++++ 3 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStepTests.java diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java index 0af8855358faa..a734f24bfc9f8 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java @@ -81,17 +81,17 @@ public Result isConditionMet(Index index, ClusterState clusterState) { if (allocationPendingAllShards > 0) { String tier = INDEX_ROUTING_INCLUDE_SETTING.get(idxMeta.getSettings()); - boolean targetTierRoleFound = false; + boolean targetTierNodeFound = false; for (DiscoveryNode node : clusterState.nodes()) { for (DiscoveryNodeRole role : node.getRoles()) { if (role.roleName().equals(tier)) { - targetTierRoleFound = true; + targetTierNodeFound = true; break; } } } String statusMessage = String.format(Locale.ROOT, "%s lifecycle action [%s] waiting for [%s] shards to be moved to the [%s] " + - "tier" + (targetTierRoleFound ? "" : " but there are currently no [%s] nodes in the cluster"), + "tier" + (targetTierNodeFound ? "" : " but there are currently no [%s] nodes in the cluster"), index, getKey().getAction(), allocationPendingAllShards, tier, tier); logger.debug(statusMessage); return new Result(false, new AllocationInfo(idxMeta.getNumberOfReplicas(), allocationPendingAllShards, true, statusMessage)); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java index 01bf23ed659a9..d81091c0abc1b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java @@ -103,6 +103,7 @@ public boolean equals(Object obj) { AllocationInfo other = (AllocationInfo) obj; return Objects.equals(actualReplicas, other.actualReplicas) && Objects.equals(numberShardsLeftToAllocate, other.numberShardsLeftToAllocate) && + Objects.equals(message, other.message) && Objects.equals(allShardsActive, other.allShardsActive); } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStepTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStepTests.java new file mode 100644 index 0000000000000..a14f365e08d3c --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStepTests.java @@ -0,0 +1,185 @@ +/* + * 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.ilm; + + +import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodeRole; +import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.cluster.routing.IndexRoutingTable; +import org.elasticsearch.cluster.routing.RoutingTable; +import org.elasticsearch.cluster.routing.ShardRoutingState; +import org.elasticsearch.cluster.routing.TestShardRouting; +import org.elasticsearch.cluster.routing.UnassignedInfo; +import org.elasticsearch.cluster.routing.UnassignedInfo.Reason; +import org.elasticsearch.index.Index; +import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.xpack.core.DataTier; +import org.elasticsearch.xpack.core.ilm.ClusterStateWaitStep.Result; +import org.elasticsearch.xpack.core.ilm.Step.StepKey; +import org.elasticsearch.xpack.core.ilm.step.info.AllocationInfo; + +import java.util.Collections; +import java.util.Set; + +import static java.util.Collections.emptyMap; +import static org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider.INDEX_ROUTING_INCLUDE_SETTING; +import static org.elasticsearch.xpack.core.ilm.step.info.AllocationInfo.waitingForActiveShardsAllocationInfo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +public class DataTierMigrationRoutedStepTests extends AbstractStepTestCase { + + @Override + public DataTierMigrationRoutedStep createRandomInstance() { + StepKey stepKey = randomStepKey(); + StepKey nextStepKey = randomStepKey(); + + return new DataTierMigrationRoutedStep(stepKey, nextStepKey); + } + + @Override + public DataTierMigrationRoutedStep mutateInstance(DataTierMigrationRoutedStep instance) { + StepKey key = instance.getKey(); + StepKey nextKey = instance.getNextStepKey(); + + switch (between(0, 1)) { + 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; + default: + throw new AssertionError("Illegal randomisation branch"); + } + + return new DataTierMigrationRoutedStep(key, nextKey); + } + + @Override + public DataTierMigrationRoutedStep copyInstance(DataTierMigrationRoutedStep instance) { + return new DataTierMigrationRoutedStep(instance.getKey(), instance.getNextStepKey()); + } + + public void testExecuteWithUnassignedShard() { + IndexMetadata indexMetadata = IndexMetadata.builder(randomAlphaOfLengthBetween(5, 10)).settings(settings(Version.CURRENT)) + .numberOfShards(1).numberOfReplicas(1).build(); + Index index = indexMetadata.getIndex(); + IndexRoutingTable.Builder indexRoutingTable = IndexRoutingTable.builder(index) + .addShard(TestShardRouting.newShardRouting(new ShardId(index, 0), "node1", true, ShardRoutingState.STARTED)) + .addShard(TestShardRouting.newShardRouting(new ShardId(index, 1), null, null, true, ShardRoutingState.UNASSIGNED, + new UnassignedInfo(randomFrom(Reason.values()), "the shard is intentionally unassigned"))); + + ClusterState clusterState = + ClusterState.builder(ClusterState.EMPTY_STATE).metadata(Metadata.builder().put(indexMetadata, true).build()) + .nodes(DiscoveryNodes.builder() + .add(newNode("node1", Collections.singleton(DataTier.DATA_HOT_NODE_ROLE))) + ) + .routingTable(RoutingTable.builder().add(indexRoutingTable).build()) + .build(); + DataTierMigrationRoutedStep step = createRandomInstance(); + Result expectedResult = new Result(false, waitingForActiveShardsAllocationInfo(1)); + + Result actualResult = step.isConditionMet(index, clusterState); + assertThat(actualResult.isComplete(), is(false)); + assertThat(actualResult.getInfomationContext(), is(expectedResult.getInfomationContext())); + } + + public void testExecuteWithPendingShards() { + IndexMetadata indexMetadata = IndexMetadata.builder(randomAlphaOfLengthBetween(5, 10)) + .settings(settings(Version.CURRENT).put(INDEX_ROUTING_INCLUDE_SETTING.getKey(), DataTier.DATA_WARM)) + .numberOfShards(1).numberOfReplicas(0).build(); + Index index = indexMetadata.getIndex(); + IndexRoutingTable.Builder indexRoutingTable = IndexRoutingTable.builder(index) + .addShard(TestShardRouting.newShardRouting(new ShardId(index, 0), "node1", true, ShardRoutingState.STARTED)); + + ClusterState clusterState = + ClusterState.builder(ClusterState.EMPTY_STATE).metadata(Metadata.builder().put(indexMetadata, true).build()) + .nodes(DiscoveryNodes.builder() + .add(newNode("node1", Collections.singleton(DataTier.DATA_HOT_NODE_ROLE))) + .add(newNode("node2", Collections.singleton(DataTier.DATA_WARM_NODE_ROLE))) + ) + .routingTable(RoutingTable.builder().add(indexRoutingTable).build()) + .build(); + DataTierMigrationRoutedStep step = createRandomInstance(); + Result expectedResult = new Result(false, new AllocationInfo(0, 1, true, + "[" + index.getName() + "] lifecycle action [" + step.getKey().getAction() + "] waiting for " + + "[1] shards to be moved to the [data_warm] tier") + ); + + Result actualResult = step.isConditionMet(index, clusterState); + assertThat(actualResult.isComplete(), is(false)); + assertThat(actualResult.getInfomationContext(), is(expectedResult.getInfomationContext())); + } + + public void testExecuteWithPendingShardsAndTargetRoleNotPresentInCluster() { + IndexMetadata indexMetadata = IndexMetadata.builder(randomAlphaOfLengthBetween(5, 10)) + .settings(settings(Version.CURRENT).put(INDEX_ROUTING_INCLUDE_SETTING.getKey(), DataTier.DATA_WARM)) + .numberOfShards(1).numberOfReplicas(0).build(); + Index index = indexMetadata.getIndex(); + IndexRoutingTable.Builder indexRoutingTable = IndexRoutingTable.builder(index) + .addShard(TestShardRouting.newShardRouting(new ShardId(index, 0), "node1", true, ShardRoutingState.STARTED)); + + ClusterState clusterState = + ClusterState.builder(ClusterState.EMPTY_STATE).metadata(Metadata.builder().put(indexMetadata, true).build()) + .nodes(DiscoveryNodes.builder() + .add(newNode("node1", Collections.singleton(DataTier.DATA_HOT_NODE_ROLE))) + ) + .routingTable(RoutingTable.builder().add(indexRoutingTable).build()) + .build(); + DataTierMigrationRoutedStep step = createRandomInstance(); + Result expectedResult = new Result(false, new AllocationInfo(0, 1, true, + "[" + index.getName() + "] lifecycle action [" + step.getKey().getAction() + "] waiting for " + + "[1] shards to be moved to the [data_warm] tier but there are currently no [data_warm] nodes in the cluster") + ); + + Result actualResult = step.isConditionMet(index, clusterState); + assertThat(actualResult.isComplete(), is(false)); + assertThat(actualResult.getInfomationContext(), is(expectedResult.getInfomationContext())); + } + + public void testExecuteIndexMissing() { + Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20)); + ClusterState clusterState = ClusterState.builder(ClusterState.EMPTY_STATE).build(); + + DataTierMigrationRoutedStep step = createRandomInstance(); + + Result actualResult = step.isConditionMet(index, clusterState); + assertThat(actualResult.isComplete(), is(false)); + assertThat(actualResult.getInfomationContext(), is(nullValue())); + } + + public void testExecuteIsComplete() { + IndexMetadata indexMetadata = IndexMetadata.builder(randomAlphaOfLengthBetween(5, 10)) + .settings(settings(Version.CURRENT).put(INDEX_ROUTING_INCLUDE_SETTING.getKey(), DataTier.DATA_WARM)) + .numberOfShards(1).numberOfReplicas(0).build(); + Index index = indexMetadata.getIndex(); + IndexRoutingTable.Builder indexRoutingTable = IndexRoutingTable.builder(index) + .addShard(TestShardRouting.newShardRouting(new ShardId(index, 0), "node2", true, ShardRoutingState.STARTED)); + + ClusterState clusterState = + ClusterState.builder(ClusterState.EMPTY_STATE).metadata(Metadata.builder().put(indexMetadata, true).build()) + .nodes(DiscoveryNodes.builder() + .add(newNode("node1", Collections.singleton(DataTier.DATA_HOT_NODE_ROLE))) + .add(newNode("node2", Collections.singleton(DataTier.DATA_WARM_NODE_ROLE))) + ) + .routingTable(RoutingTable.builder().add(indexRoutingTable).build()) + .build(); + DataTierMigrationRoutedStep step = createRandomInstance(); + Result result = step.isConditionMet(index, clusterState); + assertThat(result.isComplete(), is(true)); + assertThat(result.getInfomationContext(), is(nullValue())); + } + + private DiscoveryNode newNode(String nodeId, Set roles) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), emptyMap(), roles, Version.CURRENT); + } +} From 6c05591ce911008dc22e4dd342991f56deb9f4f0 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Fri, 4 Sep 2020 17:57:57 +0100 Subject: [PATCH 08/34] Disable data tier migration in the full policy we use for timeseries ITS We'll have dedicated data tier integration tests that'll have data migration enabled. --- .../java/org/elasticsearch/xpack/TimeSeriesRestDriver.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java b/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java index fe7d2d941baa8..eb96038fc14cb 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java @@ -27,6 +27,7 @@ import org.elasticsearch.xpack.core.ilm.ForceMergeAction; import org.elasticsearch.xpack.core.ilm.LifecycleAction; import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; +import org.elasticsearch.xpack.core.ilm.MigrateAction; import org.elasticsearch.xpack.core.ilm.Phase; import org.elasticsearch.xpack.core.ilm.RolloverAction; import org.elasticsearch.xpack.core.ilm.SetPriorityAction; @@ -151,17 +152,21 @@ public static void rolloverMaxOneDocCondition(RestClient client, String indexAbs public static void createFullPolicy(RestClient client, String policyName, TimeValue hotTime) throws IOException { Map hotActions = new HashMap<>(); + hotActions.put(MigrateAction.NAME, new MigrateAction(false)); hotActions.put(SetPriorityAction.NAME, new SetPriorityAction(100)); hotActions.put(RolloverAction.NAME, new RolloverAction(null, null, 1L)); Map warmActions = new HashMap<>(); + warmActions.put(MigrateAction.NAME, new MigrateAction(false)); warmActions.put(SetPriorityAction.NAME, new SetPriorityAction(50)); warmActions.put(ForceMergeAction.NAME, new ForceMergeAction(1, null)); warmActions.put(AllocateAction.NAME, new AllocateAction(1, singletonMap("_name", "integTest-1,integTest-2"), null, null)); warmActions.put(ShrinkAction.NAME, new ShrinkAction(1)); Map coldActions = new HashMap<>(); + coldActions.put(MigrateAction.NAME, new MigrateAction(false)); coldActions.put(SetPriorityAction.NAME, new SetPriorityAction(25)); coldActions.put(AllocateAction.NAME, new AllocateAction(0, singletonMap("_name", "integTest-3"), null, null)); Map frozenActions = new HashMap<>(); + frozenActions.put(MigrateAction.NAME, new MigrateAction(false)); frozenActions.put(SetPriorityAction.NAME, new SetPriorityAction(0)); Map phases = new HashMap<>(); phases.put("hot", new Phase("hot", hotTime, hotActions)); From 783061d20182d147e1ac064e71505c6fb62156c0 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Fri, 4 Sep 2020 17:59:54 +0100 Subject: [PATCH 09/34] Remove unused import --- .../xpack/core/ilm/DataTierMigrationRoutedStep.java | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java index a734f24bfc9f8..6ec03e7f58d02 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java @@ -18,7 +18,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.Index; import org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider; -import org.elasticsearch.xpack.core.DataTier; import org.elasticsearch.xpack.core.ilm.step.info.AllocationInfo; import java.util.HashSet; From ab3ac3362b344e5caf8d3ed0211c6487533ebb4a Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Sun, 6 Sep 2020 14:30:32 +0100 Subject: [PATCH 10/34] Revert "Disable data tier migration in the full policy we use for timeseries ITS" This reverts commit 6c05591ce911008dc22e4dd342991f56deb9f4f0. --- .../java/org/elasticsearch/xpack/TimeSeriesRestDriver.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java b/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java index eb96038fc14cb..fe7d2d941baa8 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java @@ -27,7 +27,6 @@ import org.elasticsearch.xpack.core.ilm.ForceMergeAction; import org.elasticsearch.xpack.core.ilm.LifecycleAction; import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; -import org.elasticsearch.xpack.core.ilm.MigrateAction; import org.elasticsearch.xpack.core.ilm.Phase; import org.elasticsearch.xpack.core.ilm.RolloverAction; import org.elasticsearch.xpack.core.ilm.SetPriorityAction; @@ -152,21 +151,17 @@ public static void rolloverMaxOneDocCondition(RestClient client, String indexAbs public static void createFullPolicy(RestClient client, String policyName, TimeValue hotTime) throws IOException { Map hotActions = new HashMap<>(); - hotActions.put(MigrateAction.NAME, new MigrateAction(false)); hotActions.put(SetPriorityAction.NAME, new SetPriorityAction(100)); hotActions.put(RolloverAction.NAME, new RolloverAction(null, null, 1L)); Map warmActions = new HashMap<>(); - warmActions.put(MigrateAction.NAME, new MigrateAction(false)); warmActions.put(SetPriorityAction.NAME, new SetPriorityAction(50)); warmActions.put(ForceMergeAction.NAME, new ForceMergeAction(1, null)); warmActions.put(AllocateAction.NAME, new AllocateAction(1, singletonMap("_name", "integTest-1,integTest-2"), null, null)); warmActions.put(ShrinkAction.NAME, new ShrinkAction(1)); Map coldActions = new HashMap<>(); - coldActions.put(MigrateAction.NAME, new MigrateAction(false)); coldActions.put(SetPriorityAction.NAME, new SetPriorityAction(25)); coldActions.put(AllocateAction.NAME, new AllocateAction(0, singletonMap("_name", "integTest-3"), null, null)); Map frozenActions = new HashMap<>(); - frozenActions.put(MigrateAction.NAME, new MigrateAction(false)); frozenActions.put(SetPriorityAction.NAME, new SetPriorityAction(0)); Map phases = new HashMap<>(); phases.put("hot", new Phase("hot", hotTime, hotActions)); From 6b74d7cc81bbdd55e8d1fe69d0b65f3e6a17ea7e Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Sun, 6 Sep 2020 14:51:56 +0100 Subject: [PATCH 11/34] Add MigrateActionTest --- .../xpack/core/ilm/MigrateAction.java | 4 ++ .../xpack/core/ilm/MigrateActionTests.java | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/MigrateActionTests.java diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java index 5f3940325b2a8..f40fa7024e86b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MigrateAction.java @@ -44,6 +44,10 @@ public static MigrateAction parse(XContentParser parser) { return PARSER.apply(parser, null); } + public MigrateAction() { + this(true); + } + public MigrateAction(boolean enabled) { this.enabled = enabled; } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/MigrateActionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/MigrateActionTests.java new file mode 100644 index 0000000000000..4278f515bc1bc --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/MigrateActionTests.java @@ -0,0 +1,57 @@ +/* + * 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.ilm; + +import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.xpack.core.ilm.Step.StepKey; + +import java.io.IOException; +import java.util.List; + +public class MigrateActionTests extends AbstractActionTestCase { + + @Override + protected MigrateAction doParseInstance(XContentParser parser) throws IOException { + return MigrateAction.parse(parser); + } + + @Override + protected MigrateAction createTestInstance() { + return new MigrateAction(); + } + + @Override + protected Reader instanceReader() { + return MigrateAction::new; + } + + public void testToSteps() { + String phase = randomFrom(TimeseriesLifecycleType.VALID_PHASES); + StepKey nextStepKey = new StepKey(randomAlphaOfLengthBetween(1, 10), randomAlphaOfLengthBetween(1, 10), + randomAlphaOfLengthBetween(1, 10)); + { + MigrateAction action = new MigrateAction(); + List steps = action.toSteps(null, phase, nextStepKey); + assertNotNull(steps); + assertEquals(2, steps.size()); + StepKey expectedFirstStepKey = new StepKey(phase, MigrateAction.NAME, MigrateAction.NAME); + StepKey expectedSecondStepKey = new StepKey(phase, MigrateAction.NAME, DataTierMigrationRoutedStep.NAME); + UpdateSettingsStep firstStep = (UpdateSettingsStep) steps.get(0); + DataTierMigrationRoutedStep secondStep = (DataTierMigrationRoutedStep) steps.get(1); + assertEquals(expectedFirstStepKey, firstStep.getKey()); + assertEquals(expectedSecondStepKey, firstStep.getNextStepKey()); + assertEquals(expectedSecondStepKey, secondStep.getKey()); + assertEquals(nextStepKey, secondStep.getNextStepKey()); + } + + { + MigrateAction disabledMigrateAction = new MigrateAction(false); + List steps = disabledMigrateAction.toSteps(null, phase, nextStepKey); + assertEquals(0, steps.size()); + } + } +} From 6f83f914f5906f6b871f0db39aa14cc4ee199327 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 7 Sep 2020 14:27:28 +0100 Subject: [PATCH 12/34] Don't randomly pick a disabled migrate action as it has no steps --- .../elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java | 7 +++++-- .../elasticsearch/xpack/ilm/PolicyStepsRegistryTests.java | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java index ca2c2fc175642..ef5f57a67eeae 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java @@ -44,6 +44,7 @@ import org.elasticsearch.xpack.core.ilm.LifecyclePolicyMetadata; import org.elasticsearch.xpack.core.ilm.LifecyclePolicyTests; import org.elasticsearch.xpack.core.ilm.LifecycleSettings; +import org.elasticsearch.xpack.core.ilm.MigrateAction; import org.elasticsearch.xpack.core.ilm.MockAction; import org.elasticsearch.xpack.core.ilm.MockStep; import org.elasticsearch.xpack.core.ilm.OperationMode; @@ -91,6 +92,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; +; + public class IndexLifecycleRunnerTests extends ESTestCase { private static final NamedXContentRegistry REGISTRY; private ThreadPool threadPool; @@ -171,7 +174,7 @@ public void testRunPolicyErrorStep() { Phase phase = policy.getPhases().get(phaseName); PhaseExecutionInfo phaseExecutionInfo = new PhaseExecutionInfo(policy.getName(), phase, 1, randomNonNegativeLong()); String phaseJson = Strings.toString(phaseExecutionInfo); - LifecycleAction action = randomFrom(phase.getActions().values()); + LifecycleAction action = randomValueOtherThan(new MigrateAction(false), () -> randomFrom(phase.getActions().values())); Step step = randomFrom(action.toSteps(new NoOpClient(threadPool), phaseName, null)); StepKey stepKey = step.getKey(); @@ -729,7 +732,7 @@ public void testGetCurrentStep() { Phase phase = policy.getPhases().get(phaseName); PhaseExecutionInfo pei = new PhaseExecutionInfo(policy.getName(), phase, 1, randomNonNegativeLong()); String phaseJson = Strings.toString(pei); - LifecycleAction action = randomFrom(phase.getActions().values()); + LifecycleAction action = randomValueOtherThan(new MigrateAction(false), () -> randomFrom(phase.getActions().values())); Step step = randomFrom(action.toSteps(client, phaseName, MOCK_STEP_KEY)); Settings indexSettings = Settings.builder() .put("index.number_of_shards", 1) diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/PolicyStepsRegistryTests.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/PolicyStepsRegistryTests.java index 29b23e2d3ed18..f2e8928828282 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/PolicyStepsRegistryTests.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/PolicyStepsRegistryTests.java @@ -33,6 +33,7 @@ import org.elasticsearch.xpack.core.ilm.LifecyclePolicyMetadata; import org.elasticsearch.xpack.core.ilm.LifecyclePolicyTests; import org.elasticsearch.xpack.core.ilm.LifecycleSettings; +import org.elasticsearch.xpack.core.ilm.MigrateAction; import org.elasticsearch.xpack.core.ilm.MockStep; import org.elasticsearch.xpack.core.ilm.OperationMode; import org.elasticsearch.xpack.core.ilm.Phase; @@ -91,7 +92,7 @@ public void testGetStep() { Phase phase = policy.getPhases().get(phaseName); PhaseExecutionInfo pei = new PhaseExecutionInfo(policy.getName(), phase, 1, randomNonNegativeLong()); String phaseJson = Strings.toString(pei); - LifecycleAction action = randomFrom(phase.getActions().values()); + LifecycleAction action = randomValueOtherThan(new MigrateAction(false), () -> randomFrom(phase.getActions().values())); Step step = randomFrom(action.toSteps(client, phaseName, MOCK_STEP_KEY)); LifecycleExecutionState.Builder lifecycleState = LifecycleExecutionState.builder(); lifecycleState.setPhaseDefinition(phaseJson); @@ -159,7 +160,7 @@ public void testGetStepUnknownStepKey() { Phase phase = policy.getPhases().get(phaseName); PhaseExecutionInfo pei = new PhaseExecutionInfo(policy.getName(), phase, 1, randomNonNegativeLong()); String phaseJson = Strings.toString(pei); - LifecycleAction action = randomFrom(phase.getActions().values()); + LifecycleAction action = randomValueOtherThan(new MigrateAction(false), () -> randomFrom(phase.getActions().values())); Step step = randomFrom(action.toSteps(client, phaseName, MOCK_STEP_KEY)); LifecycleExecutionState.Builder lifecycleState = LifecycleExecutionState.builder(); lifecycleState.setPhaseDefinition(phaseJson); From 4bef9b262c50904a86b0ccac50daac0e64e8b5d3 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 7 Sep 2020 14:27:41 +0100 Subject: [PATCH 13/34] Add ILM tier migratino IT --- .../xpack/ilm/DataTiersMigrationsTests.java | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java diff --git a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java new file mode 100644 index 0000000000000..908fd3de21f9d --- /dev/null +++ b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java @@ -0,0 +1,149 @@ +/* + * 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.ilm; + +import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.xpack.core.DataTier; +import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin; +import org.elasticsearch.xpack.core.XPackSettings; +import org.elasticsearch.xpack.core.ilm.DataTierMigrationRoutedStep; +import org.elasticsearch.xpack.core.ilm.ExplainLifecycleRequest; +import org.elasticsearch.xpack.core.ilm.ExplainLifecycleResponse; +import org.elasticsearch.xpack.core.ilm.IndexLifecycleExplainResponse; +import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; +import org.elasticsearch.xpack.core.ilm.LifecycleSettings; +import org.elasticsearch.xpack.core.ilm.Phase; +import org.elasticsearch.xpack.core.ilm.action.ExplainLifecycleAction; +import org.elasticsearch.xpack.core.ilm.action.PutLifecycleAction; +import org.junit.Before; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Locale; +import java.util.Map; + +import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; +import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.elasticsearch.test.NodeRoles.onlyRole; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.hamcrest.Matchers.is; + +@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0) +public class DataTiersMigrationsTests extends ESIntegTestCase { + + private String policy; + private String managedIndex; + + @Before + public void refreshDataStreamAndPolicy() { + policy = "policy-" + randomAlphaOfLength(5); + managedIndex = "index-" + randomAlphaOfLengthBetween(10, 15).toLowerCase(Locale.ROOT); + } + + @Override + protected boolean ignoreExternalCluster() { + return true; + } + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(LocalStateCompositeXPackPlugin.class, IndexLifecycle.class); + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + Settings.Builder settings = Settings.builder().put(super.nodeSettings(nodeOrdinal)); + settings.put(XPackSettings.MACHINE_LEARNING_ENABLED.getKey(), false); + settings.put(XPackSettings.SECURITY_ENABLED.getKey(), false); + settings.put(XPackSettings.WATCHER_ENABLED.getKey(), false); + settings.put(XPackSettings.GRAPH_ENABLED.getKey(), false); + settings.put(LifecycleSettings.LIFECYCLE_POLL_INTERVAL, "1s"); + settings.put(LifecycleSettings.SLM_HISTORY_INDEX_ENABLED_SETTING.getKey(), false); + return settings.build(); + } + + public static Settings hotNode(final Settings settings) { + return onlyRole(settings, DataTier.DATA_HOT_NODE_ROLE); + } + + public static Settings warmNode(final Settings settings) { + return onlyRole(settings, DataTier.DATA_WARM_NODE_ROLE); + } + + public static Settings coldNode(final Settings settings) { + return onlyRole(settings, DataTier.DATA_COLD_NODE_ROLE); + } + + public static Settings frozenNode(final Settings settings) { + return onlyRole(settings, DataTier.DATA_FROZEN_NODE_ROLE); + } + + public void testIndexDataTierMigration() throws Exception { + internalCluster().startMasterOnlyNodes(3, Settings.EMPTY); + logger.info("starting hot data node"); + internalCluster().startNode(hotNode(Settings.EMPTY)); + + Phase hotPhase = new Phase("hot", TimeValue.ZERO, Collections.emptyMap()); + Phase warmPhase = new Phase("warm", TimeValue.ZERO, Collections.emptyMap()); + Phase coldPhase = new Phase("cold", TimeValue.ZERO, Collections.emptyMap()); + Phase frozenPhase = new Phase("frozen", TimeValue.ZERO, Collections.emptyMap()); + LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, Map.of("hot", hotPhase, "warm", warmPhase, "cold", coldPhase, + "frozen", frozenPhase)); + PutLifecycleAction.Request putLifecycleRequest = new PutLifecycleAction.Request(lifecyclePolicy); + PutLifecycleAction.Response putLifecycleResponse = client().execute(PutLifecycleAction.INSTANCE, putLifecycleRequest).get(); + assertAcked(putLifecycleResponse); + + Settings settings = Settings.builder().put(indexSettings()).put(SETTING_NUMBER_OF_SHARDS, 1) + .put(SETTING_NUMBER_OF_REPLICAS, 0).put(LifecycleSettings.LIFECYCLE_NAME, policy).build(); + CreateIndexResponse res = client().admin().indices().prepareCreate(managedIndex).setSettings(settings).get(); + assertTrue(res.isAcknowledged()); + + assertBusy(() -> { + ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest().indices(managedIndex); + ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, + explainRequest).get(); + + IndexLifecycleExplainResponse indexLifecycleExplainResponse = explainResponse.getIndexResponses().get(managedIndex); + assertThat(indexLifecycleExplainResponse.getPhase(), is("warm")); + assertThat(indexLifecycleExplainResponse.getStep(), is(DataTierMigrationRoutedStep.NAME)); + }); + + logger.info("starting warm data node"); + internalCluster().startNode(warmNode(Settings.EMPTY)); + assertBusy(() -> { + ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest().indices(managedIndex); + ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, + explainRequest).get(); + + IndexLifecycleExplainResponse indexLifecycleExplainResponse = explainResponse.getIndexResponses().get(managedIndex); + assertThat(indexLifecycleExplainResponse.getPhase(), is("cold")); + assertThat(indexLifecycleExplainResponse.getStep(), is(DataTierMigrationRoutedStep.NAME)); + }); + + logger.info("starting cold data node"); + internalCluster().startNode(coldNode(Settings.EMPTY)); + logger.info("starting frozen data node"); + internalCluster().startNode(frozenNode(Settings.EMPTY)); + + // wait for lifecycle to complete in the frozen phase after the index has been migrated to the frozen node + assertBusy(() -> { + ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest().indices(managedIndex); + ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, + explainRequest).get(); + + IndexLifecycleExplainResponse indexLifecycleExplainResponse = explainResponse.getIndexResponses().get(managedIndex); + assertThat(indexLifecycleExplainResponse.getPhase(), is("frozen")); + assertThat(indexLifecycleExplainResponse.getStep(), is("complete")); + }); + } + +} From 50245950fe3b5a3a30e74d52488c4d5137e7486a Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 7 Sep 2020 16:23:16 +0100 Subject: [PATCH 14/34] Use 0 replicas in docs test as `check-migration` waits for replicas to start --- .../client/documentation/ILMDocumentationIT.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ILMDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ILMDocumentationIT.java index dcb75960557d6..d520ac812f794 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ILMDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ILMDocumentationIT.java @@ -362,6 +362,7 @@ public void testExplainLifecycle() throws Exception { CreateIndexRequest createIndexRequest = new CreateIndexRequest("my_index-1") .settings(Settings.builder() .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) .put("index.lifecycle.name", "my_policy") .put("index.lifecycle.rollover_alias", "my_alias") .build()); @@ -370,6 +371,7 @@ public void testExplainLifecycle() throws Exception { CreateIndexRequest createOtherIndexRequest = new CreateIndexRequest("other_index") .settings(Settings.builder() .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) .build()); client.indices().create(createOtherIndexRequest, RequestOptions.DEFAULT); @@ -624,6 +626,7 @@ public void testRetryPolicy() throws Exception { CreateIndexRequest createIndexRequest = new CreateIndexRequest("my_index") .settings(Settings.builder() .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 2) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) .put("index.lifecycle.name", "my_policy") .build()); client.indices().create(createIndexRequest, RequestOptions.DEFAULT); @@ -689,6 +692,7 @@ public void testRemovePolicyFromIndex() throws Exception { CreateIndexRequest createIndexRequest = new CreateIndexRequest("my_index") .settings(Settings.builder() .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) .put("index.lifecycle.name", "my_policy") .build()); client.indices().create(createIndexRequest, RequestOptions.DEFAULT); From 41dc480bc6a1e1000c07c802c0f86b4f629ce946 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 7 Sep 2020 16:23:36 +0100 Subject: [PATCH 15/34] Fix ILMHistoryTests --- .../java/org/elasticsearch/xpack/ilm/ILMHistoryTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMHistoryTests.java b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMHistoryTests.java index 0b62016ef5071..c7a8f1573e127 100644 --- a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMHistoryTests.java +++ b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMHistoryTests.java @@ -92,7 +92,7 @@ public void testIlmHistoryIndexCanRollover() throws Exception { //wait for all history items to index to avoid waiting for timeout in ILMHistoryStore beforeBulk assertBusy(() -> { SearchResponse search = client().prepareSearch(firstIndex).setQuery(matchQuery("index", firstIndex)).setSize(0).get(); - assertThat(search.getHits().getTotalHits().value, is(9L)); + assertThat(search.getHits().getTotalHits().value, is(11L)); }); //make sure ILM is stopped so no new items will be queued in ILM history From 8e9a4f3248f7f5ab374842f5696212f3371ae36a Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 7 Sep 2020 16:37:11 +0100 Subject: [PATCH 16/34] Disable migrate action in test that waits for shard allocations --- .../xpack/ilm/TimeSeriesLifecycleActionsIT.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java index c2695cbac36e9..e19b5875acbd3 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java @@ -36,6 +36,7 @@ import org.elasticsearch.xpack.core.ilm.LifecycleAction; import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; import org.elasticsearch.xpack.core.ilm.LifecycleSettings; +import org.elasticsearch.xpack.core.ilm.MigrateAction; import org.elasticsearch.xpack.core.ilm.Phase; import org.elasticsearch.xpack.core.ilm.PhaseCompleteStep; import org.elasticsearch.xpack.core.ilm.ReadOnlyAction; @@ -669,8 +670,20 @@ public void testSetSingleNodeAllocationRetriesUntilItSucceeds() throws Exception request.addParameter("level", "shards"); }); - // assign the policy that'll attempt to shrink the index - createNewSingletonPolicy(client(), policy, "warm", new ShrinkAction(expectedFinalShards)); + // assign the policy that'll attempt to shrink the index (disabling the migrate action as it'll otherwise wait for + // all shards to be active and we want that to happen as part of the shrink action) + MigrateAction migrateAction = new MigrateAction(false); + ShrinkAction shrinkAction = new ShrinkAction(expectedFinalShards); + Phase phase = new Phase("warm", TimeValue.ZERO, Map.of(migrateAction.getWriteableName(), migrateAction, + shrinkAction.getWriteableName(), shrinkAction)); + LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, singletonMap(phase.getName(), phase)); + XContentBuilder builder = jsonBuilder(); + lifecyclePolicy.toXContent(builder, null); + final StringEntity entity = new StringEntity( + "{ \"policy\":" + Strings.toString(builder) + "}", ContentType.APPLICATION_JSON); + Request putPolicyRequest = new Request("PUT", "_ilm/policy/" + policy); + putPolicyRequest.setEntity(entity); + client().performRequest(putPolicyRequest); updatePolicy(index, policy); assertTrue("ILM did not start retrying the set-single-node-allocation step", waitUntil(() -> { From 996c747191e5f90754fb7d1dad3802228b206410 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 7 Sep 2020 17:55:35 +0100 Subject: [PATCH 17/34] Add ILM client MigrateAction --- .../IndexLifecycleNamedXContentProvider.java | 3 + .../client/ilm/LifecyclePolicy.java | 11 ++- .../client/ilm/MigrateAction.java | 90 +++++++++++++++++++ .../client/IndexLifecycleIT.java | 2 + .../client/ilm/MigrateActionTests.java | 46 ++++++++++ 5 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/ilm/MigrateActionTests.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/IndexLifecycleNamedXContentProvider.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/IndexLifecycleNamedXContentProvider.java index fdaaaa8874307..aac864082be85 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/IndexLifecycleNamedXContentProvider.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/IndexLifecycleNamedXContentProvider.java @@ -57,6 +57,9 @@ public List getNamedXContentParsers() { new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(SetPriorityAction.NAME), SetPriorityAction::parse), + new NamedXContentRegistry.Entry(LifecycleAction.class, + new ParseField(MigrateAction.NAME), + MigrateAction::parse), new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(UnfollowAction.NAME), UnfollowAction::parse) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java index b6d16ddccfe89..f0c23e7a4e8b7 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java @@ -57,10 +57,13 @@ public class LifecyclePolicy implements ToXContentObject { throw new IllegalArgumentException("ordered " + PHASES_FIELD.getPreferredName() + " are not supported"); }, PHASES_FIELD); - ALLOWED_ACTIONS.put("hot", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, RolloverAction.NAME)); - ALLOWED_ACTIONS.put("warm", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, AllocateAction.NAME, ForceMergeAction.NAME, - ReadOnlyAction.NAME, ShrinkAction.NAME)); - ALLOWED_ACTIONS.put("cold", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, AllocateAction.NAME, FreezeAction.NAME)); + ALLOWED_ACTIONS.put("hot", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, MigrateAction.NAME, RolloverAction.NAME)); + ALLOWED_ACTIONS.put("warm", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, MigrateAction.NAME, AllocateAction.NAME, + ForceMergeAction.NAME, ReadOnlyAction.NAME, ShrinkAction.NAME)); + ALLOWED_ACTIONS.put("cold", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, MigrateAction.NAME, AllocateAction.NAME, + FreezeAction.NAME)); + ALLOWED_ACTIONS.put("frozen", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, MigrateAction.NAME, AllocateAction.NAME, + FreezeAction.NAME)); ALLOWED_ACTIONS.put("delete", Sets.newHashSet(DeleteAction.NAME)); } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java new file mode 100644 index 0000000000000..2ce846d8fd902 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java @@ -0,0 +1,90 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.ilm; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; + +public class MigrateAction implements LifecycleAction, ToXContentObject { + public static final String NAME = "migrate"; + + public static final ParseField ENABLED_FIELD = new ParseField("enabled"); + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(NAME, + a -> new MigrateAction(a[0] == null ? true : (boolean) a[0])); + + static { + PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), ENABLED_FIELD); + } + + public static MigrateAction parse(XContentParser parser) { + return PARSER.apply(parser, null); + } + + private final boolean enabled; + + public MigrateAction() { + this(true); + } + + public MigrateAction(boolean enabled) { + this.enabled = enabled; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(ENABLED_FIELD.getPreferredName(), enabled); + builder.endObject(); + return builder; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public int hashCode() { + return 111; + } + + @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); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java index 9844e503bb4b0..d30195b5db44b 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java @@ -37,6 +37,7 @@ import org.elasticsearch.client.ilm.LifecycleManagementStatusResponse; import org.elasticsearch.client.ilm.LifecyclePolicy; import org.elasticsearch.client.ilm.LifecyclePolicyMetadata; +import org.elasticsearch.client.ilm.MigrateAction; import org.elasticsearch.client.ilm.OperationMode; import org.elasticsearch.client.ilm.Phase; import org.elasticsearch.client.ilm.PhaseExecutionInfo; @@ -146,6 +147,7 @@ public void testStartStopILM() throws Exception { public void testExplainLifecycle() throws Exception { Map lifecyclePhases = new HashMap<>(); Map hotActions = new HashMap<>(); + hotActions.put(MigrateAction.NAME, new MigrateAction(false)); hotActions.put(RolloverAction.NAME, new RolloverAction(null, TimeValue.timeValueHours(50 * 24), null)); Phase hotPhase = new Phase("hot", randomFrom(TimeValue.ZERO, null), hotActions); lifecyclePhases.put("hot", hotPhase); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ilm/MigrateActionTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ilm/MigrateActionTests.java new file mode 100644 index 0000000000000..b03c050d4d93a --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ilm/MigrateActionTests.java @@ -0,0 +1,46 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.ilm; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractXContentTestCase; + +import java.io.IOException; + +public class MigrateActionTests extends AbstractXContentTestCase { + + @Override + protected MigrateAction doParseInstance(XContentParser parser) throws IOException { + return MigrateAction.parse(parser); + } + + @Override + protected MigrateAction createTestInstance() { + return randomInstance(); + } + + static MigrateAction randomInstance() { + return new MigrateAction(randomBoolean()); + } + + @Override + protected boolean supportsUnknownFields() { + return false; + } +} From 83c31b682ba7cee573066f83e8b7ce29ac63478e Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 7 Sep 2020 17:56:44 +0100 Subject: [PATCH 18/34] Remove unused import --- .../main/java/org/elasticsearch/client/ilm/MigrateAction.java | 1 - 1 file changed, 1 deletion(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java index 2ce846d8fd902..a39316a3dead8 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java @@ -21,7 +21,6 @@ import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; import org.elasticsearch.common.xcontent.ConstructingObjectParser; -import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; From c1746d4a6322027112f1c80ea747f12549db4d06 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 7 Sep 2020 18:07:25 +0100 Subject: [PATCH 19/34] Fix HLRC tests --- .../org/elasticsearch/client/RestHighLevelClientTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java index b6d722301251f..8df8f7299965e 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java @@ -705,7 +705,7 @@ public void testDefaultNamedXContents() { public void testProvidedNamedXContents() { List namedXContents = RestHighLevelClient.getProvidedNamedXContents(); - assertEquals(70, namedXContents.size()); + assertEquals(71, namedXContents.size()); Map, Integer> categories = new HashMap<>(); List names = new ArrayList<>(); for (NamedXContentRegistry.Entry namedXContent : namedXContents) { @@ -731,7 +731,7 @@ public void testProvidedNamedXContents() { assertTrue(names.contains(MeanReciprocalRank.NAME)); assertTrue(names.contains(DiscountedCumulativeGain.NAME)); assertTrue(names.contains(ExpectedReciprocalRank.NAME)); - assertEquals(Integer.valueOf(9), categories.get(LifecycleAction.class)); + assertEquals(Integer.valueOf(10), categories.get(LifecycleAction.class)); assertTrue(names.contains(UnfollowAction.NAME)); assertTrue(names.contains(AllocateAction.NAME)); assertTrue(names.contains(DeleteAction.NAME)); From fe1fb8f5e70847ccdfb5e52e519aad6c09164e2b Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 7 Sep 2020 18:56:05 +0100 Subject: [PATCH 20/34] Add tests for conflicting auto allocate to hot nodes and MigrateAction configurations --- .../xpack/ilm/DataTiersMigrationsTests.java | 103 +++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java index 908fd3de21f9d..5d24a57614ec0 100644 --- a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java +++ b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java @@ -6,11 +6,16 @@ package org.elasticsearch.xpack.ilm; +import org.elasticsearch.action.admin.cluster.allocation.ClusterAllocationExplainAction; +import org.elasticsearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest; +import org.elasticsearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse; import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; +import org.elasticsearch.cluster.node.DiscoveryNodeRole; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider; import org.elasticsearch.xpack.core.DataTier; import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin; import org.elasticsearch.xpack.core.XPackSettings; @@ -20,6 +25,7 @@ import org.elasticsearch.xpack.core.ilm.IndexLifecycleExplainResponse; import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; import org.elasticsearch.xpack.core.ilm.LifecycleSettings; +import org.elasticsearch.xpack.core.ilm.MigrateAction; import org.elasticsearch.xpack.core.ilm.Phase; import org.elasticsearch.xpack.core.ilm.action.ExplainLifecycleAction; import org.elasticsearch.xpack.core.ilm.action.PutLifecycleAction; @@ -30,6 +36,7 @@ import java.util.Collections; import java.util.Locale; import java.util.Map; +import java.util.Set; import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; @@ -68,6 +75,7 @@ protected Settings nodeSettings(int nodeOrdinal) { settings.put(XPackSettings.GRAPH_ENABLED.getKey(), false); settings.put(LifecycleSettings.LIFECYCLE_POLL_INTERVAL, "1s"); settings.put(LifecycleSettings.SLM_HISTORY_INDEX_ENABLED_SETTING.getKey(), false); + settings.put(LifecycleSettings.LIFECYCLE_HISTORY_INDEX_ENABLED, false); return settings.build(); } @@ -88,7 +96,7 @@ public static Settings frozenNode(final Settings settings) { } public void testIndexDataTierMigration() throws Exception { - internalCluster().startMasterOnlyNodes(3, Settings.EMPTY); + internalCluster().startMasterOnlyNodes(1, Settings.EMPTY); logger.info("starting hot data node"); internalCluster().startNode(hotNode(Settings.EMPTY)); @@ -146,4 +154,97 @@ public void testIndexDataTierMigration() throws Exception { }); } + public void testDisabledMigrateActionInHotPhaseDontMoveIndicesAwayFromHotNodes() throws Exception { + internalCluster().startMasterOnlyNodes(1, Settings.EMPTY); + logger.info("starting hot data node"); + internalCluster().startNode(hotNode(Settings.EMPTY)); + logger.info("starting warm data node"); + internalCluster().startNode(warmNode(Settings.EMPTY)); + logger.info("starting cold data node"); + internalCluster().startNode(coldNode(Settings.EMPTY)); + Phase hotPhase = new Phase("hot", TimeValue.ZERO, Map.of(MigrateAction.NAME, new MigrateAction(false))); + LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, Map.of("hot", hotPhase)); + PutLifecycleAction.Request putLifecycleRequest = new PutLifecycleAction.Request(lifecyclePolicy); + PutLifecycleAction.Response putLifecycleResponse = client().execute(PutLifecycleAction.INSTANCE, putLifecycleRequest).get(); + assertAcked(putLifecycleResponse); + + Settings settings = Settings.builder().put(indexSettings()).put(SETTING_NUMBER_OF_SHARDS, 3) + .put(SETTING_NUMBER_OF_REPLICAS, 0).put(LifecycleSettings.LIFECYCLE_NAME, policy).build(); + CreateIndexResponse res = client().admin().indices().prepareCreate(managedIndex).setSettings(settings).get(); + assertTrue(res.isAcknowledged()); + + // ILM has run + assertBusy(() -> { + ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest().indices(managedIndex); + ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, + explainRequest).get(); + + IndexLifecycleExplainResponse indexLifecycleExplainResponse = explainResponse.getIndexResponses().get(managedIndex); + assertThat(indexLifecycleExplainResponse.getPhase(), is("hot")); + assertThat(indexLifecycleExplainResponse.getStep(), is("complete")); + }); + + + // verify index is allocated only on the hot node + for (int shardId = 0; shardId < 3; shardId++) { + ClusterAllocationExplainRequest request = new ClusterAllocationExplainRequest().setIndex(managedIndex).setShard(shardId) + .setPrimary(true); + + ClusterAllocationExplainResponse allocationExplainResponse = client().execute(ClusterAllocationExplainAction.INSTANCE, request) + .get(); + Set roles = allocationExplainResponse.getExplanation().getCurrentNode().getRoles(); + assertThat(roles.size(), is(1)); + assertThat(roles.iterator().next().roleName(), is(DataTier.DATA_HOT)); + } + } + + public void testDisableAutoAllocationOfNewIndicesToHotButEnableMigrateAction() throws Exception { + internalCluster().startMasterOnlyNodes(1, Settings.EMPTY); + logger.info("starting hot data node"); + internalCluster().startNode(hotNode(Settings.EMPTY)); + logger.info("starting warm data node"); + internalCluster().startNode(warmNode(Settings.EMPTY)); + logger.info("starting cold data node"); + internalCluster().startNode(coldNode(Settings.EMPTY)); + + Phase hotPhase = new Phase("hot", TimeValue.ZERO, Map.of(MigrateAction.NAME, new MigrateAction())); + LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, Map.of("hot", hotPhase)); + PutLifecycleAction.Request putLifecycleRequest = new PutLifecycleAction.Request(lifecyclePolicy); + PutLifecycleAction.Response putLifecycleResponse = client().execute(PutLifecycleAction.INSTANCE, putLifecycleRequest).get(); + assertAcked(putLifecycleResponse); + + Settings settings = Settings.builder().put(indexSettings()).put(SETTING_NUMBER_OF_SHARDS, 3) + .put(SETTING_NUMBER_OF_REPLICAS, 0).put(LifecycleSettings.LIFECYCLE_NAME, policy) + // this will disable the auto allocation of the index to the hot node + .putNull(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE) + .build(); + CreateIndexResponse res = client().admin().indices().prepareCreate(managedIndex).setSettings(settings).get(); + assertTrue(res.isAcknowledged()); + + // ILM has run + assertBusy(() -> { + ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest().indices(managedIndex); + ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, + explainRequest).get(); + + IndexLifecycleExplainResponse indexLifecycleExplainResponse = explainResponse.getIndexResponses().get(managedIndex); + assertThat(indexLifecycleExplainResponse.getPhase(), is("hot")); + assertThat(indexLifecycleExplainResponse.getStep(), is("complete")); + }); + + + // verify index is allocated only on the hot node + for (int shardId = 0; shardId < 3; shardId++) { + ClusterAllocationExplainRequest request = new ClusterAllocationExplainRequest().setIndex(managedIndex).setShard(shardId) + .setPrimary(true); + + ClusterAllocationExplainResponse allocationExplainResponse = client().execute(ClusterAllocationExplainAction.INSTANCE, request) + .get(); + Set roles = allocationExplainResponse.getExplanation().getCurrentNode().getRoles(); + assertThat(roles.size(), is(1)); + assertThat(roles.iterator().next().roleName(), is(DataTier.DATA_HOT)); + } + + } + } From e435a2f26b9fc1def4256ff053e7f7ab12878b72 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 7 Sep 2020 19:40:44 +0100 Subject: [PATCH 21/34] Use valid tier name in test --- .../org/elasticsearch/xpack/core/ilm/MigrateActionTests.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/MigrateActionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/MigrateActionTests.java index 4278f515bc1bc..67bdb7f03e795 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/MigrateActionTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/MigrateActionTests.java @@ -12,6 +12,8 @@ import java.io.IOException; import java.util.List; +import static org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType.DELETE_PHASE; + public class MigrateActionTests extends AbstractActionTestCase { @Override @@ -30,7 +32,7 @@ protected Reader instanceReader() { } public void testToSteps() { - String phase = randomFrom(TimeseriesLifecycleType.VALID_PHASES); + String phase = randomValueOtherThan(DELETE_PHASE, () -> randomFrom(TimeseriesLifecycleType.VALID_PHASES)); StepKey nextStepKey = new StepKey(randomAlphaOfLengthBetween(1, 10), randomAlphaOfLengthBetween(1, 10), randomAlphaOfLengthBetween(1, 10)); { From 656afe540f78a4e674b556159527e1d0bc9efa95 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Tue, 8 Sep 2020 11:12:22 +0100 Subject: [PATCH 22/34] Implement hashcode --- .../main/java/org/elasticsearch/client/ilm/MigrateAction.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java index a39316a3dead8..00f413a969462 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/MigrateAction.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; +import java.util.Objects; public class MigrateAction implements LifecycleAction, ToXContentObject { public static final String NAME = "migrate"; @@ -68,7 +69,7 @@ public String getName() { @Override public int hashCode() { - return 111; + return Objects.hashCode(enabled); } @Override From 8d806e2cd886600eda641c6dead953cec98e175b Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Tue, 8 Sep 2020 11:47:02 +0100 Subject: [PATCH 23/34] Make method static --- .../xpack/core/ilm/TimeseriesLifecycleType.java | 2 +- .../xpack/core/ilm/TimeseriesLifecycleTypeTests.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java index 704cb4a477392..c8e262a7b7ecc 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java @@ -102,7 +102,7 @@ public List getOrderedPhases(Map phases) { return orderedPhases; } - boolean shouldInjectMigrateStepForPhase(Phase phase) { + static boolean shouldInjectMigrateStepForPhase(Phase phase) { AllocateAction allocateAction = (AllocateAction) phase.getActions().get(AllocateAction.NAME); if (allocateAction != null) { if (definesAllocationRules(allocateAction)) { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java index 65931fbd318e1..ae1254981ce30 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java @@ -637,7 +637,7 @@ public void testShouldMigrateDataToTiers() { actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(false)); actions.put(TEST_ALLOCATE_ACTION.getWriteableName(), TEST_ALLOCATE_ACTION); Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); - assertThat(TimeseriesLifecycleType.INSTANCE.shouldInjectMigrateStepForPhase(phase), is(false)); + assertThat(TimeseriesLifecycleType.shouldInjectMigrateStepForPhase(phase), is(false)); } { @@ -645,7 +645,7 @@ public void testShouldMigrateDataToTiers() { Map actions = new HashMap<>(); actions.put(TEST_ALLOCATE_ACTION.getWriteableName(), new AllocateAction(2, null, null, null)); Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); - assertThat(TimeseriesLifecycleType.INSTANCE.shouldInjectMigrateStepForPhase(phase), is(true)); + assertThat(TimeseriesLifecycleType.shouldInjectMigrateStepForPhase(phase), is(true)); } { @@ -653,7 +653,7 @@ public void testShouldMigrateDataToTiers() { Map actions = new HashMap<>(); actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(true)); Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); - assertThat(TimeseriesLifecycleType.INSTANCE.shouldInjectMigrateStepForPhase(phase), is(false)); + assertThat(TimeseriesLifecycleType.shouldInjectMigrateStepForPhase(phase), is(false)); } { @@ -661,7 +661,7 @@ public void testShouldMigrateDataToTiers() { Map actions = new HashMap<>(); actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(false)); Phase phase = new Phase(WARM_PHASE, TimeValue.ZERO, actions); - assertThat(TimeseriesLifecycleType.INSTANCE.shouldInjectMigrateStepForPhase(phase), is(false)); + assertThat(TimeseriesLifecycleType.shouldInjectMigrateStepForPhase(phase), is(false)); } } From 2133715100e7c1ee0367ad6a13a66032164953c0 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 10 Sep 2020 10:23:18 +0100 Subject: [PATCH 24/34] Check for "data" role in `check-migration` step --- .../core/ilm/DataTierMigrationRoutedStep.java | 3 ++- .../ilm/DataTierMigrationRoutedStepTests.java | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java index 6ec03e7f58d02..829ed32ecc45d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java @@ -25,6 +25,7 @@ import java.util.Locale; import java.util.Set; +import static org.elasticsearch.cluster.node.DiscoveryNodeRole.DATA_ROLE; import static org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider.INDEX_ROUTING_INCLUDE_SETTING; import static org.elasticsearch.xpack.core.ilm.AllocationRoutedStep.getPendingAllocations; @@ -83,7 +84,7 @@ public Result isConditionMet(Index index, ClusterState clusterState) { boolean targetTierNodeFound = false; for (DiscoveryNode node : clusterState.nodes()) { for (DiscoveryNodeRole role : node.getRoles()) { - if (role.roleName().equals(tier)) { + if (role.roleName().equals(DATA_ROLE.roleName()) || role.roleName().equals(tier)) { targetTierNodeFound = true; break; } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStepTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStepTests.java index a14f365e08d3c..13ae30adba4f6 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStepTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStepTests.java @@ -179,6 +179,27 @@ public void testExecuteIsComplete() { assertThat(result.getInfomationContext(), is(nullValue())); } + public void testExecuteWithGenericDataNodes() { + IndexMetadata indexMetadata = IndexMetadata.builder(randomAlphaOfLengthBetween(5, 10)) + .settings(settings(Version.CURRENT).put(INDEX_ROUTING_INCLUDE_SETTING.getKey(), DataTier.DATA_WARM)) + .numberOfShards(1).numberOfReplicas(0).build(); + Index index = indexMetadata.getIndex(); + IndexRoutingTable.Builder indexRoutingTable = IndexRoutingTable.builder(index) + .addShard(TestShardRouting.newShardRouting(new ShardId(index, 0), "node1", true, ShardRoutingState.STARTED)); + + ClusterState clusterState = + ClusterState.builder(ClusterState.EMPTY_STATE).metadata(Metadata.builder().put(indexMetadata, true).build()) + .nodes(DiscoveryNodes.builder() + .add(newNode("node1", Collections.singleton(DiscoveryNodeRole.DATA_ROLE))) + ) + .routingTable(RoutingTable.builder().add(indexRoutingTable).build()) + .build(); + DataTierMigrationRoutedStep step = createRandomInstance(); + Result result = step.isConditionMet(index, clusterState); + assertThat(result.isComplete(), is(true)); + assertThat(result.getInfomationContext(), is(nullValue())); + } + private DiscoveryNode newNode(String nodeId, Set roles) { return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), emptyMap(), roles, Version.CURRENT); } From 991fdc078797bae280cb3b37c26bcc60c67cb317 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 10 Sep 2020 10:33:42 +0100 Subject: [PATCH 25/34] Document AllocationInfo and rename `actual_replicas` to `number_of_replicas` --- docs/reference/ilm/apis/explain.asciidoc | 2 +- .../core/ilm/step/info/AllocationInfo.java | 26 ++++++++++++------- .../info/AllocationRoutedStepInfoTests.java | 4 +-- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/reference/ilm/apis/explain.asciidoc b/docs/reference/ilm/apis/explain.asciidoc index 66984b7ac4a36..17f016a004032 100644 --- a/docs/reference/ilm/apis/explain.asciidoc +++ b/docs/reference/ilm/apis/explain.asciidoc @@ -217,7 +217,7 @@ information for the step that's being performed on the index. "message": "Waiting for all shard copies to be active", "shards_left_to_allocate": -1, "all_shards_active": false, - "actual_replicas": 2 + "number_of_replicas": 2 }, "phase_execution": { "policy": "my_lifecycle3", diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java index d81091c0abc1b..7b5b2691f7e39 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java @@ -15,13 +15,19 @@ import java.io.IOException; import java.util.Objects; +/** + * Represents the state of an index's shards allocation, including a user friendly message describing the current state. + * It allows to transfer the allocation information to {@link org.elasticsearch.common.xcontent.XContent} using + * {@link #toXContent(XContentBuilder, Params)} + */ public class AllocationInfo implements ToXContentObject { - private final long actualReplicas; + + private final long numberOfReplicas; private final long numberShardsLeftToAllocate; private final boolean allShardsActive; private final String message; - static final ParseField ACTUAL_REPLICAS = new ParseField("actual_replicas"); + static final ParseField NUMBER_OF_REPLICAS = new ParseField("number_of_replicas"); static final ParseField SHARDS_TO_ALLOCATE = new ParseField("shards_left_to_allocate"); static final ParseField ALL_SHARDS_ACTIVE = new ParseField("all_shards_active"); static final ParseField MESSAGE = new ParseField("message"); @@ -29,14 +35,14 @@ public class AllocationInfo implements ToXContentObject { a -> new AllocationInfo((long) a[0], (long) a[1], (boolean) a[2], (String) a[3])); static { - PARSER.declareLong(ConstructingObjectParser.constructorArg(), ACTUAL_REPLICAS); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), NUMBER_OF_REPLICAS); PARSER.declareLong(ConstructingObjectParser.constructorArg(), SHARDS_TO_ALLOCATE); PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), ALL_SHARDS_ACTIVE); PARSER.declareString(ConstructingObjectParser.constructorArg(), MESSAGE); } - public AllocationInfo(long actualReplicas, long numberShardsLeftToAllocate, boolean allShardsActive, String message) { - this.actualReplicas = actualReplicas; + public AllocationInfo(long numberOfReplicas, long numberShardsLeftToAllocate, boolean allShardsActive, String message) { + this.numberOfReplicas = numberOfReplicas; this.numberShardsLeftToAllocate = numberShardsLeftToAllocate; this.allShardsActive = allShardsActive; this.message = message; @@ -60,8 +66,8 @@ public static AllocationInfo allShardsActiveAllocationInfo(long actualReplicas, "] shards to be allocated to nodes matching the given filters"); } - public long getActualReplicas() { - return actualReplicas; + public long getNumberOfReplicas() { + return numberOfReplicas; } public long getNumberShardsLeftToAllocate() { @@ -82,14 +88,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(MESSAGE.getPreferredName(), message); builder.field(SHARDS_TO_ALLOCATE.getPreferredName(), numberShardsLeftToAllocate); builder.field(ALL_SHARDS_ACTIVE.getPreferredName(), allShardsActive); - builder.field(ACTUAL_REPLICAS.getPreferredName(), actualReplicas); + builder.field(NUMBER_OF_REPLICAS.getPreferredName(), numberOfReplicas); builder.endObject(); return builder; } @Override public int hashCode() { - return Objects.hash(actualReplicas, numberShardsLeftToAllocate, allShardsActive); + return Objects.hash(numberOfReplicas, numberShardsLeftToAllocate, allShardsActive); } @Override @@ -101,7 +107,7 @@ public boolean equals(Object obj) { return false; } AllocationInfo other = (AllocationInfo) obj; - return Objects.equals(actualReplicas, other.actualReplicas) && + return Objects.equals(numberOfReplicas, other.numberOfReplicas) && Objects.equals(numberShardsLeftToAllocate, other.numberShardsLeftToAllocate) && Objects.equals(message, other.message) && Objects.equals(allShardsActive, other.allShardsActive); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationRoutedStepInfoTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationRoutedStepInfoTests.java index b72b979ad5d7f..b052ba0e38193 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationRoutedStepInfoTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationRoutedStepInfoTests.java @@ -36,12 +36,12 @@ public final void testEqualsAndHashcode() { } protected final AllocationInfo copyInstance(AllocationInfo instance) { - return new AllocationInfo(instance.getActualReplicas(), instance.getNumberShardsLeftToAllocate(), instance.allShardsActive(), + return new AllocationInfo(instance.getNumberOfReplicas(), instance.getNumberShardsLeftToAllocate(), instance.allShardsActive(), instance.getMessage()); } protected AllocationInfo mutateInstance(AllocationInfo instance) throws IOException { - long actualReplicas = instance.getActualReplicas(); + long actualReplicas = instance.getNumberOfReplicas(); long shardsToAllocate = instance.getNumberShardsLeftToAllocate(); boolean allShardsActive = instance.allShardsActive(); var message = instance.getMessage(); From be6592903733d247980b680d6075d2ed30bf89ce Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 10 Sep 2020 10:38:40 +0100 Subject: [PATCH 26/34] Disallow the migrate action in the hot phase --- .../core/ilm/TimeseriesLifecycleType.java | 4 +- .../xpack/ilm/DataTiersMigrationsTests.java | 101 ------------------ 2 files changed, 2 insertions(+), 103 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java index c8e262a7b7ecc..783761b82ea7c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java @@ -40,8 +40,8 @@ public class TimeseriesLifecycleType implements LifecycleType { static final String FROZEN_PHASE = "frozen"; static final String DELETE_PHASE = "delete"; static final List VALID_PHASES = Arrays.asList(HOT_PHASE, WARM_PHASE, COLD_PHASE, FROZEN_PHASE, DELETE_PHASE); - static final List ORDERED_VALID_HOT_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, MigrateAction.NAME, - RolloverAction.NAME, ForceMergeAction.NAME); + static final List ORDERED_VALID_HOT_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, RolloverAction.NAME, + ForceMergeAction.NAME); static final List ORDERED_VALID_WARM_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, ReadOnlyAction.NAME, MigrateAction.NAME, AllocateAction.NAME, ShrinkAction.NAME, ForceMergeAction.NAME); static final List ORDERED_VALID_COLD_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, diff --git a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java index 5d24a57614ec0..bfd3aee67e73d 100644 --- a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java +++ b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java @@ -6,16 +6,11 @@ package org.elasticsearch.xpack.ilm; -import org.elasticsearch.action.admin.cluster.allocation.ClusterAllocationExplainAction; -import org.elasticsearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest; -import org.elasticsearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse; import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; -import org.elasticsearch.cluster.node.DiscoveryNodeRole; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider; import org.elasticsearch.xpack.core.DataTier; import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin; import org.elasticsearch.xpack.core.XPackSettings; @@ -25,7 +20,6 @@ import org.elasticsearch.xpack.core.ilm.IndexLifecycleExplainResponse; import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; import org.elasticsearch.xpack.core.ilm.LifecycleSettings; -import org.elasticsearch.xpack.core.ilm.MigrateAction; import org.elasticsearch.xpack.core.ilm.Phase; import org.elasticsearch.xpack.core.ilm.action.ExplainLifecycleAction; import org.elasticsearch.xpack.core.ilm.action.PutLifecycleAction; @@ -36,7 +30,6 @@ import java.util.Collections; import java.util.Locale; import java.util.Map; -import java.util.Set; import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; @@ -153,98 +146,4 @@ public void testIndexDataTierMigration() throws Exception { assertThat(indexLifecycleExplainResponse.getStep(), is("complete")); }); } - - public void testDisabledMigrateActionInHotPhaseDontMoveIndicesAwayFromHotNodes() throws Exception { - internalCluster().startMasterOnlyNodes(1, Settings.EMPTY); - logger.info("starting hot data node"); - internalCluster().startNode(hotNode(Settings.EMPTY)); - logger.info("starting warm data node"); - internalCluster().startNode(warmNode(Settings.EMPTY)); - logger.info("starting cold data node"); - internalCluster().startNode(coldNode(Settings.EMPTY)); - Phase hotPhase = new Phase("hot", TimeValue.ZERO, Map.of(MigrateAction.NAME, new MigrateAction(false))); - LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, Map.of("hot", hotPhase)); - PutLifecycleAction.Request putLifecycleRequest = new PutLifecycleAction.Request(lifecyclePolicy); - PutLifecycleAction.Response putLifecycleResponse = client().execute(PutLifecycleAction.INSTANCE, putLifecycleRequest).get(); - assertAcked(putLifecycleResponse); - - Settings settings = Settings.builder().put(indexSettings()).put(SETTING_NUMBER_OF_SHARDS, 3) - .put(SETTING_NUMBER_OF_REPLICAS, 0).put(LifecycleSettings.LIFECYCLE_NAME, policy).build(); - CreateIndexResponse res = client().admin().indices().prepareCreate(managedIndex).setSettings(settings).get(); - assertTrue(res.isAcknowledged()); - - // ILM has run - assertBusy(() -> { - ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest().indices(managedIndex); - ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, - explainRequest).get(); - - IndexLifecycleExplainResponse indexLifecycleExplainResponse = explainResponse.getIndexResponses().get(managedIndex); - assertThat(indexLifecycleExplainResponse.getPhase(), is("hot")); - assertThat(indexLifecycleExplainResponse.getStep(), is("complete")); - }); - - - // verify index is allocated only on the hot node - for (int shardId = 0; shardId < 3; shardId++) { - ClusterAllocationExplainRequest request = new ClusterAllocationExplainRequest().setIndex(managedIndex).setShard(shardId) - .setPrimary(true); - - ClusterAllocationExplainResponse allocationExplainResponse = client().execute(ClusterAllocationExplainAction.INSTANCE, request) - .get(); - Set roles = allocationExplainResponse.getExplanation().getCurrentNode().getRoles(); - assertThat(roles.size(), is(1)); - assertThat(roles.iterator().next().roleName(), is(DataTier.DATA_HOT)); - } - } - - public void testDisableAutoAllocationOfNewIndicesToHotButEnableMigrateAction() throws Exception { - internalCluster().startMasterOnlyNodes(1, Settings.EMPTY); - logger.info("starting hot data node"); - internalCluster().startNode(hotNode(Settings.EMPTY)); - logger.info("starting warm data node"); - internalCluster().startNode(warmNode(Settings.EMPTY)); - logger.info("starting cold data node"); - internalCluster().startNode(coldNode(Settings.EMPTY)); - - Phase hotPhase = new Phase("hot", TimeValue.ZERO, Map.of(MigrateAction.NAME, new MigrateAction())); - LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, Map.of("hot", hotPhase)); - PutLifecycleAction.Request putLifecycleRequest = new PutLifecycleAction.Request(lifecyclePolicy); - PutLifecycleAction.Response putLifecycleResponse = client().execute(PutLifecycleAction.INSTANCE, putLifecycleRequest).get(); - assertAcked(putLifecycleResponse); - - Settings settings = Settings.builder().put(indexSettings()).put(SETTING_NUMBER_OF_SHARDS, 3) - .put(SETTING_NUMBER_OF_REPLICAS, 0).put(LifecycleSettings.LIFECYCLE_NAME, policy) - // this will disable the auto allocation of the index to the hot node - .putNull(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE) - .build(); - CreateIndexResponse res = client().admin().indices().prepareCreate(managedIndex).setSettings(settings).get(); - assertTrue(res.isAcknowledged()); - - // ILM has run - assertBusy(() -> { - ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest().indices(managedIndex); - ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, - explainRequest).get(); - - IndexLifecycleExplainResponse indexLifecycleExplainResponse = explainResponse.getIndexResponses().get(managedIndex); - assertThat(indexLifecycleExplainResponse.getPhase(), is("hot")); - assertThat(indexLifecycleExplainResponse.getStep(), is("complete")); - }); - - - // verify index is allocated only on the hot node - for (int shardId = 0; shardId < 3; shardId++) { - ClusterAllocationExplainRequest request = new ClusterAllocationExplainRequest().setIndex(managedIndex).setShard(shardId) - .setPrimary(true); - - ClusterAllocationExplainResponse allocationExplainResponse = client().execute(ClusterAllocationExplainAction.INSTANCE, request) - .get(); - Set roles = allocationExplainResponse.getExplanation().getCurrentNode().getRoles(); - assertThat(roles.size(), is(1)); - assertThat(roles.iterator().next().roleName(), is(DataTier.DATA_HOT)); - } - - } - } From 39b518e8e9804a79ce1346afbba6ceb72b868c71 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 10 Sep 2020 10:42:59 +0100 Subject: [PATCH 27/34] Remove rogue ; --- .../org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java index ef5f57a67eeae..d8777c021a683 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleRunnerTests.java @@ -92,8 +92,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; -; - public class IndexLifecycleRunnerTests extends ESTestCase { private static final NamedXContentRegistry REGISTRY; private ThreadPool threadPool; From f3a64d3163d1f4c0793a7667645ff71efa8459a6 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 10 Sep 2020 12:40:15 +0100 Subject: [PATCH 28/34] Fix HLRC tests --- .../main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java | 2 +- .../test/java/org/elasticsearch/client/IndexLifecycleIT.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java index f0c23e7a4e8b7..363f0021114f8 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java @@ -57,7 +57,7 @@ public class LifecyclePolicy implements ToXContentObject { throw new IllegalArgumentException("ordered " + PHASES_FIELD.getPreferredName() + " are not supported"); }, PHASES_FIELD); - ALLOWED_ACTIONS.put("hot", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, MigrateAction.NAME, RolloverAction.NAME)); + ALLOWED_ACTIONS.put("hot", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, RolloverAction.NAME)); ALLOWED_ACTIONS.put("warm", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, MigrateAction.NAME, AllocateAction.NAME, ForceMergeAction.NAME, ReadOnlyAction.NAME, ShrinkAction.NAME)); ALLOWED_ACTIONS.put("cold", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, MigrateAction.NAME, AllocateAction.NAME, diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java index d30195b5db44b..9844e503bb4b0 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java @@ -37,7 +37,6 @@ import org.elasticsearch.client.ilm.LifecycleManagementStatusResponse; import org.elasticsearch.client.ilm.LifecyclePolicy; import org.elasticsearch.client.ilm.LifecyclePolicyMetadata; -import org.elasticsearch.client.ilm.MigrateAction; import org.elasticsearch.client.ilm.OperationMode; import org.elasticsearch.client.ilm.Phase; import org.elasticsearch.client.ilm.PhaseExecutionInfo; @@ -147,7 +146,6 @@ public void testStartStopILM() throws Exception { public void testExplainLifecycle() throws Exception { Map lifecyclePhases = new HashMap<>(); Map hotActions = new HashMap<>(); - hotActions.put(MigrateAction.NAME, new MigrateAction(false)); hotActions.put(RolloverAction.NAME, new RolloverAction(null, TimeValue.timeValueHours(50 * 24), null)); Phase hotPhase = new Phase("hot", randomFrom(TimeValue.ZERO, null), hotActions); lifecyclePhases.put("hot", hotPhase); From 8f855c550baf9e244f7e65508e47bbf9de6833c6 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 10 Sep 2020 13:16:26 +0100 Subject: [PATCH 29/34] Fix ILMHistoryTests --- .../java/org/elasticsearch/xpack/ilm/ILMHistoryTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMHistoryTests.java b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMHistoryTests.java index 7510cd69d8d7e..617ce0cd02fc4 100644 --- a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMHistoryTests.java +++ b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMHistoryTests.java @@ -94,7 +94,7 @@ public void testIlmHistoryIndexCanRollover() throws Exception { assertBusy(() -> { try { SearchResponse search = client().prepareSearch(firstIndex).setQuery(matchQuery("index", firstIndex)).setSize(0).get(); - assertHitCount(search, 11); + assertHitCount(search, 9); } catch (Exception e) { //assertBusy will stop on first non-assertion error and it can happen when we try to search too early //instead of failing the whole test change it to assertion error and wait some more time From 9b90555a1689bd2fbc9c8fc992f8a106dfa87c02 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Wed, 16 Sep 2020 11:39:09 +0100 Subject: [PATCH 30/34] Remove frozen phase from HLRC --- .../main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java index 363f0021114f8..6b97f91b12104 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ilm/LifecyclePolicy.java @@ -62,8 +62,6 @@ public class LifecyclePolicy implements ToXContentObject { ForceMergeAction.NAME, ReadOnlyAction.NAME, ShrinkAction.NAME)); ALLOWED_ACTIONS.put("cold", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, MigrateAction.NAME, AllocateAction.NAME, FreezeAction.NAME)); - ALLOWED_ACTIONS.put("frozen", Sets.newHashSet(UnfollowAction.NAME, SetPriorityAction.NAME, MigrateAction.NAME, AllocateAction.NAME, - FreezeAction.NAME)); ALLOWED_ACTIONS.put("delete", Sets.newHashSet(DeleteAction.NAME)); } From eddd223b8ab42eece98285cf49006a506495137d Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Wed, 16 Sep 2020 12:31:54 +0100 Subject: [PATCH 31/34] Execute the allocate action before migrate --- .../xpack/core/ilm/TimeseriesLifecycleType.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java index 783761b82ea7c..fb7cb58bd3f7d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java @@ -43,11 +43,11 @@ public class TimeseriesLifecycleType implements LifecycleType { static final List ORDERED_VALID_HOT_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, RolloverAction.NAME, ForceMergeAction.NAME); static final List ORDERED_VALID_WARM_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, ReadOnlyAction.NAME, - MigrateAction.NAME, AllocateAction.NAME, ShrinkAction.NAME, ForceMergeAction.NAME); + AllocateAction.NAME, MigrateAction.NAME, ShrinkAction.NAME, ForceMergeAction.NAME); static final List ORDERED_VALID_COLD_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, - MigrateAction.NAME, AllocateAction.NAME, FreezeAction.NAME, SearchableSnapshotAction.NAME); + AllocateAction.NAME, MigrateAction.NAME, FreezeAction.NAME, SearchableSnapshotAction.NAME); static final List ORDERED_VALID_FROZEN_ACTIONS = Arrays.asList(SetPriorityAction.NAME, UnfollowAction.NAME, - MigrateAction.NAME, AllocateAction.NAME, FreezeAction.NAME, SearchableSnapshotAction.NAME); + AllocateAction.NAME, MigrateAction.NAME, FreezeAction.NAME, SearchableSnapshotAction.NAME); static final List ORDERED_VALID_DELETE_ACTIONS = Arrays.asList(WaitForSnapshotAction.NAME, DeleteAction.NAME); static final Set VALID_HOT_ACTIONS = Sets.newHashSet(ORDERED_VALID_HOT_ACTIONS); static final Set VALID_WARM_ACTIONS = Sets.newHashSet(ORDERED_VALID_WARM_ACTIONS); From d733d729f08dba4a9011a7fd9d050b6985b2cb06 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Wed, 16 Sep 2020 13:41:06 +0100 Subject: [PATCH 32/34] Adjust test to wait until ILM completes in the cold phase --- .../elasticsearch/xpack/ilm/DataTiersMigrationsTests.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java index bfd3aee67e73d..d7b825f17677b 100644 --- a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java +++ b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java @@ -132,17 +132,15 @@ public void testIndexDataTierMigration() throws Exception { logger.info("starting cold data node"); internalCluster().startNode(coldNode(Settings.EMPTY)); - logger.info("starting frozen data node"); - internalCluster().startNode(frozenNode(Settings.EMPTY)); - // wait for lifecycle to complete in the frozen phase after the index has been migrated to the frozen node + // wait for lifecycle to complete in the cold phase after the index has been migrated to the cold node assertBusy(() -> { ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest().indices(managedIndex); ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, explainRequest).get(); IndexLifecycleExplainResponse indexLifecycleExplainResponse = explainResponse.getIndexResponses().get(managedIndex); - assertThat(indexLifecycleExplainResponse.getPhase(), is("frozen")); + assertThat(indexLifecycleExplainResponse.getPhase(), is("cold")); assertThat(indexLifecycleExplainResponse.getStep(), is("complete")); }); } From 4da7379208c144a1f6e97dc4fd2511886d31d502 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Wed, 16 Sep 2020 14:18:48 +0100 Subject: [PATCH 33/34] Log and exception messages --- .../xpack/core/ilm/DataTierMigrationRoutedStep.java | 12 ++++++------ .../xpack/core/ilm/TimeseriesLifecycleType.java | 4 ++-- .../xpack/core/ilm/step/info/AllocationInfo.java | 8 ++++---- .../xpack/core/ilm/TimeseriesLifecycleTypeTests.java | 4 ++-- .../xpack/ilm/DataTiersMigrationsTests.java | 8 +------- 5 files changed, 15 insertions(+), 21 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java index 829ed32ecc45d..2dcb64df272ec 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java @@ -71,20 +71,20 @@ public Result isConditionMet(Index index, ClusterState clusterState) { logger.debug("[{}] lifecycle action for index [{}] executed but index no longer exists", getKey().getAction(), index.getName()); return new Result(false, null); } + String destinationTier = INDEX_ROUTING_INCLUDE_SETTING.get(idxMeta.getSettings()); if (ActiveShardCount.ALL.enoughShardsActive(clusterState, index.getName()) == false) { - logger.debug("[{}] lifecycle action for index [{}] cannot make progress because not all shards are active", - getKey().getAction(), index.getName()); + logger.debug("[{}] migration of index [{}] to the [{}] tier cannot progress, as not all shards are active", + getKey().getAction(), index.getName(), destinationTier); return new Result(false, AllocationInfo.waitingForActiveShardsAllocationInfo(idxMeta.getNumberOfReplicas())); } int allocationPendingAllShards = getPendingAllocations(index, ALLOCATION_DECIDERS, clusterState); if (allocationPendingAllShards > 0) { - String tier = INDEX_ROUTING_INCLUDE_SETTING.get(idxMeta.getSettings()); boolean targetTierNodeFound = false; for (DiscoveryNode node : clusterState.nodes()) { for (DiscoveryNodeRole role : node.getRoles()) { - if (role.roleName().equals(DATA_ROLE.roleName()) || role.roleName().equals(tier)) { + if (role.roleName().equals(DATA_ROLE.roleName()) || role.roleName().equals(destinationTier)) { targetTierNodeFound = true; break; } @@ -92,11 +92,11 @@ public Result isConditionMet(Index index, ClusterState clusterState) { } String statusMessage = String.format(Locale.ROOT, "%s lifecycle action [%s] waiting for [%s] shards to be moved to the [%s] " + "tier" + (targetTierNodeFound ? "" : " but there are currently no [%s] nodes in the cluster"), - index, getKey().getAction(), allocationPendingAllShards, tier, tier); + index, getKey().getAction(), allocationPendingAllShards, destinationTier, destinationTier); logger.debug(statusMessage); return new Result(false, new AllocationInfo(idxMeta.getNumberOfReplicas(), allocationPendingAllShards, true, statusMessage)); } else { - logger.debug("{} lifecycle action for [{}] complete", index, getKey().getAction()); + logger.debug("[{}] migration of index [{}] to tier [{}] complete", getKey().getAction(), index, destinationTier); return new Result(true, null); } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java index fb7cb58bd3f7d..f496de49cd549 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java @@ -262,8 +262,8 @@ public void validate(Collection phases) { .collect(Collectors.joining(",")); if (Strings.hasText(phasesWithConflictingMigrationActions)) { throw new IllegalArgumentException("phases [" + phasesWithConflictingMigrationActions + "] specify an enabled " + - MigrateAction.NAME + " action and the " + AllocateAction.NAME + " action. specify only one data migration action in these" + - " phases"); + MigrateAction.NAME + " action and an " + AllocateAction.NAME + " action with allocation rules. specify only a single " + + "data migration in each phase"); } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java index 7b5b2691f7e39..4ee790658f579 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/step/info/AllocationInfo.java @@ -52,8 +52,8 @@ public AllocationInfo(long numberOfReplicas, long numberShardsLeftToAllocate, bo * Builds the AllocationInfo representing a cluster state with a routing table that does not have enough shards active for a * particular index. */ - public static AllocationInfo waitingForActiveShardsAllocationInfo(long actualReplicas) { - return new AllocationInfo(actualReplicas, -1, false, + public static AllocationInfo waitingForActiveShardsAllocationInfo(long numReplicas) { + return new AllocationInfo(numReplicas, -1, false, "Waiting for all shard copies to be active"); } @@ -61,8 +61,8 @@ public static AllocationInfo waitingForActiveShardsAllocationInfo(long actualRep * Builds the AllocationInfo representing a cluster state with a routing table that has all the shards active for a particular index * but there are still {@link #numberShardsLeftToAllocate} left to be allocated. */ - public static AllocationInfo allShardsActiveAllocationInfo(long actualReplicas, long numberShardsLeftToAllocate) { - return new AllocationInfo(actualReplicas, numberShardsLeftToAllocate, true, "Waiting for [" + numberShardsLeftToAllocate + + public static AllocationInfo allShardsActiveAllocationInfo(long numReplicas, long numberShardsLeftToAllocate) { + return new AllocationInfo(numReplicas, numberShardsLeftToAllocate, true, "Waiting for [" + numberShardsLeftToAllocate + "] shards to be allocated to nodes matching the given filters"); } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java index ae1254981ce30..b1b3d81c94256 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java @@ -202,8 +202,8 @@ public void testValidateConflictingDataMigrationConfigurations() { Exception validationException = expectThrows(IllegalArgumentException.class, () -> TimeseriesLifecycleType.INSTANCE.validate(phases)); - assertThat(validationException.getMessage(), equalTo("phases [warm,cold] specify an enabled migrate action and the allocate" + - " action. specify only one data migration action in these phases")); + assertThat(validationException.getMessage(), equalTo("phases [warm,cold] specify an enabled migrate action and an allocate " + + "action with allocation rules. specify only a single data migration in each phase")); // disabling the migrate action makes the phases definition valid as only the allocate action will perform data migration actions.put(TEST_MIGRATE_ACTION.getWriteableName(), new MigrateAction(false)); diff --git a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java index d7b825f17677b..7b8f9353cd654 100644 --- a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java +++ b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/DataTiersMigrationsTests.java @@ -84,10 +84,6 @@ public static Settings coldNode(final Settings settings) { return onlyRole(settings, DataTier.DATA_COLD_NODE_ROLE); } - public static Settings frozenNode(final Settings settings) { - return onlyRole(settings, DataTier.DATA_FROZEN_NODE_ROLE); - } - public void testIndexDataTierMigration() throws Exception { internalCluster().startMasterOnlyNodes(1, Settings.EMPTY); logger.info("starting hot data node"); @@ -96,9 +92,7 @@ public void testIndexDataTierMigration() throws Exception { Phase hotPhase = new Phase("hot", TimeValue.ZERO, Collections.emptyMap()); Phase warmPhase = new Phase("warm", TimeValue.ZERO, Collections.emptyMap()); Phase coldPhase = new Phase("cold", TimeValue.ZERO, Collections.emptyMap()); - Phase frozenPhase = new Phase("frozen", TimeValue.ZERO, Collections.emptyMap()); - LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, Map.of("hot", hotPhase, "warm", warmPhase, "cold", coldPhase, - "frozen", frozenPhase)); + LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, Map.of("hot", hotPhase, "warm", warmPhase, "cold", coldPhase)); PutLifecycleAction.Request putLifecycleRequest = new PutLifecycleAction.Request(lifecyclePolicy); PutLifecycleAction.Response putLifecycleResponse = client().execute(PutLifecycleAction.INSTANCE, putLifecycleRequest).get(); assertAcked(putLifecycleResponse); From b83771856c3d1e8915539e1f196a5cfd8ef018d1 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 17 Sep 2020 09:55:56 +0100 Subject: [PATCH 34/34] Drop equals and hashcode (in favour of the ones in Step) --- .../core/ilm/DataTierMigrationRoutedStep.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java index 2dcb64df272ec..1e3b284ec1072 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/DataTierMigrationRoutedStep.java @@ -100,20 +100,4 @@ public Result isConditionMet(Index index, ClusterState clusterState) { return new Result(true, null); } } - - @Override - public int hashCode() { - return 711; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - return super.equals(obj); - } }