Skip to content

Commit 28cec56

Browse files
authored
Allocate newly created indices on data_hot tier nodes (#61342)
This commit adds the functionality to allocate newly created indices on nodes in the "hot" tier by default when they are created. This does not break existing behavior, as nodes with the `data` role are considered to be part of the hot tier. Users that separate their deployments by using the `data_hot` (and `data_warm`, `data_cold`, `data_frozen`) roles will have their data allocated on the hot tier nodes now by default. This change is a little more complicated than changing the default value for `index.routing.allocation.include._tier` from null to "data_hot". Instead, this adds the ability to have a plugin inject a setting into the builder for a newly created index. This has the benefit of allowing this setting to be visible as part of the settings when retrieving the index, for example: ``` // Create an index PUT /eggplant // Get an index GET /eggplant?flat_settings ``` Returns the default settings now of: ```json { "eggplant" : { "aliases" : { }, "mappings" : { }, "settings" : { "index.creation_date" : "1597855465598", "index.number_of_replicas" : "1", "index.number_of_shards" : "1", "index.provided_name" : "eggplant", "index.routing.allocation.include._tier" : "data_hot", "index.uuid" : "6ySG78s9RWGystRipoBFCA", "index.version.created" : "8000099" } } } ``` After the initial setting of this setting, it can be treated like any other index level setting. This new setting is *not* set on a new index if any of the following is true: - The index is created with an `index.routing.allocation.include.<anything>` setting - The index is created with an `index.routing.allocation.exclude.<anything>` setting - The index is created with an `index.routing.allocation.require.<anything>` setting - The index is created with a null `index.routing.allocation.include._tier` value - The index was created from an existing source metadata (shrink, clone, split, etc) Relates to #60848
1 parent 1ee3625 commit 28cec56

File tree

15 files changed

+444
-23
lines changed

15 files changed

+444
-23
lines changed

docs/reference/api-conventions.asciidoc

+8
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ Returns:
391391
"index.creation_date": "1474389951325",
392392
"index.uuid": "n6gzFZTgS664GUfx0Xrpjw",
393393
"index.version.created": ...,
394+
"index.routing.allocation.include._tier" : "data_hot",
394395
"index.provided_name" : "my-index-000001"
395396
}
396397
}
@@ -424,6 +425,13 @@ Returns:
424425
"version": {
425426
"created": ...
426427
},
428+
"routing": {
429+
"allocation": {
430+
"include": {
431+
"_tier": "data_hot"
432+
}
433+
}
434+
},
427435
"provided_name" : "my-index-000001"
428436
}
429437
}

docs/reference/cluster/allocation-explain.asciidoc

