Skip to content

Commit c1cebd4

Browse files
committed
Add infrastructure to upgrade settings (#33536)
In some cases we want to deprecate a setting, and then automatically upgrade uses of that setting to a replacement setting. This commit adds infrastructure for this so that we can upgrade settings when recovering the cluster state, as well as when such settings are dynamically applied on cluster update settings requests. This commit only focuses on cluster settings, index settings can build on this infrastructure in a follow-up.
1 parent 0f53972 commit c1cebd4

File tree

20 files changed

+531
-27
lines changed

20 files changed

+531
-27
lines changed

plugins/repository-azure/src/test/java/org/elasticsearch/cloud/azure/storage/AzureStorageSettingsFilterTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
import java.io.IOException;
3636

37+
import static java.util.Collections.emptySet;
3738
import static org.hamcrest.Matchers.contains;
3839

3940
/**
@@ -52,7 +53,7 @@ public class AzureStorageSettingsFilterTests extends ESTestCase {
5253

5354
public void testSettingsFiltering() throws IOException {
5455
AzureRepositoryPlugin p = new AzureRepositoryPlugin(settings);
55-
SettingsModule module = new SettingsModule(Settings.EMPTY, p.getSettings(), p.getSettingsFilter());
56+
SettingsModule module = new SettingsModule(Settings.EMPTY, p.getSettings(), p.getSettingsFilter(), emptySet());
5657
SettingsFilter settingsFilter = ModuleTestCase.bindAndGetInstance(module, SettingsFilter.class);
5758

5859
// Test using direct filtering

server/src/main/java/org/elasticsearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,12 @@ public void onFailure(String source, Exception e) {
179179

180180
@Override
181181
public ClusterState execute(final ClusterState currentState) {
182-
ClusterState clusterState =
183-
updater.updateSettings(currentState, request.transientSettings(), request.persistentSettings(), logger);
182+
final ClusterState clusterState =
183+
updater.updateSettings(
184+
currentState,
185+
clusterSettings.upgradeSettings(request.transientSettings()),
186+
clusterSettings.upgradeSettings(request.persistentSettings()),
187+
logger);
184188
changed = clusterState != currentState;
185189
return clusterState;
186190
}

server/src/main/java/org/elasticsearch/client/transport/TransportClient.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
package org.elasticsearch.client.transport;
2121

22-
import org.elasticsearch.core.internal.io.IOUtils;
2322
import org.elasticsearch.action.Action;
2423
import org.elasticsearch.action.ActionListener;
2524
import org.elasticsearch.action.ActionModule;
@@ -46,6 +45,7 @@
4645
import org.elasticsearch.common.util.BigArrays;
4746
import org.elasticsearch.common.util.PageCacheRecycler;
4847
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
48+
import org.elasticsearch.core.internal.io.IOUtils;
4949
import org.elasticsearch.indices.IndicesModule;
5050
import org.elasticsearch.indices.breaker.CircuitBreakerService;
5151
import org.elasticsearch.node.InternalSettingsPreparer;
@@ -147,7 +147,8 @@ private static ClientTemplate buildTemplate(Settings providedSettings, Settings
147147
for (final ExecutorBuilder<?> builder : threadPool.builders()) {
148148
additionalSettings.addAll(builder.getRegisteredSettings());
149149
}
150-
SettingsModule settingsModule = new SettingsModule(settings, additionalSettings, additionalSettingsFilter);
150+
SettingsModule settingsModule =
151+
new SettingsModule(settings, additionalSettings, additionalSettingsFilter, Collections.emptySet());
151152

152153
SearchModule searchModule = new SearchModule(settings, true, pluginsService.filterPlugins(SearchPlugin.class));
153154
IndicesModule indicesModule = new IndicesModule(Collections.emptyList());

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

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.common.component.AbstractComponent;
2828
import org.elasticsearch.common.regex.Regex;
2929

30+
import java.util.AbstractMap;
3031
import java.util.ArrayList;
3132
import java.util.Collections;
3233
import java.util.HashMap;
@@ -38,6 +39,7 @@
3839
import java.util.concurrent.CopyOnWriteArrayList;
3940
import java.util.function.BiConsumer;
4041
import java.util.function.Consumer;
42+
import java.util.function.Function;
4143
import java.util.function.Predicate;
4244
import java.util.regex.Pattern;
4345
import java.util.stream.Collectors;
@@ -52,14 +54,29 @@ public abstract class AbstractScopedSettings extends AbstractComponent {
5254
private final List<SettingUpdater<?>> settingUpdaters = new CopyOnWriteArrayList<>();
5355
private final Map<String, Setting<?>> complexMatchers;
5456
private final Map<String, Setting<?>> keySettings;
57+
private final Map<Setting<?>, Function<Map.Entry<String, String>, Map.Entry<String, String>>> settingUpgraders;
5558
private final Setting.Property scope;
5659
private static final Pattern KEY_PATTERN = Pattern.compile("^(?:[-\\w]+[.])*[-\\w]+$");
5760
private static final Pattern GROUP_KEY_PATTERN = Pattern.compile("^(?:[-\\w]+[.])+$");
5861
private static final Pattern AFFIX_KEY_PATTERN = Pattern.compile("^(?:[-\\w]+[.])+[*](?:[.][-\\w]+)+$");
5962

60-
protected AbstractScopedSettings(Settings settings, Set<Setting<?>> settingsSet, Setting.Property scope) {
63+
protected AbstractScopedSettings(
64+
final Settings settings,
65+
final Set<Setting<?>> settingsSet,
66+
final Set<SettingUpgrader<?>> settingUpgraders,
67+
final Setting.Property scope) {
6168
super(settings);
6269
this.lastSettingsApplied = Settings.EMPTY;
70+
71+
this.settingUpgraders =
72+
Collections.unmodifiableMap(
73+
settingUpgraders
74+
.stream()
75+
.collect(
76+
Collectors.toMap(
77+
SettingUpgrader::getSetting,
78+
u -> e -> new AbstractMap.SimpleEntry<>(u.getKey(e.getKey()), u.getValue(e.getValue())))));
79+
6380
this.scope = scope;
6481
Map<String, Setting<?>> complexMatchers = new HashMap<>();
6582
Map<String, Setting<?>> keySettings = new HashMap<>();
@@ -97,6 +114,7 @@ protected AbstractScopedSettings(Settings nodeSettings, Settings scopeSettings,
97114
this.scope = other.scope;
98115
complexMatchers = other.complexMatchers;
99116
keySettings = other.keySettings;
117+
settingUpgraders = Collections.unmodifiableMap(new HashMap<>(other.settingUpgraders));
100118
settingUpdaters.addAll(other.settingUpdaters);
101119
}
102120

@@ -757,6 +775,32 @@ private static Setting<?> findOverlappingSetting(Setting<?> newSetting, Map<Stri
757775
return null;
758776
}
759777

778+
/**
779+
* Upgrade all settings eligible for upgrade in the specified settings instance.
780+
*
781+
* @param settings the settings instance that might contain settings to be upgraded
782+
* @return a new settings instance if any settings required upgrade, otherwise the same settings instance as specified
783+
*/
784+
public Settings upgradeSettings(final Settings settings) {
785+
final Settings.Builder builder = Settings.builder();
786+
boolean changed = false; // track if any settings were upgraded
787+
for (final String key : settings.keySet()) {
788+
final Setting<?> setting = getRaw(key);
789+
final Function<Map.Entry<String, String>, Map.Entry<String, String>> upgrader = settingUpgraders.get(setting);
790+
if (upgrader == null) {
791+
// the setting does not have an upgrader, copy the setting
792+
builder.copy(key, settings);
793+
} else {
794+
// the setting has an upgrader, so mark that we have changed a setting and apply the upgrade logic
795+
changed = true;
796+
final Map.Entry<String, String> upgrade = upgrader.apply(new Entry(key, settings));
797+
builder.put(upgrade.getKey(), upgrade.getValue());
798+
}
799+
}
800+
// we only return a new instance if there was an upgrade
801+
return changed ? builder.build() : settings;
802+
}
803+
760804
/**
761805
* Archives invalid or unknown settings. Any setting that is not recognized or fails validation
762806
* will be archived. This means the setting is prefixed with {@value ARCHIVED_SETTINGS_PREFIX}
@@ -847,4 +891,5 @@ public String setValue(String value) {
847891
public boolean isPrivateSetting(String key) {
848892
return false;
849893
}
894+
850895
}

server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,21 @@
101101
import java.util.Arrays;
102102
import java.util.Collections;
103103
import java.util.HashSet;
104+
import java.util.List;
104105
import java.util.Set;
105106
import java.util.function.Predicate;
106107

107108
/**
108109
* Encapsulates all valid cluster level settings.
109110
*/
110111
public final class ClusterSettings extends AbstractScopedSettings {
111-
public ClusterSettings(Settings nodeSettings, Set<Setting<?>> settingsSet) {
112-
super(nodeSettings, settingsSet, Property.NodeScope);
112+
public ClusterSettings(final Settings nodeSettings, final Set<Setting<?>> settingsSet) {
113+
this(nodeSettings, settingsSet, Collections.emptySet());
114+
}
115+
116+
public ClusterSettings(
117+
final Settings nodeSettings, final Set<Setting<?>> settingsSet, final Set<SettingUpgrader<?>> settingUpgraders) {
118+
super(nodeSettings, settingsSet, settingUpgraders, Property.NodeScope);
113119
addSettingsUpdater(new LoggingSettingUpdater(nodeSettings));
114120
}
115121

@@ -439,4 +445,7 @@ public void apply(Settings value, Settings current, Settings previous) {
439445
IndexGraveyard.SETTING_MAX_TOMBSTONES,
440446
EnableAssignmentDecider.CLUSTER_TASKS_ALLOCATION_ENABLE_SETTING
441447
)));
448+
449+
public static List<SettingUpgrader<?>> BUILT_IN_SETTING_UPGRADERS = Collections.emptyList();
450+
442451
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ public final class IndexScopedSettings extends AbstractScopedSettings {
175175
public static final IndexScopedSettings DEFAULT_SCOPED_SETTINGS = new IndexScopedSettings(Settings.EMPTY, BUILT_IN_INDEX_SETTINGS);
176176

177177
public IndexScopedSettings(Settings settings, Set<Setting<?>> settingsSet) {
178-
super(settings, settingsSet, Property.IndexScope);
178+
super(settings, settingsSet, Collections.emptySet(), Property.IndexScope);
179179
}
180180

181181
private IndexScopedSettings(Settings settings, IndexScopedSettings other, IndexMetaData metaData) {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.common.settings;
21+
22+
/**
23+
* Represents the logic to upgrade a setting.
24+
*
25+
* @param <T> the type of the underlying setting
26+
*/
27+
public interface SettingUpgrader<T> {
28+
29+
/**
30+
* The setting upgraded by this upgrader.
31+
*
32+
* @return the setting
33+
*/
34+
Setting<T> getSetting();
35+
36+
/**
37+
* The logic to upgrade the setting key, for example by mapping the old setting key to the new setting key.
38+
*
39+
* @param key the setting key to upgrade
40+
* @return the upgraded setting key
41+
*/
42+
String getKey(String key);
43+
44+
/**
45+
* The logic to upgrade the setting value.
46+
*
47+
* @param value the setting value to upgrade
48+
* @return the upgraded setting value
49+
*/
50+
default String getValue(final String value) {
51+
return value;
52+
}
53+
54+
}

server/src/main/java/org/elasticsearch/common/settings/SettingsModule.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,14 @@ public class SettingsModule implements Module {
5454
private final SettingsFilter settingsFilter;
5555

5656
public SettingsModule(Settings settings, Setting<?>... additionalSettings) {
57-
this(settings, Arrays.asList(additionalSettings), Collections.emptyList());
57+
this(settings, Arrays.asList(additionalSettings), Collections.emptyList(), Collections.emptySet());
5858
}
5959

60-
public SettingsModule(Settings settings, List<Setting<?>> additionalSettings, List<String> settingsFilter) {
60+
public SettingsModule(
61+
Settings settings,
62+
List<Setting<?>> additionalSettings,
63+
List<String> settingsFilter,
64+
Set<SettingUpgrader<?>> settingUpgraders) {
6165
logger = Loggers.getLogger(getClass(), settings);
6266
this.settings = settings;
6367
for (Setting<?> setting : ClusterSettings.BUILT_IN_CLUSTER_SETTINGS) {
@@ -70,12 +74,22 @@ public SettingsModule(Settings settings, List<Setting<?>> additionalSettings, Li
7074
for (Setting<?> setting : additionalSettings) {
7175
registerSetting(setting);
7276
}
73-
7477
for (String filter : settingsFilter) {
7578
registerSettingsFilter(filter);
7679
}
80+
final Set<SettingUpgrader<?>> clusterSettingUpgraders = new HashSet<>();
81+
for (final SettingUpgrader<?> settingUpgrader : ClusterSettings.BUILT_IN_SETTING_UPGRADERS) {
82+
assert settingUpgrader.getSetting().hasNodeScope() : settingUpgrader.getSetting().getKey();
83+
final boolean added = clusterSettingUpgraders.add(settingUpgrader);
84+
assert added : settingUpgrader.getSetting().getKey();
85+
}
86+
for (final SettingUpgrader<?> settingUpgrader : settingUpgraders) {
87+
assert settingUpgrader.getSetting().hasNodeScope() : settingUpgrader.getSetting().getKey();
88+
final boolean added = clusterSettingUpgraders.add(settingUpgrader);
89+
assert added : settingUpgrader.getSetting().getKey();
90+
}
7791
this.indexScopedSettings = new IndexScopedSettings(settings, new HashSet<>(this.indexSettings.values()));
78-
this.clusterSettings = new ClusterSettings(settings, new HashSet<>(this.nodeSettings.values()));
92+
this.clusterSettings = new ClusterSettings(settings, new HashSet<>(this.nodeSettings.values()), clusterSettingUpgraders);
7993
Settings indexSettings = settings.filter((s) -> (s.startsWith("index.") &&
8094
// special case - we want to get Did you mean indices.query.bool.max_clause_count
8195
// which means we need to by-pass this check for this setting
@@ -205,4 +219,5 @@ public ClusterSettings getClusterSettings() {
205219
public SettingsFilter getSettingsFilter() {
206220
return settingsFilter;
207221
}
222+
208223
}

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,20 +136,25 @@ public void performStateRecovery(final GatewayStateRecoveredListener listener) t
136136
}
137137
}
138138
}
139+
final ClusterState.Builder builder = upgradeAndArchiveUnknownOrInvalidSettings(metaDataBuilder);
140+
listener.onSuccess(builder.build());
141+
}
142+
143+
ClusterState.Builder upgradeAndArchiveUnknownOrInvalidSettings(MetaData.Builder metaDataBuilder) {
139144
final ClusterSettings clusterSettings = clusterService.getClusterSettings();
140145
metaDataBuilder.persistentSettings(
141146
clusterSettings.archiveUnknownOrInvalidSettings(
142-
metaDataBuilder.persistentSettings(),
147+
clusterSettings.upgradeSettings(metaDataBuilder.persistentSettings()),
143148
e -> logUnknownSetting("persistent", e),
144149
(e, ex) -> logInvalidSetting("persistent", e, ex)));
145150
metaDataBuilder.transientSettings(
146151
clusterSettings.archiveUnknownOrInvalidSettings(
147-
metaDataBuilder.transientSettings(),
152+
clusterSettings.upgradeSettings(metaDataBuilder.transientSettings()),
148153
e -> logUnknownSetting("transient", e),
149154
(e, ex) -> logInvalidSetting("transient", e, ex)));
150155
ClusterState.Builder builder = clusterService.newClusterStateBuilder();
151156
builder.metaData(metaDataBuilder);
152-
listener.onSuccess(builder.build());
157+
return builder;
153158
}
154159

155160
private void logUnknownSetting(String settingType, Map.Entry<String, String> e) {

server/src/main/java/org/elasticsearch/node/Node.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import org.elasticsearch.common.settings.ClusterSettings;
7676
import org.elasticsearch.common.settings.Setting;
7777
import org.elasticsearch.common.settings.Setting.Property;
78+
import org.elasticsearch.common.settings.SettingUpgrader;
7879
import org.elasticsearch.common.settings.Settings;
7980
import org.elasticsearch.common.settings.SettingsModule;
8081
import org.elasticsearch.common.transport.BoundTransportAddress;
@@ -354,7 +355,15 @@ protected Node(
354355
AnalysisModule analysisModule = new AnalysisModule(this.environment, pluginsService.filterPlugins(AnalysisPlugin.class));
355356
// this is as early as we can validate settings at this point. we already pass them to ScriptModule as well as ThreadPool
356357
// so we might be late here already
357-
final SettingsModule settingsModule = new SettingsModule(this.settings, additionalSettings, additionalSettingsFilter);
358+
359+
final Set<SettingUpgrader<?>> settingsUpgraders = pluginsService.filterPlugins(Plugin.class)
360+
.stream()
361+
.map(Plugin::getSettingUpgraders)
362+
.flatMap(List::stream)
363+
.collect(Collectors.toSet());
364+
365+
final SettingsModule settingsModule =
366+
new SettingsModule(this.settings, additionalSettings, additionalSettingsFilter, settingsUpgraders);
358367
scriptModule.registerClusterSettingsListeners(settingsModule.getClusterSettings());
359368
resourcesToClose.add(resourceWatcherService);
360369
final NetworkService networkService = new NetworkService(

0 commit comments

Comments
 (0)