Pārlūkot izejas kodu

Cleanup some Inefficiencies in JavaDateFormatter (#84922)

Fixes a couple of smaller inefficiencies that I found in profiling and otherwise.
* Iteration over the parsers is faster if we just use an array.
* Make some of the object construction more efficient since formats aren't always constants, like
in e.g. the index name resolver.
* Fix non-static initializer block running useless puts into the static mutable map on every instantiation
  * no need to even have the map, just inline what it does and save some code, indirection and the
iteration over the mutable map
Armin Braun 3 gadi atpakaļ
vecāks
revīzija
7b215a13b3

+ 0 - 9
server/src/main/java/org/elasticsearch/common/time/DateFormatters.java

@@ -9,8 +9,6 @@
 package org.elasticsearch.common.time;
 
 import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.logging.DeprecationLogger;
-import org.elasticsearch.common.util.LazyInitializable;
 import org.elasticsearch.core.SuppressForbidden;
 
 import java.time.Instant;
@@ -43,13 +41,6 @@ import static java.time.temporal.ChronoField.NANO_OF_SECOND;
 import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
 
 public class DateFormatters {
-    // DateFormatters is being used even before the logging is initialized.
-    // If LogManager.getLogger is called before logging config is loaded
-    // it results in errors sent to status logger and startup to fail.
-    // Hence a lazy initialization.
-    private static final LazyInitializable<DeprecationLogger, RuntimeException> deprecationLogger = new LazyInitializable<>(
-        () -> DeprecationLogger.getLogger(FormatNames.class)
-    );
 
     public static final WeekFields WEEK_FIELDS_ROOT = WeekFields.of(Locale.ROOT);
 

+ 79 - 67
server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java

@@ -9,7 +9,6 @@
 package org.elasticsearch.common.time;
 
 import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.util.Maps;
 
 import java.text.ParsePosition;
 import java.time.ZoneId;
@@ -18,44 +17,25 @@ import java.time.format.DateTimeFormatterBuilder;
 import java.time.format.DateTimeParseException;
 import java.time.temporal.ChronoField;
 import java.time.temporal.TemporalAccessor;
-import java.time.temporal.TemporalField;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Objects;
 import java.util.function.Consumer;
+import java.util.function.UnaryOperator;
 
 class JavaDateFormatter implements DateFormatter {
 
-    // base fields which should be used for default parsing, when we round up for date math
-    private static final Map<TemporalField, Long> ROUND_UP_BASE_FIELDS = Maps.newMapWithExpectedSize(6);
-
-    {
-        ROUND_UP_BASE_FIELDS.put(ChronoField.MONTH_OF_YEAR, 1L);
-        ROUND_UP_BASE_FIELDS.put(ChronoField.DAY_OF_MONTH, 1L);
-        ROUND_UP_BASE_FIELDS.put(ChronoField.HOUR_OF_DAY, 23L);
-        ROUND_UP_BASE_FIELDS.put(ChronoField.MINUTE_OF_HOUR, 59L);
-        ROUND_UP_BASE_FIELDS.put(ChronoField.SECOND_OF_MINUTE, 59L);
-        ROUND_UP_BASE_FIELDS.put(ChronoField.NANO_OF_SECOND, 999_999_999L);
-    }
-
     private final String format;
     private final DateTimeFormatter printer;
-    private final List<DateTimeFormatter> parsers;
-    private final JavaDateFormatter roundupParser;
+    private final DateTimeFormatter[] parsers;
+    private final RoundUpFormatter roundupParser;
 
-    static class RoundUpFormatter extends JavaDateFormatter {
+    private static final class RoundUpFormatter extends JavaDateFormatter {
 
-        RoundUpFormatter(String format, List<DateTimeFormatter> roundUpParsers) {
-            super(format, firstFrom(roundUpParsers), null, roundUpParsers);
-        }
-
-        private static DateTimeFormatter firstFrom(List<DateTimeFormatter> roundUpParsers) {
-            return roundUpParsers.get(0);
+        RoundUpFormatter(String format, DateTimeFormatter[] roundUpParsers) {
+            super(format, roundUpParsers[0], (RoundUpFormatter) null, roundUpParsers);
         }
 
         @Override
@@ -66,7 +46,18 @@ class JavaDateFormatter implements DateFormatter {
 
     // named formatters use default roundUpParser
     JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeFormatter... parsers) {
-        this(format, printer, builder -> ROUND_UP_BASE_FIELDS.forEach(builder::parseDefaulting), parsers);
+        this(
+            format,
+            printer,
+            // set up base fields which should be used for default parsing, when we round up for date math
+            builder -> builder.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1L)
+                .parseDefaulting(ChronoField.DAY_OF_MONTH, 1L)
+                .parseDefaulting(ChronoField.HOUR_OF_DAY, 23L)
+                .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 59L)
+                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 59L)
+                .parseDefaulting(ChronoField.NANO_OF_SECOND, 999_999_999L),
+            parsers
+        );
     }
 
     // subclasses override roundUpParser
@@ -79,24 +70,28 @@ class JavaDateFormatter implements DateFormatter {
         if (printer == null) {
             throw new IllegalArgumentException("printer may not be null");
         }
-        long distinctZones = Arrays.stream(parsers).map(DateTimeFormatter::getZone).distinct().count();
-        if (distinctZones > 1) {
-            throw new IllegalArgumentException("formatters must have the same time zone");
-        }
-        long distinctLocales = Arrays.stream(parsers).map(DateTimeFormatter::getLocale).distinct().count();
-        if (distinctLocales > 1) {
-            throw new IllegalArgumentException("formatters must have the same locale");
-        }
         this.printer = printer;
         this.format = format;
+        this.parsers = parsersArray(printer, parsers);
+        this.roundupParser = createRoundUpParser(format, roundupParserConsumer, locale(), this.parsers);
+    }
 
+    private static DateTimeFormatter[] parsersArray(DateTimeFormatter printer, DateTimeFormatter... parsers) {
         if (parsers.length == 0) {
-            this.parsers = Collections.singletonList(printer);
-        } else {
-            this.parsers = Arrays.asList(parsers);
+            return new DateTimeFormatter[] { printer };
+        }
+        final ZoneId zoneId = parsers[0].getZone();
+        final Locale locale = parsers[0].getLocale();
+        for (int i = 1; i < parsers.length; i++) {
+            final DateTimeFormatter parser = parsers[i];
+            if (Objects.equals(parser.getZone(), zoneId) == false) {
+                throw new IllegalArgumentException("formatters must have the same time zone");
+            }
+            if (Objects.equals(parser.getLocale(), locale) == false) {
+                throw new IllegalArgumentException("formatters must have the same locale");
+            }
         }
-        List<DateTimeFormatter> roundUp = createRoundUpParser(format, roundupParserConsumer);
-        this.roundupParser = new RoundUpFormatter(format, roundUp);
+        return parsers;
     }
 
     /**
@@ -108,16 +103,19 @@ class JavaDateFormatter implements DateFormatter {
      * <code>DateFormatters</code>.
      * This means that we need to also have multiple RoundUp parsers.
      */
-    private List<DateTimeFormatter> createRoundUpParser(String format, Consumer<DateTimeFormatterBuilder> roundupParserConsumer) {
+    private static RoundUpFormatter createRoundUpParser(
+        String format,
+        Consumer<DateTimeFormatterBuilder> roundupParserConsumer,
+        Locale locale,
+        DateTimeFormatter[] parsers
+    ) {
         if (format.contains("||") == false) {
-            List<DateTimeFormatter> roundUpParsers = new ArrayList<>();
-            for (DateTimeFormatter parser : this.parsers) {
+            return new RoundUpFormatter(format, mapParsers(parser -> {
                 DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
                 builder.append(parser);
                 roundupParserConsumer.accept(builder);
-                roundUpParsers.add(builder.toFormatter(locale()));
-            }
-            return roundUpParsers;
+                return builder.toFormatter(locale);
+            }, parsers));
         }
         return null;
     }
@@ -135,22 +133,26 @@ class JavaDateFormatter implements DateFormatter {
             if (printer == null) {
                 printer = javaDateFormatter.getPrinter();
             }
-            parsers.addAll(javaDateFormatter.getParsers());
-            roundUpParsers.addAll(javaDateFormatter.getRoundupParser().getParsers());
+            Collections.addAll(parsers, javaDateFormatter.parsers);
+            Collections.addAll(roundUpParsers, javaDateFormatter.getRoundupParser().parsers);
         }
 
-        return new JavaDateFormatter(input, printer, roundUpParsers, parsers);
+        return new JavaDateFormatter(
+            input,
+            printer,
+            roundUpParsers.toArray(DateTimeFormatter[]::new),
+            parsers.toArray(DateTimeFormatter[]::new)
+        );
     }
 
-    private JavaDateFormatter(
-        String format,
-        DateTimeFormatter printer,
-        List<DateTimeFormatter> roundUpParsers,
-        List<DateTimeFormatter> parsers
-    ) {
+    private JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeFormatter[] roundUpParsers, DateTimeFormatter[] parsers) {
+        this(format, printer, new RoundUpFormatter(format, roundUpParsers), parsers);
+    }
+
+    private JavaDateFormatter(String format, DateTimeFormatter printer, RoundUpFormatter roundupParser, DateTimeFormatter[] parsers) {
         this.format = format;
         this.printer = printer;
-        this.roundupParser = roundUpParsers != null ? new RoundUpFormatter(format, roundUpParsers) : null;
+        this.roundupParser = roundupParser;
         this.parsers = parsers;
     }
 
@@ -190,7 +192,7 @@ class JavaDateFormatter implements DateFormatter {
      * @throws DateTimeParseException when unable to parse with any parsers
      */
     private TemporalAccessor doParse(String input) {
-        if (parsers.size() > 1) {
+        if (parsers.length > 1) {
             for (DateTimeFormatter formatter : parsers) {
                 ParsePosition pos = new ParsePosition(0);
                 Object object = formatter.toFormat().parseObject(input, pos);
@@ -200,7 +202,7 @@ class JavaDateFormatter implements DateFormatter {
             }
             throw new DateTimeParseException("Failed to parse with all enclosed parsers", input, 0);
         }
-        return this.parsers.get(0).parse(input);
+        return this.parsers[0].parse(input);
     }
 
     private boolean parsingSucceeded(Object object, String input, ParsePosition pos) {
@@ -213,9 +215,7 @@ class JavaDateFormatter implements DateFormatter {
         if (zoneId.equals(zone())) {
             return this;
         }
-        List<DateTimeFormatter> parsers = this.parsers.stream().map(p -> p.withZone(zoneId)).toList();
-        List<DateTimeFormatter> roundUpParsers = this.roundupParser.getParsers().stream().map(p -> p.withZone(zoneId)).toList();
-        return new JavaDateFormatter(format, printer.withZone(zoneId), roundUpParsers, parsers);
+        return mapParsers(p -> p.withZone(zoneId));
     }
 
     @Override
@@ -224,9 +224,24 @@ class JavaDateFormatter implements DateFormatter {
         if (locale.equals(locale())) {
             return this;
         }
-        List<DateTimeFormatter> parsers = this.parsers.stream().map(p -> p.withLocale(locale)).toList();
-        List<DateTimeFormatter> roundUpParsers = this.roundupParser.getParsers().stream().map(p -> p.withLocale(locale)).toList();
-        return new JavaDateFormatter(format, printer.withLocale(locale), roundUpParsers, parsers);
+        return mapParsers(p -> p.withLocale(locale));
+    }
+
+    private JavaDateFormatter mapParsers(UnaryOperator<DateTimeFormatter> mapping) {
+        return new JavaDateFormatter(
+            format,
+            mapping.apply(printer),
+            mapParsers(mapping, ((JavaDateFormatter) this.roundupParser).parsers),
+            mapParsers(mapping, this.parsers)
+        );
+    }
+
+    private static DateTimeFormatter[] mapParsers(UnaryOperator<DateTimeFormatter> mapping, DateTimeFormatter[] parsers) {
+        DateTimeFormatter[] res = new DateTimeFormatter[parsers.length];
+        for (int i = 0; i < parsers.length; i++) {
+            res[i] = mapping.apply(parsers[i]);
+        }
+        return res;
     }
 
     @Override
@@ -276,7 +291,4 @@ class JavaDateFormatter implements DateFormatter {
         return String.format(Locale.ROOT, "format[%s] locale[%s]", format, locale());
     }
 
-    Collection<DateTimeFormatter> getParsers() {
-        return parsers;
-    }
 }