Skip to content

Commit 3ed4ff0

Browse files
committed
Merge pull request #17246 from s1monw/archive_persistent_settings
Archive cluster level settings if unknown or broken We already archive index level settings if we find an unknown or invalid/broken value for a setting on node startup. The same could potentially happen for persistent cluster level settings if we remove a setting or if we add validation to a setting that didn't exist in the past. To ensure that only valid settings are recovered into the cluster state we archive them (prefix them with `archive.` and log a warning. Tools that check the cluster settings can then warn users that they have broken settings in their clusterstate that got archived.
2 parents da96b6e + c0ef318 commit 3ed4ff0

9 files changed

Lines changed: 202 additions & 85 deletions

File tree

buildSrc/src/main/resources/checkstyle_suppressions.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,7 +1116,6 @@
11161116
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]fieldstats[/\\]FieldStatsIntegrationIT.java" checks="LineLength" />
11171117
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]fieldstats[/\\]FieldStatsTests.java" checks="LineLength" />
11181118
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]gateway[/\\]AsyncShardFetchTests.java" checks="LineLength" />
1119-
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]gateway[/\\]GatewayIndexStateIT.java" checks="LineLength" />
11201119
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]gateway[/\\]GatewayMetaStateTests.java" checks="LineLength" />
11211120
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]gateway[/\\]GatewayModuleTests.java" checks="LineLength" />
11221121
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]gateway[/\\]GatewayServiceTests.java" checks="LineLength" />
@@ -1137,7 +1136,6 @@
11371136
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]http[/\\]netty[/\\]NettyPipeliningEnabledIT.java" checks="LineLength" />
11381137
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]IndexModuleTests.java" checks="LineLength" />
11391138
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]IndexServiceTests.java" checks="LineLength" />
1140-
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]IndexSettingsTests.java" checks="LineLength" />
11411139
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]IndexWithShadowReplicasIT.java" checks="LineLength" />
11421140
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]IndexingSlowLogTests.java" checks="LineLength" />
11431141
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]MergePolicySettingsTests.java" checks="LineLength" />

core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeService.java

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,20 @@
2020

2121
import com.carrotsearch.hppc.cursors.ObjectCursor;
2222
import org.apache.lucene.analysis.Analyzer;
23-
import org.apache.lucene.misc.IndexMergeTool;
2423
import org.elasticsearch.Version;
2524
import org.elasticsearch.common.component.AbstractComponent;
2625
import org.elasticsearch.common.inject.Inject;
2726
import org.elasticsearch.common.settings.IndexScopedSettings;
28-
import org.elasticsearch.common.settings.Setting;
2927
import org.elasticsearch.common.settings.Settings;
3028
import org.elasticsearch.index.IndexSettings;
31-
import org.elasticsearch.index.MergePolicyConfig;
3229
import org.elasticsearch.index.analysis.AnalysisService;
3330
import org.elasticsearch.index.analysis.NamedAnalyzer;
3431
import org.elasticsearch.index.mapper.MapperService;
3532
import org.elasticsearch.index.similarity.SimilarityService;
3633
import org.elasticsearch.indices.mapper.MapperRegistry;
3734

3835
import java.util.Collections;
39-
import java.util.Map;
40-
import java.util.Set;
4136

42-
import static java.util.Collections.unmodifiableSet;
4337
import static org.elasticsearch.common.util.set.Sets.newHashSet;
4438