+6-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ DELETE my-index-000001
113113
[source,console]
114114
--------------------------------------------------
115115
PUT /my-index-000001?master_timeout=1s&timeout=1s
116-
{"settings": {"index.routing.allocation.include._name": "non_existent_node"} }
116+
{
117+
"settings": {
118+
"index.routing.allocation.include._name": "non_existent_node",
119+
"index.routing.allocation.include._tier": null
120+
}
121+
}
117122
118123
GET /_cluster/allocation/explain
119124
{

qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java

+3
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ public void testRelocationWithConcurrentIndexing() throws Exception {
224224
// but the recovering copy will be seen as invalid and the cluster health won't return to GREEN
225225
// before timing out
226226
.put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms")
227+
.put("index.routing.allocation.include._tier", "")
227228
.put(SETTING_ALLOCATION_MAX_RETRY.getKey(), "0"); // fail faster
228229
createIndex(index, settings.build());
229230
indexDocs(index, 0, 10);
@@ -240,6 +241,7 @@ public void testRelocationWithConcurrentIndexing() throws Exception {
240241
.put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0)
241242
.put(INDEX_ROUTING_ALLOCATION_ENABLE_SETTING.getKey(), (String)null)
242243
.put("index.routing.allocation.include._id", oldNode)
244+
.putNull("index.routing.allocation.include._tier")
243245
);
244246
ensureGreen(index); // wait for the primary to be assigned
245247
ensureNoInitializingShards(); // wait for all other shard activity to finish
@@ -262,6 +264,7 @@ public void testRelocationWithConcurrentIndexing() throws Exception {
262264
updateIndexSettings(index, Settings.builder()
263265
.put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 2)
264266
.put("index.routing.allocation.include._id", (String)null)
267+
.putNull("index.routing.allocation.include._tier")
265268
);
266269
asyncIndexDocs(index, 60, 45).get();
267270
ensureGreen(index);

server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java

+74-8
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import org.elasticsearch.index.mapper.MapperService;
6969
import org.elasticsearch.index.mapper.MapperService.MergeReason;
7070
import org.elasticsearch.index.query.QueryShardContext;
71+
import org.elasticsearch.index.shard.IndexSettingProvider;
7172
import org.elasticsearch.indices.IndexCreationException;
7273
import org.elasticsearch.indices.IndicesService;
7374
import org.elasticsearch.indices.InvalidIndexNameException;
@@ -83,6 +84,7 @@
8384
import java.util.ArrayList;
8485
import java.util.Collections;
8586
import java.util.HashMap;
87+
import java.util.HashSet;
8688
import java.util.List;
8789
import java.util.Locale;
8890
import java.util.Map;
@@ -127,6 +129,7 @@ public class MetadataCreateIndexService {
127129
private final SystemIndices systemIndices;
128130
private final ShardLimitValidator shardLimitValidator;
129131
private final boolean forbidPrivateIndexSettings;
132+
private final Set<IndexSettingProvider> indexSettingProviders = new HashSet<>();
130133

131134
public MetadataCreateIndexService(
132135
final Settings settings,
@@ -155,6 +158,19 @@ public MetadataCreateIndexService(
155158
this.shardLimitValidator = shardLimitValidator;
156159
}
157160

161+
/**
162+
* Add a provider to be invoked to get additional index settings prior to an index being created
163+
*/
164+
public void addAdditionalIndexSettingProvider(IndexSettingProvider provider) {
165+
if (provider == null) {
166+
throw new IllegalArgumentException("provider must not be null");
167+
}
168+
if (indexSettingProviders.contains(provider)) {
169+
throw new IllegalArgumentException("provider already added");
170+
}
171+
this.indexSettingProviders.add(provider);
172+
}
173+
158174
/**
159175
* Validate the name for an index against some static rules and a cluster state.
160176
*/
@@ -465,7 +481,7 @@ private ClusterState applyCreateIndexRequestWithV1Templates(final ClusterState c
465481

466482
final Settings aggregatedIndexSettings =
467483
aggregateIndexSettings(currentState, request, MetadataIndexTemplateService.resolveSettings(templates),
468-
null, settings, indexScopedSettings, shardLimitValidator);
484+
null, settings, indexScopedSettings, shardLimitValidator, indexSettingProviders);
469485
int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, null);
470486
IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards);
471487

@@ -491,7 +507,7 @@ private ClusterState applyCreateIndexRequestWithV2Template(final ClusterState cu
491507
final Settings aggregatedIndexSettings =
492508
aggregateIndexSettings(currentState, request,
493509
MetadataIndexTemplateService.resolveSettings(currentState.metadata(), templateName),
494-
null, settings, indexScopedSettings, shardLimitValidator);
510+
null, settings, indexScopedSettings, shardLimitValidator, indexSettingProviders);
495511
int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, null);
496512
IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards);
497513

@@ -537,7 +553,7 @@ private ClusterState applyCreateIndexRequestWithExistingMetadata(final ClusterSt
537553
}
538554

539555
final Settings aggregatedIndexSettings = aggregateIndexSettings(currentState, request, Settings.EMPTY,
540-
sourceMetadata, settings, indexScopedSettings, shardLimitValidator);
556+
sourceMetadata, settings, indexScopedSettings, shardLimitValidator, indexSettingProviders);
541557
final int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, sourceMetadata);
542558
IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards);
543559

