-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Allow opting out of force-merging on a cloned index in ILM's searchable snapshot action #137375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
63a3c65
91f74ae
9157717
1c9386d
ab5a0f5
0c1188d
66f7f6b
6f5081c
605795f
811dabe
3521a16
77a79ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| pr: 137375 | ||
| summary: Allow opting out of force-merging on a cloned index in ILM's searchable snapshot | ||
| action | ||
| area: ILM+SLM | ||
| type: enhancement | ||
| issues: [] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 9206000,9185006 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| esql_resolve_fields_response_used,9185005 | ||
| ilm_searchable_snapshot_opt_out_clone,9185006 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| text_similarity_rank_docs_explain_chunks,9205000 | ||
| ilm_searchable_snapshot_opt_out_clone,9206000 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
|
|
||
| import org.apache.logging.log4j.LogManager; | ||
| import org.apache.logging.log4j.Logger; | ||
| import org.elasticsearch.TransportVersion; | ||
| import org.elasticsearch.TransportVersions; | ||
| import org.elasticsearch.action.admin.indices.shrink.ResizeType; | ||
| import org.elasticsearch.client.internal.Client; | ||
|
|
@@ -58,6 +59,12 @@ public class SearchableSnapshotAction implements LifecycleAction { | |
| public static final ParseField FORCE_MERGE_INDEX = new ParseField("force_merge_index"); | ||
| public static final ParseField TOTAL_SHARDS_PER_NODE = new ParseField("total_shards_per_node"); | ||
| public static final ParseField REPLICATE_FOR = new ParseField("replicate_for"); | ||
| public static final ParseField FORCE_MERGE_ON_CLONE = new ParseField("force_merge_on_clone"); | ||
|
|
||
| private static final TransportVersion FORCE_MERGE_ON_CLONE_TRANSPORT_VERSION = TransportVersion.fromName( | ||
| "ilm_searchable_snapshot_opt_out_clone" | ||
| ); | ||
|
|
||
| public static final String CONDITIONAL_SKIP_ACTION_STEP = BranchingStep.NAME + "-check-prerequisites"; | ||
| public static final String CONDITIONAL_SKIP_GENERATE_AND_CLEAN = BranchingStep.NAME + "-check-existing-snapshot"; | ||
| public static final String CONDITIONAL_SKIP_CLONE_STEP = BranchingStep.NAME + "-skip-clone-check"; | ||
|
|
@@ -87,7 +94,7 @@ public class SearchableSnapshotAction implements LifecycleAction { | |
|
|
||
| private static final ConstructingObjectParser<SearchableSnapshotAction, Void> PARSER = new ConstructingObjectParser<>( | ||
| NAME, | ||
| a -> new SearchableSnapshotAction((String) a[0], a[1] == null || (boolean) a[1], (Integer) a[2], (TimeValue) a[3]) | ||
| a -> new SearchableSnapshotAction((String) a[0], a[1] == null || (boolean) a[1], (Integer) a[2], (TimeValue) a[3], (Boolean) a[4]) | ||
| ); | ||
|
|
||
| static { | ||
|
|
@@ -100,6 +107,7 @@ public class SearchableSnapshotAction implements LifecycleAction { | |
| REPLICATE_FOR, | ||
| ObjectParser.ValueType.STRING | ||
| ); | ||
| PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), FORCE_MERGE_ON_CLONE); | ||
| } | ||
|
|
||
| public static SearchableSnapshotAction parse(XContentParser parser) { | ||
|
|
@@ -112,12 +120,16 @@ public static SearchableSnapshotAction parse(XContentParser parser) { | |
| private final Integer totalShardsPerNode; | ||
| @Nullable | ||
| private final TimeValue replicateFor; | ||
| /** Opt-out field for forcing the force-merge step to run on the source index instead of a cloned version with 0 replicas. */ | ||
| @Nullable | ||
| private final Boolean forceMergeOnClone; | ||
|
|
||
| public SearchableSnapshotAction( | ||
| String snapshotRepository, | ||
| boolean forceMergeIndex, | ||
| @Nullable Integer totalShardsPerNode, | ||
| @Nullable TimeValue replicateFor | ||
| @Nullable TimeValue replicateFor, | ||
| @Nullable Boolean forceMergeOnClone | ||
| ) { | ||
| if (Strings.hasText(snapshotRepository) == false) { | ||
| throw new IllegalArgumentException("the snapshot repository must be specified"); | ||
|
|
@@ -136,21 +148,35 @@ public SearchableSnapshotAction( | |
| ); | ||
| } | ||
| this.replicateFor = replicateFor; | ||
|
|
||
| if (forceMergeIndex == false && forceMergeOnClone != null) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't ready through the whole PR yet so I might be missing something, but what if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I was a bit on the fence here. My reason for going with a Boolean was that it better represents what the user actually does (null, false, true). However, we don't currently make a lot of use of that null state, so I don't have super strong feelings on this. If you strongly prefer having a boolean, I'm ok with changing it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It makes sense with the toXcontent not returning it when null. But is this a bug here that you can't say There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah sorry, I forgot to comment on that. I intentionally went with that behavior, as I thought it was cleaner and less confusing if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems a bit odd to me, but it sort of makes sense. |
||
| throw new IllegalArgumentException( | ||
| Strings.format( | ||
| "[%s] is not allowed when [%s] is [false]", | ||
| FORCE_MERGE_ON_CLONE.getPreferredName(), | ||
| FORCE_MERGE_INDEX.getPreferredName() | ||
| ) | ||
| ); | ||
| } | ||
| this.forceMergeOnClone = forceMergeOnClone; | ||
| } | ||
|
|
||
| public SearchableSnapshotAction(String snapshotRepository, boolean forceMergeIndex) { | ||
| this(snapshotRepository, forceMergeIndex, null, null); | ||
| this(snapshotRepository, forceMergeIndex, null, null, null); | ||
| } | ||
|
|
||
| public SearchableSnapshotAction(String snapshotRepository) { | ||
| this(snapshotRepository, true, null, null); | ||
| this(snapshotRepository, true, null, null, null); | ||
| } | ||
|
|
||
| public SearchableSnapshotAction(StreamInput in) throws IOException { | ||
| this.snapshotRepository = in.readString(); | ||
| this.forceMergeIndex = in.readBoolean(); | ||
| this.totalShardsPerNode = in.getTransportVersion().onOrAfter(TransportVersions.V_8_16_0) ? in.readOptionalInt() : null; | ||
| this.replicateFor = in.getTransportVersion().supports(TransportVersions.V_8_18_0) ? in.readOptionalTimeValue() : null; | ||
| this.forceMergeOnClone = in.getTransportVersion().supports(FORCE_MERGE_ON_CLONE_TRANSPORT_VERSION) | ||
| ? in.readOptionalBoolean() | ||
| : null; | ||
| } | ||
|
|
||
| public boolean isForceMergeIndex() { | ||
|
|
@@ -171,6 +197,11 @@ public TimeValue getReplicateFor() { | |
| return replicateFor; | ||
| } | ||
|
|
||
| @Nullable | ||
| public Boolean isForceMergeOnClone() { | ||
| return forceMergeOnClone; | ||
| } | ||
|
|
||
| @Override | ||
| public List<Step> toSteps(Client client, String phase, StepKey nextStepKey) { | ||
| assert false; | ||
|
|
@@ -299,9 +330,12 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac | |
| Instant::now | ||
| ); | ||
|
|
||
| // We force-merge on the clone by default, but allow the user to opt-out of this behavior if there is any reason why they don't want | ||
| // to clone the index (e.g. if something is preventing the cloned index shards from being assigned). | ||
| StepKey keyForForceMerge = shouldForceMergeOnClone() ? conditionalSkipCloneKey : forceMergeStepKey; | ||
| // When generating a snapshot, we either jump to the force merge section, or we skip the | ||
| // forcemerge and go straight to steps for creating the snapshot | ||
| StepKey keyForSnapshotGeneration = forceMergeIndex ? conditionalSkipCloneKey : generateSnapshotNameKey; | ||
| StepKey keyForSnapshotGeneration = forceMergeIndex ? keyForForceMerge : generateSnapshotNameKey; | ||
| // Branch, deciding whether there is an existing searchable snapshot that can be used for mounting the index | ||
| // (in which case, skip generating a new name and the snapshot cleanup), or if we need to generate a new snapshot | ||
| BranchingStep skipGeneratingSnapshotStep = new BranchingStep( | ||
|
|
@@ -529,12 +563,14 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac | |
| steps.add(waitUntilTimeSeriesEndTimeStep); | ||
| steps.add(skipGeneratingSnapshotStep); | ||
| if (forceMergeIndex) { | ||
| steps.add(conditionalSkipCloneStep); | ||
| steps.add(readOnlyStep); | ||
| steps.add(cleanupClonedIndexStep); | ||
| steps.add(generateCloneIndexNameStep); | ||
| steps.add(cloneIndexStep); | ||
| steps.add(waitForClonedIndexGreenStep); | ||
| if (shouldForceMergeOnClone()) { | ||
| steps.add(conditionalSkipCloneStep); | ||
| steps.add(readOnlyStep); | ||
| steps.add(cleanupClonedIndexStep); | ||
| steps.add(generateCloneIndexNameStep); | ||
| steps.add(cloneIndexStep); | ||
| steps.add(waitForClonedIndexGreenStep); | ||
| } | ||
| steps.add(forceMergeStep); | ||
| steps.add(segmentCountStep); | ||
| } | ||
|
|
@@ -581,6 +617,15 @@ static MountSearchableSnapshotRequest.Storage getConcreteStorageType(StepKey cur | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether we should first clone the index and perform the force-merge on that cloned index (true) or force-merge on the | ||
| * original index (false). Defaults to true when {@link #forceMergeOnClone} is null/unspecified. Note that this value is ignored when | ||
| * {@link #forceMergeIndex} is false. | ||
| */ | ||
| private boolean shouldForceMergeOnClone() { | ||
| return forceMergeOnClone == null || forceMergeOnClone; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean isSafeAction() { | ||
| return true; | ||
|
|
@@ -601,6 +646,9 @@ public void writeTo(StreamOutput out) throws IOException { | |
| if (out.getTransportVersion().supports(TransportVersions.V_8_18_0)) { | ||
| out.writeOptionalTimeValue(replicateFor); | ||
| } | ||
| if (out.getTransportVersion().supports(FORCE_MERGE_ON_CLONE_TRANSPORT_VERSION)) { | ||
| out.writeOptionalBoolean(forceMergeOnClone); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -614,6 +662,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws | |
| if (replicateFor != null) { | ||
| builder.field(REPLICATE_FOR.getPreferredName(), replicateFor); | ||
| } | ||
| if (forceMergeOnClone != null) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this answers my question from above -- we don't want it in the xcontent if it has not been set explicitly. |
||
| builder.field(FORCE_MERGE_ON_CLONE.getPreferredName(), forceMergeOnClone); | ||
| } | ||
| builder.endObject(); | ||
| return builder; | ||
| } | ||
|
|
@@ -630,12 +681,13 @@ public boolean equals(Object o) { | |
| return Objects.equals(snapshotRepository, that.snapshotRepository) | ||
| && Objects.equals(forceMergeIndex, that.forceMergeIndex) | ||
| && Objects.equals(totalShardsPerNode, that.totalShardsPerNode) | ||
| && Objects.equals(replicateFor, that.replicateFor); | ||
| && Objects.equals(replicateFor, that.replicateFor) | ||
| && Objects.equals(forceMergeOnClone, that.forceMergeOnClone); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(snapshotRepository, forceMergeIndex, totalShardsPerNode, replicateFor); | ||
| return Objects.hash(snapshotRepository, forceMergeIndex, totalShardsPerNode, replicateFor, forceMergeOnClone); | ||
| } | ||
|
|
||
| @Nullable | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.