Skip to content

Commit

Permalink
Avoid unnecessary LifecycleExecutionState recalculation (#81558)
Browse files Browse the repository at this point in the history
  • Loading branch information
joegallo committed Jan 14, 2022
1 parent 0a4a6f8 commit c6e5c55
Show file tree
Hide file tree
Showing 71 changed files with 370 additions and 317 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,8 @@ public static APIBlock readFrom(StreamInput input) throws IOException {

private final int shardsPerNodeLimit;

private final LifecycleExecutionState lifecycleExecutionState;

private IndexMetadata(
final Index index,
final long version,
Expand Down Expand Up @@ -533,9 +535,9 @@ private IndexMetadata(
final long creationDate,
final boolean ignoreDiskWatermarks,
@Nullable final List<String> tierPreference,
final int shardsPerNodeLimit
final int shardsPerNodeLimit,
final LifecycleExecutionState lifecycleExecutionState
) {

this.index = index;
this.version = version;
assert mappingVersion >= 0 : mappingVersion;
Expand Down Expand Up @@ -575,6 +577,7 @@ private IndexMetadata(
this.ignoreDiskWatermarks = ignoreDiskWatermarks;
this.tierPreference = tierPreference;
this.shardsPerNodeLimit = shardsPerNodeLimit;
this.lifecycleExecutionState = lifecycleExecutionState;
assert numberOfShards * routingFactor == routingNumShards : routingNumShards + " must be a multiple of " + numberOfShards;
}

Expand Down Expand Up @@ -614,7 +617,8 @@ IndexMetadata withMappingMetadata(MappingMetadata mapping) {
this.creationDate,
this.ignoreDiskWatermarks,
this.tierPreference,
this.shardsPerNodeLimit
this.shardsPerNodeLimit,
this.lifecycleExecutionState
);
}

Expand Down Expand Up @@ -738,6 +742,10 @@ public List<String> getTierPreference() {
return tierPreference;
}

public LifecycleExecutionState getLifecycleExecutionState() {
return lifecycleExecutionState;
}

/**
* Return the concrete mapping for this index or {@code null} if this index has no mappings at all.
*/
Expand Down Expand Up @@ -1203,6 +1211,7 @@ public static class Builder {
private Integer routingNumShards;
private boolean isSystem;
private IndexLongFieldRange timestampRange = IndexLongFieldRange.NO_SHARDS;
private LifecycleExecutionState lifecycleExecutionState = LifecycleExecutionState.EMPTY_STATE;

public Builder(String index) {
this.index = index;
Expand Down Expand Up @@ -1230,6 +1239,7 @@ public Builder(IndexMetadata indexMetadata) {
this.rolloverInfos = ImmutableOpenMap.builder(indexMetadata.rolloverInfos);
this.isSystem = indexMetadata.isSystem;
this.timestampRange = indexMetadata.timestampRange;
this.lifecycleExecutionState = indexMetadata.lifecycleExecutionState;
}

public Builder index(String index) {
Expand Down Expand Up @@ -1561,6 +1571,14 @@ public IndexMetadata build() {
tierPreference = null;
}

ImmutableOpenMap<String, DiffableStringMap> newCustomMetadata = customMetadata.build();
Map<String, String> custom = newCustomMetadata.get(LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY);
if (custom != null && custom.isEmpty() == false) {
lifecycleExecutionState = LifecycleExecutionState.fromCustomMetadata(custom);
} else {
lifecycleExecutionState = LifecycleExecutionState.EMPTY_STATE;
}

return new IndexMetadata(
new Index(index, uuid),
version,
Expand All @@ -1574,7 +1592,7 @@ public IndexMetadata build() {
settings,
mapping,
aliases.build(),
customMetadata.build(),
newCustomMetadata,
filledInSyncAllocationIds.build(),
requireFilters,
initialRecoveryFilters,
Expand All @@ -1593,7 +1611,8 @@ public IndexMetadata build() {
settings.getAsLong(SETTING_CREATION_DATE, -1L),
DiskThresholdDecider.SETTING_IGNORE_DISK_WATERMARKS.get(settings),
tierPreference,
ShardsLimitAllocationDecider.INDEX_TOTAL_SHARDS_PER_NODE_SETTING.get(settings)
ShardsLimitAllocationDecider.INDEX_TOTAL_SHARDS_PER_NODE_SETTING.get(settings),
lifecycleExecutionState
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.xpack.core.ilm;
package org.elasticsearch.cluster.metadata;

import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.Strings;
import org.elasticsearch.core.Nullable;

import java.util.Collections;
import java.util.HashMap;
Expand Down Expand Up @@ -101,59 +99,6 @@ private LifecycleExecutionState(
this.rollupIndexName = rollupIndexName;
}

/**
* Retrieves the execution state from an {@link IndexMetadata} based on the
* custom metadata.
* @param indexMetadata The metadata of the index to retrieve the execution
* state from.
* @return The execution state of that index.
*/
public static LifecycleExecutionState fromIndexMetadata(IndexMetadata indexMetadata) {
Map<String, String> customData = indexMetadata.getCustomData(ILM_CUSTOM_METADATA_KEY);
if (customData != null && customData.isEmpty() == false) {
return fromCustomMetadata(customData);
} else {
return EMPTY_STATE;
}
}

/**
* Return true if this index is in the frozen phase, false if not controlled by ILM or not in frozen.
* @param indexMetadata the metadata of the index to retrieve phase from.
* @return true if frozen phase, false otherwise.
*/
public static boolean isFrozenPhase(IndexMetadata indexMetadata) {
Map<String, String> customData = indexMetadata.getCustomData(ILM_CUSTOM_METADATA_KEY);
// deliberately do not parse out the entire `LifeCycleExecutionState` to avoid the extra work involved since this method is
// used heavily by autoscaling.
return customData != null && TimeseriesLifecycleType.FROZEN_PHASE.equals(customData.get(PHASE));
}

/**
* Retrieves the current {@link Step.StepKey} from the lifecycle state. Note that
* it is illegal for the step to be set with the phase and/or action unset,
* or for the step to be unset with the phase and/or action set. All three
* settings must be either present or missing.
*
* @param lifecycleState the index custom data to extract the {@link Step.StepKey} from.
*/
@Nullable
public static Step.StepKey getCurrentStepKey(LifecycleExecutionState lifecycleState) {
Objects.requireNonNull(lifecycleState, "cannot determine current step key as lifecycle state is null");
String currentPhase = lifecycleState.getPhase();
String currentAction = lifecycleState.getAction();
String currentStep = lifecycleState.getStep();
if (Strings.isNullOrEmpty(currentStep)) {
assert Strings.isNullOrEmpty(currentPhase) : "Current phase is not empty: " + currentPhase;
assert Strings.isNullOrEmpty(currentAction) : "Current action is not empty: " + currentAction;
return null;
} else {
assert Strings.isNullOrEmpty(currentPhase) == false;
assert Strings.isNullOrEmpty(currentAction) == false;
return new Step.StepKey(currentPhase, currentAction, currentStep);
}
}

public static Builder builder() {
return new Builder();
}
Expand All @@ -178,7 +123,7 @@ public static Builder builder(LifecycleExecutionState state) {
.setStepTime(state.stepTime);
}

static LifecycleExecutionState fromCustomMetadata(Map<String, String> customData) {
public static LifecycleExecutionState fromCustomMetadata(Map<String, String> customData) {
Builder builder = builder();
String phase = customData.get(PHASE);
if (phase != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import org.elasticsearch.xpack.autoscaling.capacity.AutoscalingDeciderContext;
import org.elasticsearch.xpack.autoscaling.capacity.AutoscalingDeciderResult;
import org.elasticsearch.xpack.autoscaling.capacity.AutoscalingDeciderService;
import org.elasticsearch.xpack.core.ilm.LifecycleExecutionState;

import java.io.IOException;
import java.util.Collections;
Expand Down Expand Up @@ -63,7 +62,7 @@ public AutoscalingDeciderResult scale(Settings configuration, AutoscalingDecider
}

boolean needsTier(IndexMetadata idxMeta) {
return LifecycleExecutionState.isFrozenPhase(idxMeta);
return isFrozenPhase(idxMeta);
}

@Override
Expand Down Expand Up @@ -128,4 +127,16 @@ public int hashCode() {
}
}

// this is only here to support isFrozenPhase, TimeseriesLifecycleType.FROZEN_PHASE is the canonical source for this
static String FROZEN_PHASE = "frozen"; // visible for testing

/**
* Return true if this index is in the frozen phase, false if not controlled by ILM or not in frozen.
* @param indexMetadata the metadata of the index to retrieve phase from.
* @return true if frozen phase, false otherwise.
*/
// visible for testing
static boolean isFrozenPhase(IndexMetadata indexMetadata) {
return FROZEN_PHASE.equals(indexMetadata.getLifecycleExecutionState().getPhase());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,22 @@
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.LifecycleExecutionState;
import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.xpack.autoscaling.AutoscalingTestCase;
import org.elasticsearch.xpack.autoscaling.capacity.AutoscalingCapacity;
import org.elasticsearch.xpack.autoscaling.capacity.AutoscalingDeciderContext;
import org.elasticsearch.xpack.autoscaling.capacity.AutoscalingDeciderResult;
import org.elasticsearch.xpack.core.ilm.LifecycleExecutionState;
import org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType;

import java.util.function.Consumer;

import static org.elasticsearch.cluster.metadata.LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY;
import static org.elasticsearch.xpack.autoscaling.existence.FrozenExistenceDeciderService.isFrozenPhase;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -81,4 +85,41 @@ private void assertZeroCapacity(AutoscalingDeciderResult result) {
assertThat(capacity.node(), is(nullValue()));
assertThat(result.reason().summary(), equalTo("indices []"));
}

public void testIsFrozenPhase() {
assertThat(TimeseriesLifecycleType.ORDERED_VALID_PHASES, hasItem(FrozenExistenceDeciderService.FROZEN_PHASE));

IndexMetadata meta0 = IndexMetadata.builder("index")
.settings(settings(Version.CURRENT))
.numberOfShards(randomIntBetween(1, 5))
.numberOfReplicas(randomIntBetween(0, 5))
.build();
assertFalse(isFrozenPhase(meta0));

LifecycleExecutionState.Builder hot = LifecycleExecutionState.builder()
.setPhase("hot")
.setAction(randomAlphaOfLengthBetween(5, 20))
.setAction(randomAlphaOfLengthBetween(5, 20));

IndexMetadata meta1 = IndexMetadata.builder("index")
.settings(settings(Version.CURRENT))
.numberOfShards(randomIntBetween(1, 5))
.numberOfReplicas(randomIntBetween(0, 5))
.putCustom(ILM_CUSTOM_METADATA_KEY, hot.build().asMap())
.build();
assertFalse(isFrozenPhase(meta1));

LifecycleExecutionState.Builder frozen = LifecycleExecutionState.builder()
.setPhase(FrozenExistenceDeciderService.FROZEN_PHASE)
.setAction(randomAlphaOfLengthBetween(5, 20))
.setAction(randomAlphaOfLengthBetween(5, 20));

IndexMetadata meta2 = IndexMetadata.builder("index")
.settings(settings(Version.CURRENT))
.numberOfShards(randomIntBetween(1, 5))
.numberOfReplicas(randomIntBetween(0, 5))
.putCustom(ILM_CUSTOM_METADATA_KEY, frozen.build().asMap())
.build();
assertTrue(isFrozenPhase(meta2));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@
import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.LifecycleExecutionState;
import org.elasticsearch.common.Strings;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.IndexNotFoundException;

import static org.elasticsearch.xpack.core.ilm.LifecycleExecutionState.fromIndexMetadata;

/**
* Deletes the index identified by the shrink index name stored in the lifecycle state of the managed index (if any was generated)
*/
Expand Down Expand Up @@ -58,7 +57,7 @@ void performDuringNoSnapshot(IndexMetadata indexMetadata, ClusterState currentCl
}
}

LifecycleExecutionState lifecycleState = fromIndexMetadata(indexMetadata);
LifecycleExecutionState lifecycleState = indexMetadata.getLifecycleExecutionState();
final String shrinkIndexName = lifecycleState.getShrinkIndexName();
// if the shrink index was not generated there is nothing to delete so we move on
if (Strings.hasText(shrinkIndexName) == false) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@
import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.LifecycleExecutionState;
import org.elasticsearch.common.Strings;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.repositories.RepositoryMissingException;
import org.elasticsearch.snapshots.SnapshotMissingException;

import static org.elasticsearch.xpack.core.ilm.LifecycleExecutionState.fromIndexMetadata;

/**
* Deletes the snapshot designated by the repository and snapshot name present in the lifecycle execution state.
*/
Expand All @@ -38,7 +37,7 @@ public boolean isRetryable() {
void performDuringNoSnapshot(IndexMetadata indexMetadata, ClusterState currentClusterState, ActionListener<Void> listener) {
final String indexName = indexMetadata.getIndex().getName();

LifecycleExecutionState lifecycleState = fromIndexMetadata(indexMetadata);
LifecycleExecutionState lifecycleState = indexMetadata.getLifecycleExecutionState();
final String repositoryName = lifecycleState.getSnapshotRepository();
// if the snapshot information is missing from the ILM execution state there is nothing to delete so we move on
if (Strings.hasText(repositoryName) == false) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.apache.logging.log4j.Logger;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.LifecycleExecutionState;
import org.elasticsearch.common.Strings;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.TimeValue;
Expand All @@ -22,8 +23,6 @@
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.elasticsearch.xpack.core.ilm.LifecycleExecutionState.fromIndexMetadata;

/**
* This step wraps an {@link ClusterStateWaitStep} in order to be able to manipulate what the next step will be, depending on the result of
* the wrapped {@link ClusterStateWaitStep}.
Expand Down Expand Up @@ -68,7 +67,7 @@ public Result isConditionMet(Index index, ClusterState clusterState) {
// checking the threshold after we execute the step to make sure we execute the wrapped step at least once (because time is a
// wonderful thing)
TimeValue retryThreshold = LifecycleSettings.LIFECYCLE_STEP_WAIT_TIME_THRESHOLD_SETTING.get(idxMeta.getSettings());
LifecycleExecutionState lifecycleState = fromIndexMetadata(idxMeta);
LifecycleExecutionState lifecycleState = idxMeta.getLifecycleExecutionState();
if (stepToExecute.isCompletable() == false) {
// we may not have passed the time threshold, but the step is not completable due to a different reason
thresholdPassed.set(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
import org.apache.logging.log4j.Logger;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.LifecycleExecutionState;
import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.index.Index;

import java.util.Objects;
import java.util.function.BiFunction;

import static org.elasticsearch.xpack.core.ilm.LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY;
import static org.elasticsearch.cluster.metadata.LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY;

/**
* Copies the execution state data from one index to another, typically after a
Expand Down Expand Up @@ -67,7 +68,7 @@ public ClusterState performAction(Index index, ClusterState clusterState) {
return clusterState;
}
// get target index
LifecycleExecutionState lifecycleState = LifecycleExecutionState.fromIndexMetadata(indexMetadata);
LifecycleExecutionState lifecycleState = indexMetadata.getLifecycleExecutionState();
String targetIndexName = targetIndexNameSupplier.apply(index.getName(), lifecycleState);
IndexMetadata targetIndexMetadata = clusterState.metadata().index(targetIndexName);

Expand Down

0 comments on commit c6e5c55

Please sign in to comment.