Skip to content

Commit ce699ee

Browse files
authored
Data tiers: migrate the coldest node attribute (#81940) (#82247)
This fixes the migrate to data tiers routing API to take into account the scenario where the node attribute configuration for an index is more accurate than the existing `_tier_preference` configuration. Previously we would simply remove the node attributes routing if there was a `_tier_preference` configured for the index. With this commit, we'll look if either the `require.data` or `include.data` custom routings are colder than the existing `_tier_preference` configuration (ie. `cold` vs `data_warm,data_hot`) and update the tier routing accordingly. eg. { index.routing.allocation.require.data: "warm", index.routing.allocation.include.data: "cold", index.routing.allocation.include._tier_preference: "data_hot" } will be migrated to: { index.routing.allocation.include._tier_preference: "data_cold,data_warm,data_hot" } This also removes the existing invariant that had the `require.data` configuration take precedence over a possible `include.data` configuration, and will now migrate the coldest configuration to the corresponding `_tier_preference`. eg. { index.routing.allocation.require.data: "warm", index.routing.allocation.include.data: "cold" } will be migrated to: { index.routing.allocation.include._tier_preference: "data_cold,data_warm,data_hot" }
1 parent eb31269 commit ce699ee

File tree

5 files changed

+302
-33
lines changed

5 files changed

+302
-33
lines changed

docs/reference/data-management/migrate-index-allocation-filters.asciidoc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,34 @@ PUT my-index/_settings
194194
// TEST[continued]
195195

196196
If an index is already in the cold phase, include the cold, warm, and hot tiers.
197+
198+
For indices that have both the `_tier_preference` and `require.data` configured
199+
but the `_tier_preference` is outdated (ie. the node attribute configuration
200+
is "colder" than the configured `_tier_preference`), the migration needs to
201+
remove the `require.data` attribute and update the `_tier_preference` to reflect
202+
the correct tiering.
203+
204+
eg. For an index with the following routing configuration:
205+
[source,JSON]
206+
----
207+
{
208+
"index.routing.allocation.require.data": "warm",
209+
"index.routing.allocation.include._tier_preference": "data_hot"
210+
}
211+
----
212+
213+
The routing configuration should be fixed like so:
214+
[source,console]
215+
----
216+
PUT my-index/_settings
217+
{
218+
"index.routing.allocation.require.data": null,
219+
"index.routing.allocation.include._tier_preference": "data_warm,data_hot"
220+
}
221+
----
222+
// TEST[continued]
223+
224+
This situation can occur in a system that defaults to data tiers when, e.g.,
225+
an ILM policy that uses node attributes is restored and transitions the managed
226+
indices from the hot phase into the warm phase. In this case the node attribute
227+
configuration indicates the correct tier where the index should be allocated.

server/src/main/java/org/elasticsearch/cluster/routing/allocation/DataTier.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,37 @@ public static List<String> parseTierList(String tiers) {
188188
}
189189
}
190190

