Skip to content

Commit 253f1f4

Browse files
authored
[7.17] [ILM] More resilient when a policy is added to searchable snapshot (#102741) (#103070)
* Backport #102741
1 parent 522439b commit 253f1f4

File tree

7 files changed

+334
-38
lines changed

7 files changed

+334
-38
lines changed

docs/changelog/102741.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 102741
2+
summary: "[ILM] More resilient when a policy is added to searchable snapshot"
3+
area: ILM+SLM
4+
type: bug
5+
issues:
6+
- 101958

docs/reference/ilm/actions/ilm-delete.asciidoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ Defaults to `true`.
1616
This option is applicable when the <<ilm-searchable-snapshot,searchable
1717
snapshot>> action is used in any previous phase.
1818

19+
WARNING: If a policy with a searchable snapshot action is applied on an existing searchable snapshot index,
20+
the snapshot backing this index will NOT be deleted because it was not created by this policy. If you want
21+
to clean this snapshot, please delete it manually after the index is deleted using the <<delete-snapshot-api, delete snapshot API>>, you
22+
can find the repository and snapshot name using the <<indices-get-index, get index API>>.
23+
1924
[[ilm-delete-action-ex]]
2025
==== Example
2126

server/src/main/java/org/elasticsearch/snapshots/SearchableSnapshotsSettings.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public final class SearchableSnapshotsSettings {
2929
);
3030
public static final String SEARCHABLE_SNAPSHOTS_REPOSITORY_NAME_SETTING_KEY = "index.store.snapshot.repository_name";
3131
public static final String SEARCHABLE_SNAPSHOTS_REPOSITORY_UUID_SETTING_KEY = "index.store.snapshot.repository_uuid";
32+
public static final String SEARCHABLE_SNAPSHOTS_SNAPSHOT_NAME_SETTING_KEY = "index.store.snapshot.snapshot_name";
3233

3334
private SearchableSnapshotsSettings() {}
3435

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/MountSnapshotStep.java

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,30 @@ void performDuringNoSnapshot(IndexMetadata indexMetadata, ClusterState currentCl
7070
LifecycleExecutionState lifecycleState = fromIndexMetadata(indexMetadata);
7171

7272
String policyName = indexMetadata.getSettings().get(LifecycleSettings.LIFECYCLE_NAME);
73-
final String snapshotRepository = lifecycleState.getSnapshotRepository();
73+
String snapshotRepository = lifecycleState.getSnapshotRepository();
74+
SearchableSnapshotAction.SearchableSnapshotMetadata searchableSnapshotMetadata = SearchableSnapshotAction
75+
.extractSearchableSnapshotFromSettings(indexMetadata);
7476
if (Strings.hasText(snapshotRepository) == false) {
75-
listener.onFailure(
76-
new IllegalStateException(
77-
"snapshot repository is not present for policy [" + policyName + "] and index [" + indexName + "]"
78-
)
79-
);
80-
return;
77+
if (searchableSnapshotMetadata == null) {
78+
listener.onFailure(
79+
new IllegalStateException(
80+
"snapshot repository is not present for policy [" + policyName + "] and index [" + indexName + "]"
81+
)
82+
);
83+
return;
84+
} else {
85+
snapshotRepository = searchableSnapshotMetadata.repositoryName();
86+
}
8187
}
8288

83-
final String snapshotName = lifecycleState.getSnapshotName();
84-
if (Strings.hasText(snapshotName) == false) {
89+
String snapshotName = lifecycleState.getSnapshotName();
90+
if (Strings.hasText(snapshotName) == false && searchableSnapshotMetadata == null) {
8591
listener.onFailure(
8692
new IllegalStateException("snapshot name was not generated for policy [" + policyName + "] and index [" + indexName + "]")
8793
);
8894
return;
95+
} else if (searchableSnapshotMetadata != null) {
96+
snapshotName = searchableSnapshotMetadata.snapshotName();
8997
}
9098

9199
String mountedIndexName = restoredIndexPrefix + indexName;
@@ -102,16 +110,20 @@ void performDuringNoSnapshot(IndexMetadata indexMetadata, ClusterState currentCl
102110

103111
final String snapshotIndexName = lifecycleState.getSnapshotIndexName();
104112
if (snapshotIndexName == null) {
105-
// This index had its searchable snapshot created prior to a version where we captured
106-
// the original index name, so make our best guess at the name
107-
indexName = bestEffortIndexNameResolution(indexName);
108-
logger.debug(
109-
"index [{}] using policy [{}] does not have a stored snapshot index name, "
110-
+ "using our best effort guess of [{}] for the original snapshotted index name",
111-
indexMetadata.getIndex().getName(),
112-
policyName,
113-
indexName
114-
);
113+
if (searchableSnapshotMetadata == null) {
114+
// This index had its searchable snapshot created prior to a version where we captured
115+
// the original index name, so make our best guess at the name
116+
indexName = bestEffortIndexNameResolution(indexName);
117+
logger.debug(
118+
"index [{}] using policy [{}] does not have a stored snapshot index name, "
119+
+ "using our best effort guess of [{}] for the original snapshotted index name",
120+
indexMetadata.getIndex().getName(),
121+
policyName,
122+
indexName
123+
);
124+
} else {
125+
indexName = searchableSnapshotMetadata.sourceIndex();
126+
}
115127
} else {
116128
// Use the name of the snapshot as specified in the metadata, because the current index
117129
// name not might not reflect the name of the index actually in the snapshot

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/SearchableSnapshotAction.java

Lines changed: 78 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.common.Strings;
1717
import org.elasticsearch.common.io.stream.StreamInput;
1818
import org.elasticsearch.common.io.stream.StreamOutput;
19+
import org.elasticsearch.core.Nullable;
1920
import org.elasticsearch.license.LicenseUtils;
2021
import org.elasticsearch.license.XPackLicenseState;
2122
import org.elasticsearch.xcontent.ConstructingObjectParser;
@@ -31,6 +32,7 @@
3132
import java.util.Objects;
3233

3334
import static org.elasticsearch.snapshots.SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOTS_REPOSITORY_NAME_SETTING_KEY;
35+
import static org.elasticsearch.snapshots.SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOTS_SNAPSHOT_NAME_SETTING_KEY;
3436
import static org.elasticsearch.snapshots.SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_PARTIAL_SETTING_KEY;
3537
import static org.elasticsearch.xpack.core.searchablesnapshots.SearchableSnapshotsConstants.SEARCHABLE_SNAPSHOT_FEATURE;
3638

@@ -143,10 +145,12 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac
143145
IndexMetadata indexMetadata = clusterState.getMetadata().index(index);
144146
assert indexMetadata != null : "index " + index.getName() + " must exist in the cluster state";
145147
String policyName = LifecycleSettings.LIFECYCLE_NAME_SETTING.get(indexMetadata.getSettings());
146-
if (indexMetadata.getSettings().get(LifecycleSettings.SNAPSHOT_INDEX_NAME) != null) {
148+
SearchableSnapshotMetadata searchableSnapshotMetadata = extractSearchableSnapshotFromSettings(indexMetadata);
149+
if (searchableSnapshotMetadata != null) {
150+
// TODO: allow this behavior instead of returning false, in this case the index is already a searchable a snapshot
151+
// so the most graceful way of recovery might be to use this repo
147152
// The index is already a searchable snapshot, let's see if the repository matches
148-
String repo = indexMetadata.getSettings().get(SEARCHABLE_SNAPSHOTS_REPOSITORY_NAME_SETTING_KEY);
149-
if (this.snapshotRepository.equals(repo) == false) {
153+
if (this.snapshotRepository.equals(searchableSnapshotMetadata.repositoryName) == false) {
150154
// Okay, different repo, we need to go ahead with the searchable snapshot
151155
logger.debug(
152156
"[{}] action is configured for index [{}] in policy [{}] which is already mounted as a searchable "
@@ -155,15 +159,14 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac
155159
SearchableSnapshotAction.NAME,
156160
index.getName(),
157161
policyName,
158-
repo,
162+
searchableSnapshotMetadata.repositoryName,
159163
this.snapshotRepository
160164
);
161165
return false;
162166
}
163167

164168
// Check to the storage type to see if we need to convert between full <-> partial
165-
final boolean partial = indexMetadata.getSettings().getAsBoolean(SEARCHABLE_SNAPSHOT_PARTIAL_SETTING_KEY, false);
166-
MountSearchableSnapshotRequest.Storage existingType = partial
169+
MountSearchableSnapshotRequest.Storage existingType = searchableSnapshotMetadata.partial
167170
? MountSearchableSnapshotRequest.Storage.SHARED_CACHE
168171
: MountSearchableSnapshotRequest.Storage.FULL_COPY;
169172
MountSearchableSnapshotRequest.Storage type = getConcreteStorageType(preActionBranchingKey);
@@ -174,7 +177,7 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac
174177
SearchableSnapshotAction.NAME,
175178
index.getName(),
176179
policyName,
177-
repo,
180+
searchableSnapshotMetadata.repositoryName,
178181
type
179182
);
180183
return true;
@@ -211,7 +214,7 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac
211214
// When generating a snapshot, we either jump to the force merge step, or we skip the
212215
// forcemerge and go straight to steps for creating the snapshot
213216
StepKey keyForSnapshotGeneration = forceMergeIndex ? forceMergeStepKey : generateSnapshotNameKey;
214-
// Branch, deciding whether there is an existing searchable snapshot snapshot that can be used for mounting the index
217+
// Branch, deciding whether there is an existing searchable snapshot that can be used for mounting the index
215218
// (in which case, skip generating a new name and the snapshot cleanup), or if we need to generate a new snapshot
216219
BranchingStep skipGeneratingSnapshotStep = new BranchingStep(
217220
skipGeneratingSnapshotKey,
@@ -221,7 +224,8 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac
221224
IndexMetadata indexMetadata = clusterState.getMetadata().index(index);
222225
String policyName = LifecycleSettings.LIFECYCLE_NAME_SETTING.get(indexMetadata.getSettings());
223226
LifecycleExecutionState lifecycleExecutionState = LifecycleExecutionState.fromIndexMetadata(indexMetadata);
224-
if (lifecycleExecutionState.getSnapshotName() == null) {
227+
SearchableSnapshotMetadata searchableSnapshotMetadata = extractSearchableSnapshotFromSettings(indexMetadata);
228+
if (lifecycleExecutionState.getSnapshotName() == null && searchableSnapshotMetadata == null) {
225229
// No name exists, so it must be generated
226230
logger.trace(
227231
"no snapshot name for index [{}] in policy [{}] exists, so one will be generated",
@@ -230,8 +234,20 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac
230234
);
231235
return false;
232236
}
237+
String snapshotIndexName;
238+
String snapshotName;
239+
String repoName;
240+
if (lifecycleExecutionState.getSnapshotName() != null) {
241+
snapshotIndexName = lifecycleExecutionState.getSnapshotIndexName();
242+
snapshotName = lifecycleExecutionState.getSnapshotName();
243+
repoName = lifecycleExecutionState.getSnapshotRepository();
244+
} else {
245+
snapshotIndexName = searchableSnapshotMetadata.sourceIndex;
246+
snapshotName = searchableSnapshotMetadata.snapshotName;
247+
repoName = searchableSnapshotMetadata.repositoryName;
248+
}
233249

234-
if (this.snapshotRepository.equals(lifecycleExecutionState.getSnapshotRepository()) == false) {
250+
if (this.snapshotRepository.equals(repoName) == false) {
235251
// A different repository is being used
236252
// TODO: allow this behavior instead of throwing an exception
237253
throw new IllegalArgumentException("searchable snapshot indices may be converted only within the same repository");
@@ -240,12 +256,14 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac
240256
// We can skip the generate, initial cleanup, and snapshot taking for this index, as we already have a generated snapshot.
241257
// This will jump ahead directly to the "mount snapshot" step
242258
logger.debug(
243-
"an existing snapshot [{}] in repository [{}] (index name: [{}]) "
244-
+ "will be used for mounting [{}] as a searchable snapshot",
245-
lifecycleExecutionState.getSnapshotName(),
246-
lifecycleExecutionState.getSnapshotRepository(),
247-
lifecycleExecutionState.getSnapshotIndexName(),
248-
index.getName()
259+
"Policy [{}] will use an existing snapshot [{}] in repository [{}] (index name: [{}]) "
260+
+ "to mount [{}] as a searchable snapshot. This snapshot was found in the {}.",
261+
policyName,
262+
snapshotName,
263+
snapshotRepository,
264+
snapshotIndexName,
265+
index.getName(),
266+
lifecycleExecutionState.getSnapshotName() != null ? "lifecycle execution state" : "metadata of " + index.getName()
249267
);
250268
return true;
251269
}
@@ -401,11 +419,53 @@ public boolean equals(Object o) {
401419
return false;
402420
}
403421
SearchableSnapshotAction that = (SearchableSnapshotAction) o;
404-
return Objects.equals(snapshotRepository, that.snapshotRepository);
422+
return Objects.equals(snapshotRepository, that.snapshotRepository) && Objects.equals(forceMergeIndex, that.forceMergeIndex);
405423
}
406424

407425
@Override
408426
public int hashCode() {
409-
return Objects.hash(snapshotRepository);
427+
return Objects.hash(snapshotRepository, forceMergeIndex);
428+
}
429+
430+
@Nullable
431+
static SearchableSnapshotMetadata extractSearchableSnapshotFromSettings(IndexMetadata indexMetadata) {
432+
String indexName = indexMetadata.getSettings().get(LifecycleSettings.SNAPSHOT_INDEX_NAME);
433+
if (indexName == null) {
434+
return null;
435+
}
436+
String snapshotName = indexMetadata.getSettings().get(SEARCHABLE_SNAPSHOTS_SNAPSHOT_NAME_SETTING_KEY);
437+
String repo = indexMetadata.getSettings().get(SEARCHABLE_SNAPSHOTS_REPOSITORY_NAME_SETTING_KEY);
438+
final boolean partial = indexMetadata.getSettings().getAsBoolean(SEARCHABLE_SNAPSHOT_PARTIAL_SETTING_KEY, false);
439+
return new SearchableSnapshotMetadata(indexName, repo, snapshotName, partial);
410440
}
441+
442+
static class SearchableSnapshotMetadata {
443+
private final String sourceIndex;
444+
private final String repositoryName;
445+
private final String snapshotName;
446+
private final boolean partial;
447+
448+
SearchableSnapshotMetadata(String sourceIndex, String repositoryName, String snapshotName, boolean partial) {
449+
this.sourceIndex = sourceIndex;
450+
this.repositoryName = repositoryName;
451+
this.snapshotName = snapshotName;
452+
this.partial = partial;
453+
}
454+
455+
public String sourceIndex() {
456+
return sourceIndex;
457+
}
458+
459+
public String repositoryName() {
460+
return repositoryName;
461+
}
462+
463+
public String snapshotName() {
464+
return snapshotName;
465+
}
466+
467+
public boolean partial() {
468+
return partial;
469+
}
470+
};
411471
}

0 commit comments

Comments
 (0)