@@ -596,14 +612,64 @@ static Map<String, Object> parseV1Mappings(String mappingsJson, List<CompressedX
596612
* @return the aggregated settings for the new index
597613
*/
598614
static Settings aggregateIndexSettings(ClusterState currentState, CreateIndexClusterStateUpdateRequest request,
599-
Settings templateSettings, @Nullable IndexMetadata sourceMetadata, Settings settings,
600-
IndexScopedSettings indexScopedSettings, ShardLimitValidator shardLimitValidator) {
601-
Settings.Builder indexSettingsBuilder = Settings.builder();
615+
Settings combinedTemplateSettings, @Nullable IndexMetadata sourceMetadata, Settings settings,
616+
IndexScopedSettings indexScopedSettings, ShardLimitValidator shardLimitValidator,
617+
Set<IndexSettingProvider> indexSettingProviders) {
618+
// Create builders for the template and request settings. We transform these into builders
619+
// because we may want settings to be "removed" from these prior to being set on the new
620+
// index (see more comments below)
621+
final Settings.Builder templateSettings = Settings.builder().put(combinedTemplateSettings);
622+
final Settings.Builder requestSettings = Settings.builder().put(request.settings());
623+
624+
final Settings.Builder indexSettingsBuilder = Settings.builder();
602625
if (sourceMetadata == null) {
603-
indexSettingsBuilder.put(templateSettings);
626+
final Settings.Builder additionalIndexSettings = Settings.builder();
627+
final Settings templateAndRequestSettings = Settings.builder()
628+
.put(combinedTemplateSettings)
629+
.put(request.settings())
630+
.build();
631+
632+
// Loop through all the explicit index setting providers, adding them to the
633+
// additionalIndexSettings map
634+
for (IndexSettingProvider provider : indexSettingProviders) {
635+
additionalIndexSettings.put(provider.getAdditionalIndexSettings(request.index(), templateAndRequestSettings));
636+
}
637+
638+
// For all the explicit settings, we go through the template and request level settings
639+
// and see if either a template or the request has "cancelled out" an explicit default
640+
// setting. For example, if a plugin had as an explicit setting:
641+
// "index.mysetting": "blah
642+
// And either a template or create index request had:
643+
// "index.mysetting": null
644+
// We want to remove the explicit setting not only from the explicitly set settings, but
645+
// also from the template and request settings, so that from the newly create index's
646+
// perspective it is as though the setting has not been set at all (using the default
647+
// value).
648+
for (String explicitSetting : additionalIndexSettings.keys()) {
649+
if (templateSettings.keys().contains(explicitSetting) && templateSettings.get(explicitSetting) == null) {
650+
logger.debug("removing default [{}] setting as it in set to null in a template for [{}] creation",
651+
explicitSetting, request.index());
652+
additionalIndexSettings.remove(explicitSetting);
653+
templateSettings.remove(explicitSetting);
654+
}
655+
if (requestSettings.keys().contains(explicitSetting) && requestSettings.get(explicitSetting) == null) {
656+
logger.debug("removing default [{}] setting as it in set to null in the request for [{}] creation",
657+
explicitSetting, request.index());
658+
additionalIndexSettings.remove(explicitSetting);
659+
requestSettings.remove(explicitSetting);
660+
}
661+
}
662+
663+
// Finally, we actually add the explicit defaults prior to the template settings and the
664+
// request settings, so that the precedence goes:
665+
// Explicit Defaults -> Template -> Request -> Necessary Settings (# of shards, uuid, etc)
666+
indexSettingsBuilder.put(additionalIndexSettings.build());
667+
indexSettingsBuilder.put(templateSettings.build());
604668
}
669+
605670
// now, put the request settings, so they override templates
606-
indexSettingsBuilder.put(request.settings());
671+
indexSettingsBuilder.put(requestSettings.build());
672+
607673
if (indexSettingsBuilder.get(IndexMetadata.SETTING_INDEX_VERSION_CREATED.getKey()) == null) {
608674
final DiscoveryNodes nodes = currentState.nodes();
609675
final Version createdVersion = Version.min(Version.CURRENT, nodes.getSmallestNonClientNodeVersion());

server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodeFilters.java

+3
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ public boolean match(DiscoveryNode node) {
181181
}
182182
}
183183
}
184+
} else if ("_tier".equals(attr)) {
185+
// Always allow _tier as an attribute, will be handled elsewhere
186+
return true;
184187
} else {
185188
String nodeAttributeValue = node.getAttributes().get(attr);
186189
if (nodeAttributeValue == null) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.index.shard;
21+
22+
import org.elasticsearch.common.settings.Settings;
23+
24+
/**
25+
* An {@link IndexSettingProvider} is a provider for index level settings that can be set
26+
* explicitly as a default value (so they show up as "set" for newly created indices)
27+
*/
28+
public interface IndexSettingProvider {
29+
/**
30+
* Returns explicitly set default index {@link Settings} for the given index. This should not
31+
* return null.
32+
*/
33+
default Settings getAdditionalIndexSettings(String indexName, Settings templateAndRequestSettings) {
34+
return Settings.EMPTY;
35+
}
36+
}

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

+3
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,9 @@ protected Node(final Environment initialEnvironment,
503503
systemIndices,
504504
forbidPrivateIndexSettings
505505
);
506+
pluginsService.filterPlugins(Plugin.class)
507+
.forEach(p -> p.getAdditionalIndexSettingProviders()
508+
.forEach(metadataCreateIndexService::addAdditionalIndexSettingProvider));
506509

507510
final MetadataCreateDataStreamService metadataCreateDataStreamService =
508511
new MetadataCreateDataStreamService(threadPool, clusterService, metadataCreateIndexService);

server/src/main/java/org/elasticsearch/plugins/Plugin.java

+11
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.elasticsearch.env.Environment;
3737
import org.elasticsearch.env.NodeEnvironment;
3838
import org.elasticsearch.index.IndexModule;
39+
import org.elasticsearch.index.shard.IndexSettingProvider;
3940
import org.elasticsearch.repositories.RepositoriesService;
4041
import org.elasticsearch.script.ScriptService;
4142
import org.elasticsearch.threadpool.ExecutorBuilder;
@@ -197,4 +198,14 @@ public Set<DiscoveryNodeRole> getRoles() {
197198
public void close() throws IOException {
198199

199200
}
201+
202+
/**
203+
* An {@link IndexSettingProvider} allows hooking in to parts of an index
204+
* lifecycle to provide explicit default settings for newly created indices. Rather than changing
205+
* the default values for an index-level setting, these act as though the setting has been set
206+
* explicitly, but still allow the setting to be overridden by a template or creation request body.
207+
*/
208+
public Collection<IndexSettingProvider> getAdditionalIndexSettingProviders() {
209+
return Collections.emptyList();
210+
}
200211
}

0 commit comments

Comments
 (0)