191+
/**
192+
* Compares the provided tiers for coldness order (eg. warm is colder than hot).
193+
*
194+
* Similar to {@link java.util.Comparator#compare(Object, Object)} returns
195+
* -1 if tier1 is colder than tier2 (ie. compare("data_cold", "data_hot"))
196+
* 0 if tier1 is as cold as tier2 (ie. tier1.equals(tier2) )
197+
* 1 if tier1 is warmer than tier2 (ie. compare("data_hot", "data_cold"))
198+
*
199+
* The provided tiers parameters must be valid data tiers values (ie. {@link #ALL_DATA_TIERS}.
200+
* NOTE: `data_content` is treated as "equal to data_hot" in the tiers hierarchy.
201+
* If invalid tier names are passed the result is non-deterministic.
202+
*/
203+
public static int compare(String tier1, String tier2) {
204+
if (tier1.equals(DATA_CONTENT)) {
205+
tier1 = DATA_HOT;
206+
}
207+
if (tier2.equals(DATA_CONTENT)) {
208+
tier2 = DATA_HOT;
209+
}
210+
int indexOfTier1 = ORDERED_FROZEN_TO_HOT_TIERS.indexOf(tier1);
211+
assert indexOfTier1 >= 0 : "expecting a valid tier to compare but got:" + tier1;
212+
int indexOfTier2 = ORDERED_FROZEN_TO_HOT_TIERS.indexOf(tier2);
213+
assert indexOfTier2 >= 0 : "expecting a valid tier to compare but got:" + tier2;
214+
215+
if (indexOfTier1 == indexOfTier2) {
216+
return 0;
217+
} else {
218+
return indexOfTier1 < indexOfTier2 ? -1 : 1;
219+
}
220+
}
221+
191222
/**
192223
* This setting provider injects the setting allocating all newly created indices with
193224
* {@code index.routing.allocation.include._tier_preference: "data_hot"} for a data stream index

server/src/test/java/org/elasticsearch/cluster/routing/allocation/DataTierTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,16 @@ public void testDataTierSettingValidator() {
177177
expectThrows(IllegalArgumentException.class, () -> validator.validate(DATA_WARM + ", "));
178178
expectThrows(IllegalArgumentException.class, () -> validator.validate(DATA_WARM + ", " + DATA_HOT));
179179
}
180+
181+
public void testCompareDataTiers() {
182+
assertThat(DataTier.compare("data_cold", "data_warm"), is(-1));
183+
assertThat(DataTier.compare("data_cold", "data_cold"), is(0));
184+
assertThat(DataTier.compare("data_warm", "data_cold"), is(1));
185+
// data_content is treated as equal to data_hot
186+
assertThat(DataTier.compare("data_warm", "data_content"), is(-1));
187+
assertThat(DataTier.compare("data_content", "data_content"), is(0));
188+
assertThat(DataTier.compare("data_content", "data_hot"), is(0));
189+
assertThat(DataTier.compare("data_content", "data_warm"), is(1));
190+
191+
}
180192
}

x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/cluster/metadata/MetadataMigrateToDataTiersRoutingService.java

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private MetadataMigrateToDataTiersRoutingService() {}
9191
* index.routing.allocation.include.{nodeAttrName} setting (if present) to the corresponding data tier `_tier_preference` routing.
9292
* We are only able to convert the `frozen`, `cold`, `warm`, or `hot` setting values to the `_tier_preference`. If other
9393
* configuration values are present eg ("the_warm_nodes") the index will not be migrated.
94-
* If the require or include setting is successfully migrated to _tier_preference, the **other** routing settings for the
94+
* If the require or include setting is successfully migrated to _tier_preference, all the **other** routing settings for the
9595
* provided attribute are also removed (if present).
9696
* Eg. if we manage to migrate the `index.routing.allocation.require.data` setting, but the index also has configured
9797
* `index.routing.allocation.include.data` and `index.routing.allocation.exclude.data`, the
@@ -109,6 +109,19 @@ private MetadataMigrateToDataTiersRoutingService() {}
109109
* index.routing.allocation.include._tier_preference: "data_warm,data_hot"
110110
* }
111111
*
112+
* If both the `index.routing.allocation.require.data` and `index.routing.allocation.include.data` settings are configured to
113+
* recognized values the coldest one will be converted to the corresponding `_tier_preference` configuration.
114+
* Eg. the following configuration:
115+
* {
116+
* index.routing.allocation.require.data: "warm",
117+
* index.routing.allocation.include.data: "cold",
118+
* index.routing.allocation.exclude.data: "rack2,rack3"
119+
* }
120+
* will be migrated to:
121+
* {
122+
* index.routing.allocation.include._tier_preference: "data_cold,data_warm,data_hot"
123+
* }
124+
*
112125
* If no @param nodeAttrName is provided "data" will be used.
113126
* If no @param indexTemplateToDelete is provided, no index templates will be deleted.
114127
*
@@ -452,18 +465,21 @@ static List<String> migrateIndices(Metadata.Builder mb, ClusterState currentStat
452465
String nodeAttrIndexExcludeRoutingSetting = INDEX_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + nodeAttrName;
453466
for (ObjectObjectCursor<String, IndexMetadata> index : currentState.metadata().indices()) {
454467
IndexMetadata indexMetadata = index.value;
468+
String indexName = indexMetadata.getIndex().getName();
455469
Settings currentSettings = indexMetadata.getSettings();
456470

457471
boolean removeNodeAttrIndexRoutingSettings = true;
458472

459473
// migrate using the `require` setting
460-
Settings newSettings = maybeMigrateRoutingSettingToTierPreference(nodeAttrIndexRequireRoutingSetting, indexMetadata);
474+
Settings newSettings = maybeMigrateRoutingSettingToTierPreference(
475+
nodeAttrIndexRequireRoutingSetting,
476+
currentSettings,
477+
indexName
478+
);
479+
// we possibly migrated the `require` setting, but maybe that attribute was not the coldest configured.
480+
// let's try to migrate the `include` setting as well
481+
newSettings = maybeMigrateRoutingSettingToTierPreference(nodeAttrIndexIncludeRoutingSetting, newSettings, indexName);
461482

462-
if (newSettings.equals(currentSettings)) {
463-
// migrating based on the `require` setting was not successful, so let's check if the index used the `include` routing
464-
// setting to configure the allocations and try to migrate it
465-
newSettings = maybeMigrateRoutingSettingToTierPreference(nodeAttrIndexIncludeRoutingSetting, indexMetadata);
466-
}
467483
if (newSettings.equals(currentSettings)) {
468484
removeNodeAttrIndexRoutingSettings = false;
469485
// migrating based on the `include` setting was not successful,
@@ -493,25 +509,51 @@ static List<String> migrateIndices(Metadata.Builder mb, ClusterState currentStat
493509

494510
/**
495511
* Attempts to migrate the value of the given attribute routing setting to the _tier_preference equivalent. The provided setting
496-
* needs to be configured and have one of the supported values (hot, warm, cold, or frozen) in order for the migration to be preformed.
512+
* needs to be configured and have one of the supported values (hot, warm, cold, or frozen) in order for the migration to be performed.
497513
* If the migration is successful the provided setting will be removed.
498514
*
499515
* If the migration is **not** executed the current index settings is returned, otherwise the updated settings are returned
500516
*/
501517
private static Settings maybeMigrateRoutingSettingToTierPreference(
502518
String attributeBasedRoutingSettingName,
503-
IndexMetadata indexMetadata
519+
Settings currentIndexSettings,
520+
String indexName
504521
) {
505-
Settings currentIndexSettings = indexMetadata.getSettings();
506522
if (currentIndexSettings.keySet().contains(attributeBasedRoutingSettingName) == false) {
507523
return currentIndexSettings;
508524
}
509525

510526
Settings.Builder newSettingsBuilder = Settings.builder().put(currentIndexSettings);
511-
String indexName = indexMetadata.getIndex().getName();
512527

513528
// look at the value, get the correct tiers config and update the settings
514529
if (currentIndexSettings.keySet().contains(TIER_PREFERENCE)) {
530+
String tierPreferenceConfiguration = currentIndexSettings.get(TIER_PREFERENCE);
531+
List<String> tiersConfiguration = DataTier.parseTierList(tierPreferenceConfiguration);
532+
if (tiersConfiguration.isEmpty() == false) {
533+
String coldestConfiguredTier = tiersConfiguration.get(0);
534+
String attributeValue = currentIndexSettings.get(attributeBasedRoutingSettingName);
535+
String attributeTierEquivalent = "data_" + attributeValue;
536+
if (DataTier.validTierName(attributeTierEquivalent)) {
537+
// if the attribute's tier equivalent would be colder than what is currently the coldest tier configured
538+
// in the _tier_preference setting, the configured attribute routing is more accurate so we'll update the
539+
// tier_preference to reflect this before removing the attribute routing setting.
540+
if (DataTier.compare(attributeTierEquivalent, coldestConfiguredTier) < 0) {
541+
String newTierPreferenceConfiguration = convertAttributeValueToTierPreference(attributeValue);
542+
if (newTierPreferenceConfiguration != null) {
543+
logger.debug(
544+
"index [{}]: updated the [{}] setting to [{}] as the attribute based routing setting [{}] had "
545+
+ "the value [{}]",
546+
indexName,
547+
TIER_PREFERENCE,
548+
newTierPreferenceConfiguration,
549+
attributeBasedRoutingSettingName,
550+
attributeValue
551+
);
552+
newSettingsBuilder.put(TIER_PREFERENCE, newTierPreferenceConfiguration);
553+
}
554+
}
555+
}
556+
}
515557
newSettingsBuilder.remove(attributeBasedRoutingSettingName);
516558
logger.debug("index [{}]: removed setting [{}]", indexName, attributeBasedRoutingSettingName);
517559
} else {

0 commit comments

Comments
 (0)