Skip to content

Commit e5ad498

Browse files
authored
Return unique deprecation for old indices with incompatible date formats (#124597)
1 parent 64a220a commit e5ad498

File tree

4 files changed

+95
-1
lines changed

4 files changed

+95
-1
lines changed

docs/changelog/124597.yaml

+5
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

+7-1
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,10 @@ public static ZonedDateTime nowWithMillisResolution(Clock clock) {
423423
private static final boolean USES_COMPAT = System.getProperty("java.locale.providers", "").contains("COMPAT");
424424
// check for all textual fields, and localized zone offset
425425
// 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)
426+
private static final Predicate<String> LEGACY_DATE_FORMAT_MATCHER = Pattern.compile("[BEGOavz]|LLL|MMM|QQQ|qqq|ccc|eee|(?<!Z)Z{4}(?!Z)")
427+
.asPredicate();
426428
private static final Predicate<String> CONTAINS_CHANGING_TEXT_SPECIFIERS = USES_COMPAT
427-
? Pattern.compile("[BEGOavz]|LLL|MMM|QQQ|qqq|ccc|eee|(?<!Z)Z{4}(?!Z)").asPredicate()
429+
? LEGACY_DATE_FORMAT_MATCHER
428430
: Predicates.never();
429431
// week dates are changing on CLDR, as the rules are changing for start-of-week and min-days-in-week
430432
private static final Predicate<String> CONTAINS_WEEK_DATE_SPECIFIERS = USES_COMPAT
@@ -452,4 +454,8 @@ static void checkTextualDateFormats(String format) {
452454
);
453455
}
454456
}
457+
458+
public static boolean containsCompatOnlyDateFormat(String format) {
459+
return LEGACY_DATE_FORMAT_MATCHER.test(format);
460+
}
455461
}

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

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

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

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

+38
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,44 @@ public void testMultipleOldIndicesCheckWithTransforms() {
196196
assertEquals(expected, issuesByIndex);
197197
}
198198

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

0 commit comments

Comments
 (0)