4539
/**
@@ -53,13 +47,13 @@
5347
public class MetaDataIndexUpgradeService extends AbstractComponent {
5448

5549
private final MapperRegistry mapperRegistry;
56-
private final IndexScopedSettings indexScopedSettigns;
50+
private final IndexScopedSettings indexScopedSettings;
5751

5852
@Inject
5953
public MetaDataIndexUpgradeService(Settings settings, MapperRegistry mapperRegistry, IndexScopedSettings indexScopedSettings) {
6054
super(settings);
6155
this.mapperRegistry = mapperRegistry;
62-
this.indexScopedSettigns = indexScopedSettings;
56+
this.indexScopedSettings = indexScopedSettings;
6357
}
6458

6559
/**
@@ -182,39 +176,13 @@ public void close() {
182176
}
183177
}
184178

185-
private static final String ARCHIVED_SETTINGS_PREFIX = "archived.";
186-
187179
IndexMetaData archiveBrokenIndexSettings(IndexMetaData indexMetaData) {
188-
Settings settings = indexMetaData.getSettings();
189-
Settings.Builder builder = Settings.builder();
190-
boolean changed = false;
191-
for (Map.Entry<String, String> entry : settings.getAsMap().entrySet()) {
192-
try {
193-
Setting<?> setting = indexScopedSettigns.get(entry.getKey());
194-
if (setting != null) {
195-
setting.get(settings);
196-
builder.put(entry.getKey(), entry.getValue());
197-
} else {
198-
if (indexScopedSettigns.isPrivateSetting(entry.getKey()) || entry.getKey().startsWith(ARCHIVED_SETTINGS_PREFIX)) {
199-
builder.put(entry.getKey(), entry.getValue());
200-
} else {
201-
changed = true;
202-
logger.warn("[{}] found unknown index setting: {} value: {} - archiving", indexMetaData.getIndex(), entry.getKey(), entry.getValue());
203-
// we put them back in here such that tools can check from the outside if there are any indices with broken settings. The setting can remain there
204-
// but we want users to be aware that some of their setting are broken and they can research why and what they need to do to replace them.
205-
builder.put(ARCHIVED_SETTINGS_PREFIX + entry.getKey(), entry.getValue());
206-
}
207-
}
208-
} catch (IllegalArgumentException ex) {
209-
changed = true;
210-
logger.warn("[{}] found invalid index setting: {} value: {} - archiving",ex, indexMetaData.getIndex(), entry.getKey(), entry.getValue());
211-
// we put them back in here such that tools can check from the outside if there are any indices with broken settings. The setting can remain there
212-
// but we want users to be aware that some of their setting sare broken and they can research why and what they need to do to replace them.
213-
builder.put(ARCHIVED_SETTINGS_PREFIX + entry.getKey(), entry.getValue());
214-
}
180+
final Settings settings = indexMetaData.getSettings();
181+
final Settings upgrade = indexScopedSettings.archiveUnknownOrBrokenSettings(settings);
182+
if (upgrade != settings) {
183+
return IndexMetaData.builder(indexMetaData).settings(upgrade).build();
184+
} else {
185+
return indexMetaData;
215186
}
216-
217-
return changed ? IndexMetaData.builder(indexMetaData).settings(builder.build()).build() : indexMetaData;
218187
}
219-
220188
}

core/src/main/java/org/elasticsearch/cluster/service/ClusterService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,4 +1000,8 @@ public void onTimeout() {
10001000
}
10011001
}
10021002
}
1003+
1004+
public ClusterSettings getClusterSettings() {
1005+
return clusterSettings;
1006+
}
10031007
}

core/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
* This service offers transactional application of updates settings.
4949
*/
5050
public abstract class AbstractScopedSettings extends AbstractComponent {
51+
public static final String ARCHIVED_SETTINGS_PREFIX = "archived.";
5152
private Settings lastSettingsApplied = Settings.EMPTY;
5253
private final List<SettingUpdater<?>> settingUpdaters = new CopyOnWriteArrayList<>();
5354
private final Map<String, Setting<?>> complexMatchers;
@@ -478,4 +479,53 @@ private static Setting<?> findOverlappingSetting(Setting<?> newSetting, Map<Stri
478479
}
479480
return null;
480481
}
482+
483+
/**
484+
* Archives broken or unknown settings. Any setting that is not recognized or fails
485+
* validation will be archived. This means the setting is prefixed with {@value ARCHIVED_SETTINGS_PREFIX}
486+
* and remains in the settings object. This can be used to detect broken settings via APIs.
487+
*/
488+
public Settings archiveUnknownOrBrokenSettings(Settings settings) {
489+
Settings.Builder builder = Settings.builder();
490+
boolean changed = false;
491+
for (Map.Entry<String, String> entry : settings.getAsMap().entrySet()) {
492+
try {
493+
Setting<?> setting = get(entry.getKey());
494+
if (setting != null) {
495+
setting.get(settings);
496+
builder.put(entry.getKey(), entry.getValue());
497+
} else {
498+
if (entry.getKey().startsWith(ARCHIVED_SETTINGS_PREFIX) || isPrivateSetting(entry.getKey())) {
499+
builder.put(entry.getKey(), entry.getValue());
500+
} else {
501+
changed = true;
502+
logger.warn("found unknown setting: {} value: {} - archiving", entry.getKey(), entry.getValue());
503+
// we put them back in here such that tools can check from the outside if there are any indices with broken settings. The setting can remain there
504+
// but we want users to be aware that some of their setting are broken and they can research why and what they need to do to replace them.
505+
builder.put(ARCHIVED_SETTINGS_PREFIX + entry.getKey(), entry.getValue());
506+
}
507+
}
508+
} catch (IllegalArgumentException ex) {
509+
changed = true;
510+
logger.warn("found invalid setting: {} value: {} - archiving",ex , entry.getKey(), entry.getValue());
511+
// we put them back in here such that tools can check from the outside if there are any indices with broken settings. The setting can remain there
512+
// but we want users to be aware that some of their setting sare broken and they can research why and what they need to do to replace them.
513+
builder.put(ARCHIVED_SETTINGS_PREFIX + entry.getKey(), entry.getValue());
514+
}
515+
}
516+
if (changed) {
517+
return builder.build();
518+
} else {
519+
return settings;
520+
}
521+
}
522+
523+
/**
524+
* Returns <code>true</code> iff the setting is a private setting ie. it should be treated as valid even though it has no internal
525+
* representation. Otherwise <code>false</code>
526+
*/
527+
// TODO this should be replaced by Setting.Property.HIDDEN or something like this.
528+
protected boolean isPrivateSetting(String key) {
529+
return false;
530+
}
481531
}

