Skip to content

Commit f820acc

Browse files
moschemark-vieira
andauthored
Return unique deprecation for old indices with incompatible date formats (#124597) (#134371)
(cherry picked from commit e5ad498) Co-authored-by: Mark Vieira <[email protected]>
1 parent 9f98eb8 commit f820acc

File tree

4 files changed

+95
-1
lines changed

4 files changed

+95
-1
lines changed

docs/changelog/124597.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 124597
2+
summary: Return unique deprecation for old indices with incompatible date formats
3+
area: Infra/Core
4+
type: enhancement
5+
issues: []

server/src/main/java/org/elasticsearch/common/time/DateUtils.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,8 +519,10 @@ public static ZonedDateTime nowWithMillisResolution(Clock clock) {
519519
private static final boolean USES_COMPAT = System.getProperty("java.locale.providers", "").contains("COMPAT");
520520
// check for all textual fields, and localized zone offset
521521
// the weird thing with Z is to ONLY match 4 in a row, with no Z before or after (but those groups can also be empty)
522+
private static final Predicate<String> LEGACY_DATE_FORMAT_MATCHER = Pattern.compile("[BEGOavz]|LLL|MMM|QQQ|qqq|ccc|eee|(?<!Z)Z{4}(?!Z)")
523+
.asPredicate();
522524
private static final Predicate<String> CONTAINS_CHANGING_TEXT_SPECIFIERS = USES_COMPAT
523-
? Pattern.compile("[BEGOavz]|LLL|MMM|QQQ|qqq|ccc|eee|(?<!Z)Z{4}(?!Z)").asPredicate()
525+
? LEGACY_DATE_FORMAT_MATCHER
524526
: Predicates.never();
525527
// week dates are changing on CLDR, as the rules are changing for start-of-week and min-days-in-week
526528
private static final Predicate<String> CONTAINS_WEEK_DATE_SPECIFIERS = USES_COMPAT
@@ -547,4 +549,8 @@ static void checkTextualDateFormats(String format) {
547549
);
548550
}
549551
}
552+
553+
public static boolean containsCompatOnlyDateFormat(String format) {
554+
return LEGACY_DATE_FORMAT_MATCHER.test(format);
555+
}
550556
}

