From b74c0fec246b94ed4d7e97d1f3b16b5a98cec541 Mon Sep 17 00:00:00 2001 From: Jordan Powers Date: Thu, 20 Nov 2025 08:38:19 -0800 Subject: [PATCH 1/2] Add MappingParsingBenchmark --- benchmarks/build.gradle | 1 + .../common/MappingParsingBenchmark.java | 171 ++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 benchmarks/src/main/java/org/elasticsearch/benchmark/indices/common/MappingParsingBenchmark.java diff --git a/benchmarks/build.gradle b/benchmarks/build.gradle index 93a8797cccbae..dcb63e74dcfcf 100644 --- a/benchmarks/build.gradle +++ b/benchmarks/build.gradle @@ -48,6 +48,7 @@ dependencies { api(project(':x-pack:plugin:esql')) api(project(':x-pack:plugin:esql:compute')) api(project(':x-pack:plugin:mapper-exponential-histogram')) + api(project(':x-pack:plugin:logsdb')) implementation project(path: ':libs:native') implementation project(path: ':libs:simdvec') implementation project(path: ':libs:exponential-histogram') diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/indices/common/MappingParsingBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/indices/common/MappingParsingBenchmark.java new file mode 100644 index 0000000000000..cb9ff8773132b --- /dev/null +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/indices/common/MappingParsingBenchmark.java @@ -0,0 +1,171 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.benchmark.indices.common; + +import org.elasticsearch.TransportVersion; +import org.elasticsearch.cluster.ClusterModule; +import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.common.compress.CompressedXContent; +import org.elasticsearch.common.logging.LogConfigurator; +import org.elasticsearch.common.settings.IndexScopedSettings; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; +import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.analysis.IndexAnalyzers; +import org.elasticsearch.index.cache.bitset.BitsetFilterCache; +import org.elasticsearch.index.mapper.MapperMetrics; +import org.elasticsearch.index.mapper.MapperRegistry; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.mapper.ProvidedIdFieldMapper; +import org.elasticsearch.index.similarity.SimilarityService; +import org.elasticsearch.indices.IndicesModule; +import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptCompiler; +import org.elasticsearch.script.ScriptContext; +import org.elasticsearch.xcontent.NamedXContentRegistry; +import org.elasticsearch.xcontent.XContentParserConfiguration; +import org.elasticsearch.xpack.logsdb.LogsDBPlugin; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +@Fork(value = 1) +@Warmup(iterations = 2) +@Measurement(iterations = 5) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +public class MappingParsingBenchmark { + static { + // For Elasticsearch900Lucene101Codec: + LogConfigurator.loadLog4jPlugins(); + LogConfigurator.configureESLogging(); + LogConfigurator.setNodeName("test"); + } + + private static final String MAPPING = """ + { + "_doc": { + "dynamic": false, + "properties": { + "@timestamp": { + "type": "date" + }, + "host": { + "type": "object", + "properties": { + "name": { + "type": "keyword" + } + } + }, + "message": { + "type": "pattern_text" + } + } + } + } + \s"""; + + @Param("1024") + private int numIndices; + + private List mapperServices; + private CompressedXContent compressedMapping; + + private Random random = new Random(); + private static final String CHARS = "abcdefghijklmnopqrstuvwxyz1234567890"; + + private String randomIndexName() { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < 10; i++) { + b.append(CHARS.charAt(random.nextInt(CHARS.length()))); + } + return b.toString(); + } + + @Setup + public void setUp() throws IOException { + Settings settings = Settings.builder() + .put("index.number_of_replicas", 0) + .put("index.number_of_shards", 1) + .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current()) + .put("index.mode", "logsdb") + .put("index.logsdb.sort_on_host_name", true) + .put("index.logsdb.sort_on_message_template", true) + .build(); + + LogsDBPlugin logsDBPlugin = new LogsDBPlugin(settings); + + Set> definedSettings = new HashSet<>(IndexScopedSettings.BUILT_IN_INDEX_SETTINGS); + definedSettings.addAll(logsDBPlugin.getSettings().stream().filter(Setting::hasIndexScope).toList()); + IndexScopedSettings indexScopedSettings = new IndexScopedSettings(Settings.EMPTY, definedSettings); + + mapperServices = new ArrayList<>(numIndices); + for (int i = 0; i < numIndices; i++) { + IndexMetadata meta = IndexMetadata.builder(randomIndexName()).settings(settings).build(); + IndexSettings indexSettings = new IndexSettings(meta, settings, indexScopedSettings); + MapperRegistry mapperRegistry = new IndicesModule(List.of(logsDBPlugin)).getMapperRegistry(); + SimilarityService similarityService = new SimilarityService(indexSettings, null, Map.of()); + BitsetFilterCache bitsetFilterCache = new BitsetFilterCache(indexSettings, BitsetFilterCache.Listener.NOOP); + MapperService mapperService = new MapperService( + () -> TransportVersion.current(), + indexSettings, + IndexAnalyzers.of(Map.of()), + XContentParserConfiguration.EMPTY.withRegistry(new NamedXContentRegistry(ClusterModule.getNamedXWriteables())) + .withDeprecationHandler(LoggingDeprecationHandler.INSTANCE), + similarityService, + mapperRegistry, + () -> { + throw new UnsupportedOperationException(); + }, + new ProvidedIdFieldMapper(() -> true), + new ScriptCompiler() { + @Override + public T compile(Script script, ScriptContext scriptContext) { + throw new UnsupportedOperationException(); + } + }, + bitsetFilterCache::getBitSetProducer, + MapperMetrics.NOOP + ); + + mapperServices.add(mapperService); + } + + compressedMapping = new CompressedXContent(MAPPING); + } + + @Benchmark + public void mappingParsingBenchmark() { + for (MapperService service : mapperServices) { + service.merge("_doc", compressedMapping, MapperService.MergeReason.MAPPING_UPDATE); + } + } +} From b4f9c07503c6218de4973e3e3db5d53e4b005f94 Mon Sep 17 00:00:00 2001 From: Jordan Powers Date: Tue, 25 Nov 2025 15:04:46 -0800 Subject: [PATCH 2/2] Cache default setting values --- .../org/elasticsearch/common/settings/Setting.java | 6 +----- .../org/elasticsearch/common/settings/Settings.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/settings/Setting.java b/server/src/main/java/org/elasticsearch/common/settings/Setting.java index 35e6de2a6f249..f346e87510ce9 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/Setting.java +++ b/server/src/main/java/org/elasticsearch/common/settings/Setting.java @@ -650,11 +650,7 @@ String innerGetRaw(final Settings settings) { + " and must be stored inside elasticsearch.yml, but was found inside the Elasticsearch keystore" ); } - final String found = settings.get(key); - if (found != null) { - return found; - } - return defaultValue.apply(settings); + return settings.getWithDefaultProvider(key, defaultValue); } /** diff --git a/server/src/main/java/org/elasticsearch/common/settings/Settings.java b/server/src/main/java/org/elasticsearch/common/settings/Settings.java index 321661e3b31ef..cdc651bf09f58 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/Settings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/Settings.java @@ -62,6 +62,7 @@ import java.util.Objects; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.IntFunction; @@ -88,6 +89,8 @@ public final class Settings implements ToXContentFragment, Writeable, Diffable settings; + private final Map defaultCache = new ConcurrentHashMap<>(); + /** The secure settings storage associated with these settings. */ private final SecureSettings secureSettings; @@ -291,6 +294,14 @@ public String get(String setting, String defaultValue) { return retVal == null ? defaultValue : retVal; } + public String getWithDefaultProvider(String setting, Function defaultProvider) { + String retVal = get(setting); + if (retVal == null) { + return defaultCache.computeIfAbsent(setting, (key) -> defaultProvider.apply(this)); + } + return retVal; + } + /** * Returns the values for the given settings pattern. *