core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ protected void validateSettingKey(Setting setting) {
171171
super.validateSettingKey(setting);
172172
}
173173

174-
public boolean isPrivateSetting(String key) {
174+
@Override
175+
protected final boolean isPrivateSetting(String key) {
175176
switch (key) {
176177
case IndexMetaData.SETTING_CREATION_DATE:
177178
case IndexMetaData.SETTING_INDEX_UUID:

core/src/main/java/org/elasticsearch/common/util/IndexFolderUpgrader.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,7 @@
2424
import org.elasticsearch.common.logging.ESLogger;
2525
import org.elasticsearch.common.logging.Loggers;
2626
import org.elasticsearch.common.settings.Settings;
27-
import org.elasticsearch.common.xcontent.XContentBuilder;
28-
import org.elasticsearch.common.xcontent.XContentParser;
29-
import org.elasticsearch.common.xcontent.XContentType;
3027
import org.elasticsearch.env.NodeEnvironment;
31-
import org.elasticsearch.gateway.MetaDataStateFormat;
32-
import org.elasticsearch.gateway.MetaStateService;
3328
import org.elasticsearch.index.Index;
3429
import org.elasticsearch.index.IndexSettings;
3530

core/src/main/java/org/elasticsearch/gateway/Gateway.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package org.elasticsearch.gateway;
2121

2222
import com.carrotsearch.hppc.ObjectFloatHashMap;
23-
import com.carrotsearch.hppc.ObjectHashSet;
2423
import com.carrotsearch.hppc.cursors.ObjectCursor;
2524
import org.apache.lucene.util.IOUtils;
2625
import org.elasticsearch.action.FailedNodeException;
@@ -31,18 +30,16 @@
3130
import org.elasticsearch.cluster.metadata.MetaData;
3231
import org.elasticsearch.cluster.service.ClusterService;
3332
import org.elasticsearch.common.component.AbstractComponent;
33+
import org.elasticsearch.common.settings.ClusterSettings;
3434
import org.elasticsearch.common.settings.Settings;
3535
import org.elasticsearch.discovery.Discovery;
3636
import org.elasticsearch.env.NodeEnvironment;
3737
import org.elasticsearch.index.Index;
38-
import org.elasticsearch.index.IndexService;
3938
import org.elasticsearch.index.NodeServicesProvider;
4039
import org.elasticsearch.indices.IndicesService;
4140

42-
import java.io.IOException;
4341
import java.nio.file.Path;
4442
import java.util.Arrays;
45-
import java.util.Collections;
4643
import java.util.function.Supplier;
4744

4845
/**
@@ -155,6 +152,9 @@ public void performStateRecovery(final GatewayStateRecoveredListener listener) t
155152
}
156153
}
157154
}
155+
final ClusterSettings clusterSettings = clusterService.getClusterSettings();
156+
metaDataBuilder.persistentSettings(clusterSettings.archiveUnknownOrBrokenSettings(metaDataBuilder.persistentSettings()));
157+
metaDataBuilder.transientSettings(clusterSettings.archiveUnknownOrBrokenSettings(metaDataBuilder.transientSettings()));
158158
ClusterState.Builder builder = ClusterState.builder(clusterService.state().getClusterName());
159159
builder.metaData(metaDataBuilder);
160160
listener.onSuccess(builder.build());

0 commit comments

Comments
 (0)