x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecker.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.elasticsearch.cluster.metadata.MappingMetadata;
1414
import org.elasticsearch.common.TriFunction;
1515
import org.elasticsearch.common.time.DateFormatter;
16+
import org.elasticsearch.common.time.DateUtils;
1617
import org.elasticsearch.common.time.LegacyFormatNames;
1718
import org.elasticsearch.core.Strings;
1819
import org.elasticsearch.index.IndexModule;
@@ -99,6 +100,21 @@ private DeprecationIssue oldIndicesCheck(
99100
IndexVersion currentCompatibilityVersion = indexMetadata.getCompatibilityVersion();
100101
// We intentionally exclude indices that are in data streams because they will be picked up by DataStreamDeprecationChecks
101102
if (DeprecatedIndexPredicate.reindexRequired(indexMetadata, false, false) && isNotDataStreamIndex(indexMetadata, clusterState)) {
103+
List<String> cldrIncompatibleFieldMappings = new ArrayList<>();
104+
fieldLevelMappingIssue(
105+
indexMetadata,
106+
(mappingMetadata, sourceAsMap) -> cldrIncompatibleFieldMappings.addAll(
107+
findInPropertiesRecursively(
108+
mappingMetadata.type(),
109+
sourceAsMap,
110+
this::isDateFieldWithCompatFormatPattern,
111+
this::cldrIncompatibleFormatPattern,
112+
"",
113+
""
114+
)
115+
)
116+
);
117+
102118
var transforms = transformIdsForIndex(indexMetadata, indexToTransformIds);
103119
if (transforms.isEmpty() == false) {
104120
return new DeprecationIssue(
@@ -116,6 +132,17 @@ private DeprecationIssue oldIndicesCheck(
116132
false,
117133
Map.of("reindex_required", true, "transform_ids", transforms)
118134
);
135+
} else if (cldrIncompatibleFieldMappings.isEmpty() == false) {
136+
return new DeprecationIssue(
137+
DeprecationIssue.Level.CRITICAL,
138+
"Field mappings with incompatible date format patterns in old index",
139+
"https://www.elastic.co/blog/locale-changes-elasticsearch-8-16-jdk-23",
140+
"The index was created before 8.0 and contains mappings that must be reindexed due to locale changes in 8.16+. "
141+
+ "Manual reindexing is required. "
142+
+ String.join(", ", cldrIncompatibleFieldMappings),
143+
false,
144+
null
145+
);
119146
} else {
120147
return new DeprecationIssue(
121148
DeprecationIssue.Level.CRITICAL,
@@ -396,6 +423,24 @@ private DeprecationIssue deprecatedCamelCasePattern(
396423
return null;
397424
}
398425

426+
private boolean isDateFieldWithCompatFormatPattern(Map<?, ?> property) {
427+
if ("date".equals(property.get("type")) && property.containsKey("format")) {
428+
String[] patterns = DateFormatter.splitCombinedPatterns((String) property.get("format"));
429+
for (String pattern : patterns) {
430+
if (DateUtils.containsCompatOnlyDateFormat(pattern)) {
431+
return true;
432+
}
433+
}
434+
}
435+
return false;
436+
}
437+
438+
private String cldrIncompatibleFormatPattern(String type, Map.Entry<?, ?> entry) {
439+
Map<?, ?> value = (Map<?, ?>) entry.getValue();
440+
final String formatFieldValue = (String) value.get("format");
441+
return "Field [" + entry.getKey() + "] with format pattern [" + formatFieldValue + "].";
442+
}
443+
399444
private boolean isDateFieldWithCamelCasePattern(Map<?, ?> property) {
400445
if ("date".equals(property.get("type")) && property.containsKey("format")) {
401446
String[] patterns = DateFormatter.splitCombinedPatterns((String) property.get("format"));

x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationCheckerTests.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,44 @@ public void testMultipleOldIndicesCheckWithTransforms() {
203203
assertEquals(expected, issuesByIndex);
204204
}
205205

206+
public void testOldIndicesWithIncompatibleDateFormatsCheck() {
207+
IndexMetadata indexMetadata = IndexMetadata.builder("test")
208+
.settings(settings(OLD_VERSION))
209+
.numberOfShards(1)
210+
.numberOfReplicas(0)
211+
.state(indexMetdataState)
212+
.putMapping("""
213+
{
214+
"properties": {
215+
"date": {
216+
"type": "date",
217+
"format": "qqqq yyyy"
218+
}
219+
}
220+
}""")
221+
.build();
222+
ClusterState clusterState = ClusterState.builder(ClusterState.EMPTY_STATE)
223+
.metadata(Metadata.builder().put(indexMetadata, true))
224+
.blocks(clusterBlocksForIndices(indexMetadata))
225+
.build();
226+
DeprecationIssue expected = new DeprecationIssue(
227+
DeprecationIssue.Level.CRITICAL,
228+
"Field mappings with incompatible date format patterns in old index",
229+
"https://www.elastic.co/blog/locale-changes-elasticsearch-8-16-jdk-23",
230+
"The index was created before 8.0 and contains mappings that must be reindexed due to locale changes in 8.16+. "
231+
+ "Manual reindexing is required. Field [date] with format pattern [qqqq yyyy].",
232+
false,
233+
null
234+
);
235+
Map<String, List<DeprecationIssue>> issuesByIndex = checker.check(
236+
clusterState,
237+
new DeprecationInfoAction.Request(TimeValue.THIRTY_SECONDS),
238+
emptyPrecomputedData
239+
);
240+
List<DeprecationIssue> issues = issuesByIndex.get("test");
241+
assertEquals(singletonList(expected), issues);
242+
}
243+
206244
private IndexMetadata indexMetadata(String indexName, IndexVersion indexVersion) {
207245
return IndexMetadata.builder(indexName)
208246
.settings(settings(indexVersion))

0 commit comments

Comments
 (0)