Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.11] Performance Improvement for Datetime formats to 2.11 #10453

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- [Remote Store] Add support to reload repository metadata inplace ([#9569](https://github.com/opensearch-project/OpenSearch/pull/9569))
- [Metrics Framework] Add Metrics framework. ([#10241](https://github.com/opensearch-project/OpenSearch/pull/10241))
- Updating the separator for RemoteStoreLockManager since underscore is allowed in base64UUID url charset ([#10379](https://github.com/opensearch-project/OpenSearch/pull/10379))
- Performance improvement for Datetime field caching ([#4558](https://github.com/opensearch-project/OpenSearch/issues/4558))

### Deprecated

Expand Down
6 changes: 6 additions & 0 deletions distribution/src/config/opensearch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,9 @@ ${path.logs}
# index searcher threadpool.
#
#opensearch.experimental.feature.concurrent_segment_search.enabled: false
#
#
# Gates the optimization of datetime formatters caching along with change in default datetime formatter
# Once there is no observed impact on performance, this feature flag can be removed.
#
#opensearch.experimental.optimization.datetime_formatter_caching.enabled: false
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ protected Settings featureFlagSettings() {
}

private ZonedDateTime date(String date) {
return DateFormatters.from(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parse(date));
return DateFormatters.from(DateFieldMapper.getDefaultDateTimeFormatter().parse(date));
}

private static String format(ZonedDateTime date, String pattern) {
Expand Down Expand Up @@ -1624,8 +1624,8 @@ public void testScriptCaching() throws Exception {
.setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
.get()
);
String date = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.format(date(1, 1));
String date2 = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.format(date(2, 1));
String date = DateFieldMapper.getDefaultDateTimeFormatter().format(date(1, 1));
String date2 = DateFieldMapper.getDefaultDateTimeFormatter().format(date(2, 1));
indexRandom(
true,
client().prepareIndex("cache_test_idx").setId("1").setSource("d", date),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ protected Settings featureFlagSettings() {
}

private ZonedDateTime date(String date) {
return DateFormatters.from(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parse(date));
return DateFormatters.from(DateFieldMapper.getDefaultDateTimeFormatter().parse(date));
}

@Before
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@
return pattern;
}

@Override
public String printPattern() {
throw new UnsupportedOperationException("JodaDateFormatter does not have a print pattern");

Check warning on line 130 in server/src/main/java/org/opensearch/common/joda/JodaDateFormatter.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/common/joda/JodaDateFormatter.java#L130

Added line #L130 was not covered by tests
}

@Override
public Locale locale() {
return printer.getLocale();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ protected FeatureFlagSettings(
FeatureFlags.EXTENSIONS_SETTING,
FeatureFlags.IDENTITY_SETTING,
FeatureFlags.CONCURRENT_SEGMENT_SEARCH_SETTING,
FeatureFlags.TELEMETRY_SETTING
FeatureFlags.TELEMETRY_SETTING,
FeatureFlags.DATETIME_FORMATTER_CACHING_SETTING
)
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@
*/
String pattern();

/**
* A name based format for this formatter. Can be one of the registered formatters like <code>epoch_millis</code> or
* a configured format like <code>HH:mm:ss</code>
*
* @return The name of this formatter
*/
String printPattern();

/**
* Returns the configured locale of the date formatter
*
Expand All @@ -147,7 +155,7 @@
*/
DateMathParser toDateMathParser();

static DateFormatter forPattern(String input) {
static DateFormatter forPattern(String input, String printPattern, Boolean canCacheFormatter) {

if (Strings.hasLength(input) == false) {
throw new IllegalArgumentException("No date pattern provided");
Expand All @@ -158,7 +166,28 @@
List<String> patterns = splitCombinedPatterns(format);
List<DateFormatter> formatters = patterns.stream().map(DateFormatters::forPattern).collect(Collectors.toList());

return JavaDateFormatter.combined(input, formatters);
DateFormatter printFormatter = formatters.get(0);
if (Strings.hasLength(printPattern)) {
String printFormat = strip8Prefix(printPattern);
try {
printFormatter = DateFormatters.forPattern(printFormat);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid print format: " + e.getMessage(), e);

Check warning on line 175 in server/src/main/java/org/opensearch/common/time/DateFormatter.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/common/time/DateFormatter.java#L174-L175

Added lines #L174 - L175 were not covered by tests
}
}
return JavaDateFormatter.combined(input, formatters, printFormatter, canCacheFormatter);
}

static DateFormatter forPattern(String input) {
return forPattern(input, null, false);
}

static DateFormatter forPattern(String input, String printPattern) {
return forPattern(input, printPattern, false);
}

static DateFormatter forPattern(String input, Boolean canCacheFormatter) {
return forPattern(input, null, canCacheFormatter);

Check warning on line 190 in server/src/main/java/org/opensearch/common/time/DateFormatter.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/common/time/DateFormatter.java#L190

Added line #L190 was not covered by tests
}

static String strip8Prefix(String input) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

package org.opensearch.common.time;

import org.opensearch.common.util.FeatureFlags;
import org.opensearch.core.common.Strings;

import java.text.ParsePosition;
Expand All @@ -51,6 +52,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

Expand All @@ -67,9 +69,12 @@ class JavaDateFormatter implements DateFormatter {
}

private final String format;
private final String printFormat;
private final DateTimeFormatter printer;
private final List<DateTimeFormatter> parsers;
private final JavaDateFormatter roundupParser;
private final Boolean canCacheLastParsedFormatter;
private volatile DateTimeFormatter lastParsedformatter = null;

/**
* A round up formatter
Expand All @@ -93,8 +98,18 @@ JavaDateFormatter getRoundupParser() {
}

// named formatters use default roundUpParser
JavaDateFormatter(
String format,
String printFormat,
DateTimeFormatter printer,
Boolean canCacheLastParsedFormatter,
DateTimeFormatter... parsers
) {
this(format, printFormat, printer, ROUND_UP_BASE_FIELDS, canCacheLastParsedFormatter, parsers);
}

JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeFormatter... parsers) {
this(format, printer, ROUND_UP_BASE_FIELDS, parsers);
this(format, format, printer, false, parsers);
}

private static final BiConsumer<DateTimeFormatterBuilder, DateTimeFormatter> ROUND_UP_BASE_FIELDS = (builder, parser) -> {
Expand All @@ -111,8 +126,10 @@ JavaDateFormatter getRoundupParser() {
// subclasses override roundUpParser
JavaDateFormatter(
String format,
String printFormat,
DateTimeFormatter printer,
BiConsumer<DateTimeFormatterBuilder, DateTimeFormatter> roundupParserConsumer,
Boolean canCacheLastParsedFormatter,
DateTimeFormatter... parsers
) {
if (printer == null) {
Expand All @@ -128,6 +145,8 @@ JavaDateFormatter getRoundupParser() {
}
this.printer = printer;
this.format = format;
this.printFormat = printFormat;
this.canCacheLastParsedFormatter = canCacheLastParsedFormatter;

if (parsers.length == 0) {
this.parsers = Collections.singletonList(printer);
Expand All @@ -138,6 +157,15 @@ JavaDateFormatter getRoundupParser() {
this.roundupParser = new RoundUpFormatter(format, roundUp);
}

JavaDateFormatter(
String format,
DateTimeFormatter printer,
BiConsumer<DateTimeFormatterBuilder, DateTimeFormatter> roundupParserConsumer,
DateTimeFormatter... parsers
) {
this(format, format, printer, roundupParserConsumer, false, parsers);
}

/**
* This is when the RoundUp Formatters are created. In further merges (with ||) it will only append them to a list.
* || is not expected to be provided as format when a RoundUp formatter is created. It will be splitted before in
Expand All @@ -164,36 +192,61 @@ private List<DateTimeFormatter> createRoundUpParser(
return null;
}

public static DateFormatter combined(String input, List<DateFormatter> formatters) {
public static DateFormatter combined(
String input,
List<DateFormatter> formatters,
DateFormatter printFormatter,
Boolean canCacheLastParsedFormatter
) {
assert formatters.size() > 0;
assert printFormatter != null;

List<DateTimeFormatter> parsers = new ArrayList<>(formatters.size());
List<DateTimeFormatter> roundUpParsers = new ArrayList<>(formatters.size());

DateTimeFormatter printer = null;
assert printFormatter instanceof JavaDateFormatter;
JavaDateFormatter javaPrintFormatter = (JavaDateFormatter) printFormatter;
DateTimeFormatter printer = javaPrintFormatter.getPrinter();
for (DateFormatter formatter : formatters) {
assert formatter instanceof JavaDateFormatter;
JavaDateFormatter javaDateFormatter = (JavaDateFormatter) formatter;
if (printer == null) {
printer = javaDateFormatter.getPrinter();
}
parsers.addAll(javaDateFormatter.getParsers());
roundUpParsers.addAll(javaDateFormatter.getRoundupParser().getParsers());
}

return new JavaDateFormatter(input, printer, roundUpParsers, parsers);
return new JavaDateFormatter(
input,
javaPrintFormatter.format,
printer,
roundUpParsers,
parsers,
canCacheLastParsedFormatter & FeatureFlags.isEnabled(FeatureFlags.DATETIME_FORMATTER_CACHING_SETTING)
); // check if caching is enabled
}

private JavaDateFormatter(
String format,
String printFormat,
DateTimeFormatter printer,
List<DateTimeFormatter> roundUpParsers,
List<DateTimeFormatter> parsers
List<DateTimeFormatter> parsers,
Boolean canCacheLastParsedFormatter
) {
this.format = format;
this.printFormat = printFormat;
this.printer = printer;
this.roundupParser = roundUpParsers != null ? new RoundUpFormatter(format, roundUpParsers) : null;
this.parsers = parsers;
this.canCacheLastParsedFormatter = canCacheLastParsedFormatter;
}

private JavaDateFormatter(
String format,
DateTimeFormatter printer,
List<DateTimeFormatter> roundUpParsers,
List<DateTimeFormatter> parsers
) {
this(format, format, printer, roundUpParsers, parsers, false);
}

JavaDateFormatter getRoundupParser() {
Expand Down Expand Up @@ -233,13 +286,23 @@ public TemporalAccessor parse(String input) {
*/
private TemporalAccessor doParse(String input) {
if (parsers.size() > 1) {
Object object = null;
if (canCacheLastParsedFormatter && lastParsedformatter != null) {
ParsePosition pos = new ParsePosition(0);
object = lastParsedformatter.toFormat().parseObject(input, pos);
if (parsingSucceeded(object, input, pos)) {
return (TemporalAccessor) object;
}
}
for (DateTimeFormatter formatter : parsers) {
ParsePosition pos = new ParsePosition(0);
Object object = formatter.toFormat().parseObject(input, pos);
object = formatter.toFormat().parseObject(input, pos);
if (parsingSucceeded(object, input, pos)) {
lastParsedformatter = formatter;
return (TemporalAccessor) object;
}
}

throw new DateTimeParseException("Failed to parse with all enclosed parsers", input, 0);
}
return this.parsers.get(0).parse(input);
Expand All @@ -255,12 +318,14 @@ public DateFormatter withZone(ZoneId zoneId) {
if (zoneId.equals(zone())) {
return this;
}
List<DateTimeFormatter> parsers = this.parsers.stream().map(p -> p.withZone(zoneId)).collect(Collectors.toList());
List<DateTimeFormatter> parsers = new CopyOnWriteArrayList<>(
this.parsers.stream().map(p -> p.withZone(zoneId)).collect(Collectors.toList())
);
List<DateTimeFormatter> roundUpParsers = this.roundupParser.getParsers()
.stream()
.map(p -> p.withZone(zoneId))
.collect(Collectors.toList());
return new JavaDateFormatter(format, printer.withZone(zoneId), roundUpParsers, parsers);
return new JavaDateFormatter(format, printFormat, printer.withZone(zoneId), roundUpParsers, parsers, canCacheLastParsedFormatter);
}

@Override
Expand All @@ -269,12 +334,14 @@ public DateFormatter withLocale(Locale locale) {
if (locale.equals(locale())) {
return this;
}
List<DateTimeFormatter> parsers = this.parsers.stream().map(p -> p.withLocale(locale)).collect(Collectors.toList());
List<DateTimeFormatter> parsers = new CopyOnWriteArrayList<>(
this.parsers.stream().map(p -> p.withLocale(locale)).collect(Collectors.toList())
);
List<DateTimeFormatter> roundUpParsers = this.roundupParser.getParsers()
.stream()
.map(p -> p.withLocale(locale))
.collect(Collectors.toList());
return new JavaDateFormatter(format, printer.withLocale(locale), roundUpParsers, parsers);
return new JavaDateFormatter(format, printFormat, printer.withLocale(locale), roundUpParsers, parsers, canCacheLastParsedFormatter);
}

@Override
Expand All @@ -287,6 +354,11 @@ public String pattern() {
return format;
}

@Override
public String printPattern() {
return printFormat;
}

@Override
public Locale locale() {
return this.printer.getLocale();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@
*/
public static final String TELEMETRY = "opensearch.experimental.feature.telemetry.enabled";

/**
* Gates the optimization of datetime formatters caching along with change in default datetime formatter.
*/
public static final String DATETIME_FORMATTER_CACHING = "opensearch.experimental.optimization.datetime_formatter_caching.enabled";

/**
* Should store the settings from opensearch.yml.
*/
Expand Down Expand Up @@ -83,6 +88,17 @@
return settings != null && settings.getAsBoolean(featureFlagName, false);
}

public static boolean isEnabled(Setting<Boolean> featureFlag) {
if ("true".equalsIgnoreCase(System.getProperty(featureFlag.getKey()))) {
// TODO: Remove the if condition once FeatureFlags are only supported via opensearch.yml
return true;

Check warning on line 94 in server/src/main/java/org/opensearch/common/util/FeatureFlags.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/common/util/FeatureFlags.java#L94

Added line #L94 was not covered by tests
} else if (settings != null) {
return featureFlag.get(settings);
} else {
return featureFlag.getDefault(Settings.EMPTY);
}
}

public static final Setting<Boolean> SEGMENT_REPLICATION_EXPERIMENTAL_SETTING = Setting.boolSetting(
SEGMENT_REPLICATION_EXPERIMENTAL,
false,
Expand All @@ -100,4 +116,10 @@
false,
Property.NodeScope
);

public static final Setting<Boolean> DATETIME_FORMATTER_CACHING_SETTING = Setting.boolSetting(
DATETIME_FORMATTER_CACHING,
true,
Property.NodeScope
);
}
Loading
Loading