|
| 1 | +/* |
| 2 | + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one |
| 3 | + * or more contributor license agreements. Licensed under the Elastic License |
| 4 | + * 2.0; you may not use this file except in compliance with the Elastic License |
| 5 | + * 2.0. |
| 6 | + */ |
| 7 | +package org.elasticsearch.xpack.core.ilm; |
| 8 | + |
| 9 | +import org.apache.logging.log4j.LogManager; |
| 10 | +import org.apache.logging.log4j.Logger; |
| 11 | +import org.elasticsearch.action.ActionListener; |
| 12 | +import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; |
| 13 | +import org.elasticsearch.action.admin.indices.shrink.ResizeType; |
| 14 | +import org.elasticsearch.client.internal.Client; |
| 15 | +import org.elasticsearch.cluster.ClusterStateObserver; |
| 16 | +import org.elasticsearch.cluster.ProjectState; |
| 17 | +import org.elasticsearch.cluster.metadata.IndexMetadata; |
| 18 | +import org.elasticsearch.cluster.metadata.LifecycleExecutionState; |
| 19 | +import org.elasticsearch.common.settings.Settings; |
| 20 | +import org.elasticsearch.common.unit.ByteSizeValue; |
| 21 | +import org.elasticsearch.core.Nullable; |
| 22 | +import org.elasticsearch.core.TimeValue; |
| 23 | + |
| 24 | +import java.util.Objects; |
| 25 | +import java.util.function.BiFunction; |
| 26 | +import java.util.function.Function; |
| 27 | + |
| 28 | +/** |
| 29 | + * Resizes an index with the specified settings, using the name that was generated in a previous {@link GenerateUniqueIndexNameStep} step. |
| 30 | + */ |
| 31 | +public class ResizeIndexStep extends AsyncActionStep { |
| 32 | + |
| 33 | + public static final String SHRINK = "shrink"; |
| 34 | + public static final String CLONE = "clone"; |
| 35 | + private static final Logger logger = LogManager.getLogger(ResizeIndexStep.class); |
| 36 | + |
| 37 | + private final ResizeType resizeType; |
| 38 | + private final BiFunction<String, LifecycleExecutionState, String> targetIndexNameSupplier; |
| 39 | + /** A supplier that takes the index metadata of the <i>original</i> index and returns settings for the target index . */ |
| 40 | + private final Function<IndexMetadata, Settings> targetIndexSettingsSupplier; |
| 41 | + @Nullable |
| 42 | + private final ByteSizeValue maxPrimaryShardSize; |
| 43 | + |
| 44 | + public ResizeIndexStep( |
| 45 | + StepKey key, |
| 46 | + StepKey nextStepKey, |
| 47 | + Client client, |
| 48 | + ResizeType resizeType, |
| 49 | + BiFunction<String, LifecycleExecutionState, String> targetIndexNameSupplier, |
| 50 | + Function<IndexMetadata, Settings> targetIndexSettingsSupplier, |
| 51 | + @Nullable ByteSizeValue maxPrimaryShardSize |
| 52 | + ) { |
| 53 | + super(key, nextStepKey, client); |
| 54 | + this.resizeType = resizeType; |
| 55 | + this.targetIndexNameSupplier = targetIndexNameSupplier; |
| 56 | + this.targetIndexSettingsSupplier = targetIndexSettingsSupplier; |
| 57 | + this.maxPrimaryShardSize = maxPrimaryShardSize; |
| 58 | + assert resizeType == ResizeType.SHRINK || maxPrimaryShardSize == null : "maxPrimaryShardSize can only be set for shrink operations"; |
| 59 | + } |
| 60 | + |
| 61 | + @Override |
| 62 | + public boolean isRetryable() { |
| 63 | + return true; |
| 64 | + } |
| 65 | + |
| 66 | + @Override |
| 67 | + public void performAction( |
| 68 | + IndexMetadata indexMetadata, |
| 69 | + ProjectState currentState, |
| 70 | + ClusterStateObserver observer, |
| 71 | + ActionListener<Void> listener |
| 72 | + ) { |
| 73 | + LifecycleExecutionState lifecycleState = indexMetadata.getLifecycleExecutionState(); |
| 74 | + if (lifecycleState.lifecycleDate() == null) { |
| 75 | + throw new IllegalStateException("source index [" + indexMetadata.getIndex().getName() + "] is missing lifecycle date"); |
| 76 | + } |
| 77 | + |
| 78 | + final String targetIndexName = targetIndexNameSupplier.apply(indexMetadata.getIndex().getName(), lifecycleState); |
| 79 | + if (currentState.metadata().index(targetIndexName) != null) { |
| 80 | + logger.warn( |
| 81 | + "skipping [{}] step for index [{}] as part of policy [{}] as the target index [{}] already exists", |
| 82 | + getKey().name(), |
| 83 | + indexMetadata.getIndex().getName(), |
| 84 | + indexMetadata.getLifecyclePolicyName(), |
| 85 | + targetIndexName |
| 86 | + ); |
| 87 | + listener.onResponse(null); |
| 88 | + return; |
| 89 | + } |
| 90 | + |
| 91 | + Settings relevantTargetSettings = Settings.builder() |
| 92 | + .put(targetIndexSettingsSupplier.apply(indexMetadata)) |
| 93 | + // We add the skip setting to prevent ILM from processing the shrunken index before the execution state has been copied - which |
| 94 | + // could happen if the shards of the shrunken index take a long time to allocate. |
| 95 | + .put(LifecycleSettings.LIFECYCLE_SKIP, true) |
| 96 | + .build(); |
| 97 | + |
| 98 | + ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, indexMetadata.getIndex().getName()).masterNodeTimeout( |
| 99 | + TimeValue.MAX_VALUE |
| 100 | + ); |
| 101 | + resizeRequest.setResizeType(resizeType); |
| 102 | + resizeRequest.getTargetIndexRequest().settings(relevantTargetSettings); |
| 103 | + if (resizeType == ResizeType.SHRINK) { |
| 104 | + resizeRequest.setMaxPrimaryShardSize(maxPrimaryShardSize); |
| 105 | + } |
| 106 | + |
| 107 | + // This request does not wait for (successful) completion of the resize operation - it fires-and-forgets. |
| 108 | + // It's up to a subsequent step to check for the existence of the target index and wait for it to be green. |
| 109 | + getClient(currentState.projectId()).admin() |
| 110 | + .indices() |
| 111 | + .resizeIndex(resizeRequest, listener.delegateFailureAndWrap((l, response) -> l.onResponse(null))); |
| 112 | + |
| 113 | + } |
| 114 | + |
| 115 | + public ResizeType getResizeType() { |
| 116 | + return resizeType; |
| 117 | + } |
| 118 | + |
| 119 | + public BiFunction<String, LifecycleExecutionState, String> getTargetIndexNameSupplier() { |
| 120 | + return targetIndexNameSupplier; |
| 121 | + } |
| 122 | + |
| 123 | + public Function<IndexMetadata, Settings> getTargetIndexSettingsSupplier() { |
| 124 | + return targetIndexSettingsSupplier; |
| 125 | + } |
| 126 | + |
| 127 | + public ByteSizeValue getMaxPrimaryShardSize() { |
| 128 | + return maxPrimaryShardSize; |
| 129 | + } |
| 130 | + |
| 131 | + @Override |
| 132 | + public int hashCode() { |
| 133 | + return Objects.hash(super.hashCode(), resizeType, maxPrimaryShardSize); |
| 134 | + } |
| 135 | + |
| 136 | + @Override |
| 137 | + public boolean equals(Object obj) { |
| 138 | + if (obj == null) { |
| 139 | + return false; |
| 140 | + } |
| 141 | + if (getClass() != obj.getClass()) { |
| 142 | + return false; |
| 143 | + } |
| 144 | + ResizeIndexStep other = (ResizeIndexStep) obj; |
| 145 | + return super.equals(obj) |
| 146 | + && Objects.equals(resizeType, other.resizeType) |
| 147 | + && Objects.equals(maxPrimaryShardSize, other.maxPrimaryShardSize); |
| 148 | + } |
| 149 | + |
| 150 | +} |
0 commit comments