diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy
index 0d116a32712..9d205869881 100644
--- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy
+++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy
@@ -340,7 +340,7 @@ class ClusterFormationTasks {
}
// delay reading the file location until execution time by wrapping in a closure within a GString
String file = "${-> new File(node.pluginsTmpDir, pluginZip.singleFile.getName()).toURI().toURL().toString()}"
- Object[] args = [new File(node.homeDir, 'bin/plugin'), 'install', file]
+ Object[] args = [new File(node.homeDir, 'bin/elasticsearch-plugin'), 'install', file]
return configureExecTask(name, project, setup, node, args)
}
diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml
index 476d1269884..69ecb69d32a 100644
--- a/buildSrc/src/main/resources/checkstyle_suppressions.xml
+++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml
@@ -1273,7 +1273,6 @@
-
@@ -1447,7 +1446,6 @@
-
diff --git a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCLIParser.java b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCLIParser.java
index 9cae3b8cb15..ca67fc91132 100644
--- a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCLIParser.java
+++ b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCLIParser.java
@@ -32,10 +32,12 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.monitor.jvm.JvmInfo;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
import static org.elasticsearch.common.cli.CliToolConfig.Builder.cmd;
import static org.elasticsearch.common.cli.CliToolConfig.Builder.optionBuilder;
@@ -131,6 +133,7 @@ final class BootstrapCLIParser extends CliTool {
// hacky way to extract all the fancy extra args, there is no CLI tool helper for this
Iterator iterator = cli.getArgList().iterator();
+ final Map properties = new HashMap<>();
while (iterator.hasNext()) {
String arg = iterator.next();
if (!arg.startsWith("--")) {
@@ -148,20 +151,22 @@ final class BootstrapCLIParser extends CliTool {
String[] splitArg = arg.split("=", 2);
String key = splitArg[0];
String value = splitArg[1];
- System.setProperty("es." + key, value);
+ properties.put("es." + key, value);
} else {
if (iterator.hasNext()) {
String value = iterator.next();
if (value.startsWith("--")) {
throw new UserError(ExitStatus.USAGE, "Parameter [" + arg + "] needs value");
}
- System.setProperty("es." + arg, value);
+ properties.put("es." + arg, value);
} else {
throw new UserError(ExitStatus.USAGE, "Parameter [" + arg + "] needs value");
}
}
}
-
+ for (Map.Entry entry : properties.entrySet()) {
+ System.setProperty(entry.getKey(), entry.getValue());
+ }
return new Start(terminal);
}
diff --git a/core/src/main/java/org/elasticsearch/common/hash/MessageDigests.java b/core/src/main/java/org/elasticsearch/common/hash/MessageDigests.java
index 7b3a108cc45..c7488c0f12b 100644
--- a/core/src/main/java/org/elasticsearch/common/hash/MessageDigests.java
+++ b/core/src/main/java/org/elasticsearch/common/hash/MessageDigests.java
@@ -23,48 +23,46 @@ import org.elasticsearch.ElasticsearchException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
public class MessageDigests {
- private static final MessageDigest MD5_DIGEST;
- private static final MessageDigest SHA_1_DIGEST;
- private static final MessageDigest SHA_256_DIGEST;
-
- static {
- try {
- MD5_DIGEST = MessageDigest.getInstance("MD5");
- SHA_1_DIGEST = MessageDigest.getInstance("SHA-1");
- SHA_256_DIGEST = MessageDigest.getInstance("SHA-256");
- } catch (NoSuchAlgorithmException e) {
- throw new ElasticsearchException("Unexpected exception creating MessageDigest instance", e);
- }
+ private static ThreadLocal createThreadLocalMessageDigest(String digest) {
+ return ThreadLocal.withInitial(() -> {
+ try {
+ return MessageDigest.getInstance(digest);
+ } catch (NoSuchAlgorithmException e) {
+ throw new ElasticsearchException("unexpected exception creating MessageDigest instance for [" + digest + "]", e);
+ }
+ });
}
+ private static final ThreadLocal MD5_DIGEST = createThreadLocalMessageDigest("MD5");
+ private static final ThreadLocal SHA_1_DIGEST = createThreadLocalMessageDigest("SHA-1");
+ private static final ThreadLocal SHA_256_DIGEST = createThreadLocalMessageDigest("SHA-256");
+
public static MessageDigest md5() {
- return clone(MD5_DIGEST);
+ return get(MD5_DIGEST);
}
public static MessageDigest sha1() {
- return clone(SHA_1_DIGEST);
+ return get(SHA_1_DIGEST);
}
public static MessageDigest sha256() {
- return clone(SHA_256_DIGEST);
+ return get(SHA_256_DIGEST);
}
- private static MessageDigest clone(MessageDigest messageDigest) {
- try {
- return (MessageDigest) messageDigest.clone();
- } catch (CloneNotSupportedException e) {
- throw new ElasticsearchException("Unexpected exception cloning MessageDigest instance", e);
- }
+ private static MessageDigest get(ThreadLocal messageDigest) {
+ MessageDigest instance = messageDigest.get();
+ instance.reset();
+ return instance;
}
private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
+
public static String toHexString(byte[] bytes) {
- if (bytes == null) {
- throw new NullPointerException("bytes");
- }
+ Objects.requireNonNull(bytes);
StringBuilder sb = new StringBuilder(2 * bytes.length);
for (int i = 0; i < bytes.length; i++) {
@@ -74,4 +72,5 @@ public class MessageDigests {
return sb.toString();
}
+
}
diff --git a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java
index a6babce961d..e4b92d82d20 100644
--- a/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java
+++ b/core/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java
@@ -60,6 +60,7 @@ import org.elasticsearch.gateway.PrimaryShardAllocator;
import org.elasticsearch.http.HttpTransportSettings;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.store.IndexStoreConfig;
+import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.indices.analysis.HunspellService;
import org.elasticsearch.indices.breaker.HierarchyCircuitBreakerService;
import org.elasticsearch.indices.cache.query.IndicesQueryCache;
@@ -290,7 +291,7 @@ public final class ClusterSettings extends AbstractScopedSettings {
ScriptService.SCRIPT_CACHE_SIZE_SETTING,
ScriptService.SCRIPT_CACHE_EXPIRE_SETTING,
ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING,
- IndicesFieldDataCache.INDICES_FIELDDATA_CLEAN_INTERVAL_SETTING,
+ IndicesService.INDICES_FIELDDATA_CLEAN_INTERVAL_SETTING,
IndicesFieldDataCache.INDICES_FIELDDATA_CACHE_SIZE_KEY,
IndicesRequestCache.INDICES_CACHE_QUERY_SIZE,
IndicesRequestCache.INDICES_CACHE_QUERY_EXPIRE,
diff --git a/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java b/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java
index 86ead8c7ff9..4197da980bc 100644
--- a/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java
+++ b/core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java
@@ -38,8 +38,8 @@ import org.elasticsearch.index.percolator.PercolatorQueriesRegistry;
import org.elasticsearch.index.store.FsDirectoryService;
import org.elasticsearch.index.store.IndexStore;
import org.elasticsearch.index.store.Store;
+import org.elasticsearch.index.IndexWarmer;
import org.elasticsearch.indices.cache.request.IndicesRequestCache;
-import org.elasticsearch.search.SearchService;
import java.util.Arrays;
import java.util.Collections;
@@ -132,7 +132,7 @@ public final class IndexScopedSettings extends AbstractScopedSettings {
PrimaryShardAllocator.INDEX_RECOVERY_INITIAL_SHARDS_SETTING,
FsDirectoryService.INDEX_LOCK_FACTOR_SETTING,
EngineConfig.INDEX_CODEC_SETTING,
- SearchService.INDEX_NORMS_LOADING_SETTING,
+ IndexWarmer.INDEX_NORMS_LOADING_SETTING,
// this sucks but we can't really validate all the analyzers/similarity in here
Setting.groupSetting("index.similarity.", false, Setting.Scope.INDEX), // this allows similarity settings to be passed
Setting.groupSetting("index.analysis.", false, Setting.Scope.INDEX) // this allows analysis settings to be passed
diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/XContentType.java b/core/src/main/java/org/elasticsearch/common/xcontent/XContentType.java
index 329bad87265..296f9d2aedd 100644
--- a/core/src/main/java/org/elasticsearch/common/xcontent/XContentType.java
+++ b/core/src/main/java/org/elasticsearch/common/xcontent/XContentType.java
@@ -27,6 +27,7 @@ import org.elasticsearch.common.xcontent.smile.SmileXContent;
import org.elasticsearch.common.xcontent.yaml.YamlXContent;
import java.io.IOException;
+import java.util.Locale;
/**
* The content type of {@link org.elasticsearch.common.xcontent.XContent}.
@@ -38,7 +39,12 @@ public enum XContentType {
*/
JSON(0) {
@Override
- public String restContentType() {
+ protected String mediaTypeWithoutParameters() {
+ return "application/json";
+ }
+
+ @Override
+ public String mediaType() {
return "application/json; charset=UTF-8";
}
@@ -57,7 +63,7 @@ public enum XContentType {
*/
SMILE(1) {
@Override
- public String restContentType() {
+ protected String mediaTypeWithoutParameters() {
return "application/smile";
}
@@ -76,7 +82,7 @@ public enum XContentType {
*/
YAML(2) {
@Override
- public String restContentType() {
+ protected String mediaTypeWithoutParameters() {
return "application/yaml";
}
@@ -95,7 +101,7 @@ public enum XContentType {
*/
CBOR(3) {
@Override
- public String restContentType() {
+ protected String mediaTypeWithoutParameters() {
return "application/cbor";
}
@@ -108,31 +114,30 @@ public enum XContentType {
public XContent xContent() {
return CborXContent.cborXContent;
}
- },;
+ };
- public static XContentType fromRestContentType(String contentType) {
- if (contentType == null) {
+ public static XContentType fromMediaTypeOrFormat(String mediaType) {
+ if (mediaType == null) {
return null;
}
- if ("application/json".equals(contentType) || "json".equalsIgnoreCase(contentType)) {
+ for (XContentType type : values()) {
+ if (isSameMediaTypeAs(mediaType, type)) {
+ return type;
+ }
+ }
+ if(mediaType.toLowerCase(Locale.ROOT).startsWith("application/*")) {
return JSON;
}
- if ("application/smile".equals(contentType) || "smile".equalsIgnoreCase(contentType)) {
- return SMILE;
- }
-
- if ("application/yaml".equals(contentType) || "yaml".equalsIgnoreCase(contentType)) {
- return YAML;
- }
-
- if ("application/cbor".equals(contentType) || "cbor".equalsIgnoreCase(contentType)) {
- return CBOR;
- }
-
return null;
}
+ private static boolean isSameMediaTypeAs(String stringType, XContentType type) {
+ return type.mediaTypeWithoutParameters().equalsIgnoreCase(stringType) ||
+ stringType.toLowerCase(Locale.ROOT).startsWith(type.mediaTypeWithoutParameters().toLowerCase(Locale.ROOT) + ";") ||
+ type.shortName().equalsIgnoreCase(stringType);
+ }
+
private int index;
XContentType(int index) {
@@ -143,12 +148,16 @@ public enum XContentType {
return index;
}
- public abstract String restContentType();
+ public String mediaType() {
+ return mediaTypeWithoutParameters();
+ }
public abstract String shortName();
public abstract XContent xContent();
+ protected abstract String mediaTypeWithoutParameters();
+
public static XContentType readFrom(StreamInput in) throws IOException {
int index = in.readVInt();
for (XContentType contentType : values()) {
diff --git a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java
index 488938919db..ce083147117 100644
--- a/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java
+++ b/core/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java
@@ -910,7 +910,7 @@ public class ZenDiscovery extends AbstractLifecycleComponent implemen
activeNodes.add(localNode);
long joinsCounter = clusterJoinsCounter.get();
if (joinsCounter > 0) {
- logger.trace("adding local node to the list of active nodes who has previously joined the cluster (joins counter is [{}})", joinsCounter);
+ logger.trace("adding local node to the list of active nodes that have previously joined the cluster (joins counter is [{}])", joinsCounter);
joinedOnceActiveNodes.add(localNode);
}
}
diff --git a/core/src/main/java/org/elasticsearch/gateway/MetaStateService.java b/core/src/main/java/org/elasticsearch/gateway/MetaStateService.java
index 6f38b039fec..9ef09753c43 100644
--- a/core/src/main/java/org/elasticsearch/gateway/MetaStateService.java
+++ b/core/src/main/java/org/elasticsearch/gateway/MetaStateService.java
@@ -59,7 +59,7 @@ public class MetaStateService extends AbstractComponent {
public MetaStateService(Settings settings, NodeEnvironment nodeEnv) {
super(settings);
this.nodeEnv = nodeEnv;
- this.format = XContentType.fromRestContentType(settings.get(FORMAT_SETTING, "smile"));
+ this.format = XContentType.fromMediaTypeOrFormat(settings.get(FORMAT_SETTING, "smile"));
if (this.format == XContentType.SMILE) {
Map params = new HashMap<>();
params.put("binary", "true");
diff --git a/core/src/main/java/org/elasticsearch/index/IndexModule.java b/core/src/main/java/org/elasticsearch/index/IndexModule.java
index 4688fba5034..fada5f42830 100644
--- a/core/src/main/java/org/elasticsearch/index/IndexModule.java
+++ b/core/src/main/java/org/elasticsearch/index/IndexModule.java
@@ -38,6 +38,7 @@ import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.index.store.IndexStore;
import org.elasticsearch.index.store.IndexStoreConfig;
import org.elasticsearch.indices.cache.query.IndicesQueryCache;
+import org.elasticsearch.indices.fielddata.cache.IndicesFieldDataCache;
import org.elasticsearch.indices.mapper.MapperRegistry;
import java.io.IOException;
@@ -240,7 +241,7 @@ public final class IndexModule {
IndexSearcherWrapper newWrapper(final IndexService indexService);
}
- public IndexService newIndexService(NodeEnvironment environment, IndexService.ShardStoreDeleter shardStoreDeleter, NodeServicesProvider servicesProvider, MapperRegistry mapperRegistry,
+ public IndexService newIndexService(NodeEnvironment environment, IndexService.ShardStoreDeleter shardStoreDeleter, NodeServicesProvider servicesProvider, MapperRegistry mapperRegistry, IndicesFieldDataCache indicesFieldDataCache,
IndexingOperationListener... listeners) throws IOException {
IndexSearcherWrapperFactory searcherWrapperFactory = indexSearcherWrapper.get() == null ? (shard) -> null : indexSearcherWrapper.get();
IndexEventListener eventListener = freeze();
@@ -264,7 +265,7 @@ public final class IndexModule {
final BiFunction queryCacheProvider = queryCaches.get(queryCacheType);
final QueryCache queryCache = queryCacheProvider.apply(indexSettings, servicesProvider.getIndicesQueryCache());
return new IndexService(indexSettings, environment, new SimilarityService(indexSettings, similarities), shardStoreDeleter, analysisRegistry, engineFactory.get(),
- servicesProvider, queryCache, store, eventListener, searcherWrapperFactory, mapperRegistry, listeners);
+ servicesProvider, queryCache, store, eventListener, searcherWrapperFactory, mapperRegistry, indicesFieldDataCache, listeners);
}
}
diff --git a/core/src/main/java/org/elasticsearch/index/IndexService.java b/core/src/main/java/org/elasticsearch/index/IndexService.java
index 8c87b2b5606..86925c35a18 100644
--- a/core/src/main/java/org/elasticsearch/index/IndexService.java
+++ b/core/src/main/java/org/elasticsearch/index/IndexService.java
@@ -54,6 +54,7 @@ import org.elasticsearch.index.analysis.AnalysisService;
import org.elasticsearch.index.cache.IndexCache;
import org.elasticsearch.index.cache.bitset.BitsetFilterCache;
import org.elasticsearch.index.cache.query.QueryCache;
+import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.index.engine.EngineClosedException;
import org.elasticsearch.index.engine.EngineFactory;
import org.elasticsearch.index.fielddata.FieldDataType;
@@ -76,6 +77,7 @@ import org.elasticsearch.index.store.Store;
import org.elasticsearch.index.translog.Translog;
import org.elasticsearch.indices.AliasFilterParsingException;
import org.elasticsearch.indices.InvalidAliasNameException;
+import org.elasticsearch.indices.fielddata.cache.IndicesFieldDataCache;
import org.elasticsearch.indices.mapper.MapperRegistry;
import org.elasticsearch.threadpool.ThreadPool;
@@ -101,6 +103,7 @@ public final class IndexService extends AbstractIndexComponent implements IndexC
private final MapperService mapperService;
private final SimilarityService similarityService;
private final EngineFactory engineFactory;
+ private final IndexWarmer warmer;
private volatile Map shards = emptyMap();
private final AtomicBoolean closed = new AtomicBoolean(false);
private final AtomicBoolean deleted = new AtomicBoolean(false);
@@ -122,20 +125,22 @@ public final class IndexService extends AbstractIndexComponent implements IndexC
IndexEventListener eventListener,
IndexModule.IndexSearcherWrapperFactory wrapperFactory,
MapperRegistry mapperRegistry,
+ IndicesFieldDataCache indicesFieldDataCache,
IndexingOperationListener... listenersIn) throws IOException {
super(indexSettings);
this.indexSettings = indexSettings;
this.analysisService = registry.build(indexSettings);
this.similarityService = similarityService;
this.mapperService = new MapperService(indexSettings, analysisService, similarityService, mapperRegistry, IndexService.this::getQueryShardContext);
- this.indexFieldData = new IndexFieldDataService(indexSettings, nodeServicesProvider.getIndicesFieldDataCache(), nodeServicesProvider.getCircuitBreakerService(), mapperService);
+ this.indexFieldData = new IndexFieldDataService(indexSettings, indicesFieldDataCache, nodeServicesProvider.getCircuitBreakerService(), mapperService);
this.shardStoreDeleter = shardStoreDeleter;
this.eventListener = eventListener;
this.nodeEnv = nodeEnv;
this.nodeServicesProvider = nodeServicesProvider;
this.indexStore = indexStore;
indexFieldData.setListener(new FieldDataCacheListener(this));
- this.bitsetFilterCache = new BitsetFilterCache(indexSettings, nodeServicesProvider.getWarmer(), new BitsetCacheListener(this));
+ this.bitsetFilterCache = new BitsetFilterCache(indexSettings, new BitsetCacheListener(this));
+ this.warmer = new IndexWarmer(indexSettings.getSettings(), nodeServicesProvider.getThreadPool(), bitsetFilterCache.createListener(nodeServicesProvider.getThreadPool()));
this.indexCache = new IndexCache(indexSettings, queryCache, bitsetFilterCache);
this.engineFactory = engineFactory;
// initialize this last -- otherwise if the wrapper requires any other member to be non-null we fail with an NPE
@@ -310,11 +315,18 @@ public final class IndexService extends AbstractIndexComponent implements IndexC
// if we are on a shared FS we only own the shard (ie. we can safely delete it) if we are the primary.
final boolean canDeleteShardContent = IndexMetaData.isOnSharedFilesystem(indexSettings) == false ||
(primary && IndexMetaData.isOnSharedFilesystem(indexSettings));
+ final Engine.Warmer engineWarmer = (searcher, toLevel) -> {
+ IndexShard shard = getShardOrNull(shardId.getId());
+ if (shard != null) {
+ warmer.warm(searcher, shard, IndexService.this.indexSettings, toLevel);
+ }
+ };
+
store = new Store(shardId, this.indexSettings, indexStore.newDirectoryService(path), lock, new StoreCloseListener(shardId, canDeleteShardContent, () -> nodeServicesProvider.getIndicesQueryCache().onClose(shardId)));
if (useShadowEngine(primary, indexSettings)) {
- indexShard = new ShadowIndexShard(shardId, this.indexSettings, path, store, indexCache, mapperService, similarityService, indexFieldData, engineFactory, eventListener, searcherWrapper, nodeServicesProvider, searchSlowLog); // no indexing listeners - shadow engines don't index
+ indexShard = new ShadowIndexShard(shardId, this.indexSettings, path, store, indexCache, mapperService, similarityService, indexFieldData, engineFactory, eventListener, searcherWrapper, nodeServicesProvider, searchSlowLog, engineWarmer); // no indexing listeners - shadow engines don't index
} else {
- indexShard = new IndexShard(shardId, this.indexSettings, path, store, indexCache, mapperService, similarityService, indexFieldData, engineFactory, eventListener, searcherWrapper, nodeServicesProvider, searchSlowLog, listeners);
+ indexShard = new IndexShard(shardId, this.indexSettings, path, store, indexCache, mapperService, similarityService, indexFieldData, engineFactory, eventListener, searcherWrapper, nodeServicesProvider, searchSlowLog, engineWarmer, listeners);
}
eventListener.indexShardStateChanged(indexShard, null, indexShard.state(), "shard created");
eventListener.afterIndexShardCreated(indexShard);
diff --git a/core/src/main/java/org/elasticsearch/index/IndexWarmer.java b/core/src/main/java/org/elasticsearch/index/IndexWarmer.java
new file mode 100644
index 00000000000..9fabc8efc40
--- /dev/null
+++ b/core/src/main/java/org/elasticsearch/index/IndexWarmer.java
@@ -0,0 +1,292 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.index;
+
+import com.carrotsearch.hppc.ObjectHashSet;
+import com.carrotsearch.hppc.ObjectSet;
+import com.carrotsearch.hppc.cursors.ObjectCursor;
+import org.apache.lucene.index.IndexOptions;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.elasticsearch.common.component.AbstractComponent;
+import org.elasticsearch.common.settings.Setting;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.index.engine.Engine;
+import org.elasticsearch.index.fielddata.FieldDataType;
+import org.elasticsearch.index.fielddata.IndexFieldData;
+import org.elasticsearch.index.fielddata.IndexFieldDataService;
+import org.elasticsearch.index.mapper.DocumentMapper;
+import org.elasticsearch.index.mapper.FieldMapper;
+import org.elasticsearch.index.mapper.MappedFieldType;
+import org.elasticsearch.index.mapper.MapperService;
+import org.elasticsearch.index.shard.IndexShard;
+import org.elasticsearch.index.shard.IndexShardState;
+import org.elasticsearch.threadpool.ThreadPool;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ */
+public final class IndexWarmer extends AbstractComponent {
+
+ public static final Setting INDEX_NORMS_LOADING_SETTING = new Setting<>("index.norms.loading",
+ MappedFieldType.Loading.LAZY.toString(), (s) -> MappedFieldType.Loading.parse(s, MappedFieldType.Loading.LAZY),
+ false, Setting.Scope.INDEX);
+ private final List listeners;
+
+ IndexWarmer(Settings settings, ThreadPool threadPool, Listener... listeners) {
+ super(settings);
+ ArrayList list = new ArrayList<>();
+ final Executor executor = threadPool.executor(ThreadPool.Names.WARMER);
+ list.add(new NormsWarmer(executor));
+ list.add(new FieldDataWarmer(executor));
+ for (Listener listener : listeners) {
+ list.add(listener);
+ }
+ this.listeners = Collections.unmodifiableList(list);
+ }
+
+ void warm(Engine.Searcher searcher, IndexShard shard, IndexSettings settings, boolean isTopReader) {
+ if (shard.state() == IndexShardState.CLOSED) {
+ return;
+ }
+ if (settings.isWarmerEnabled() == false) {
+ return;
+ }
+ if (logger.isTraceEnabled()) {
+ if (isTopReader) {
+ logger.trace("{} top warming [{}]", shard.shardId(), searcher.reader());
+ } else {
+ logger.trace("{} warming [{}]", shard.shardId(), searcher.reader());
+ }
+ }
+ shard.warmerService().onPreWarm();
+ long time = System.nanoTime();
+ final List terminationHandles = new ArrayList<>();
+ // get a handle on pending tasks
+ for (final Listener listener : listeners) {
+ if (isTopReader) {
+ terminationHandles.add(listener.warmTopReader(shard, searcher));
+ } else {
+ terminationHandles.add(listener.warmNewReaders(shard, searcher));
+ }
+ }
+ // wait for termination
+ for (TerminationHandle terminationHandle : terminationHandles) {
+ try {
+ terminationHandle.awaitTermination();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ if (isTopReader) {
+ logger.warn("top warming has been interrupted", e);
+ } else {
+ logger.warn("warming has been interrupted", e);
+ }
+ break;
+ }
+ }
+ long took = System.nanoTime() - time;
+ shard.warmerService().onPostWarm(took);
+ if (shard.warmerService().logger().isTraceEnabled()) {
+ if (isTopReader) {
+ shard.warmerService().logger().trace("top warming took [{}]", new TimeValue(took, TimeUnit.NANOSECONDS));
+ } else {
+ shard.warmerService().logger().trace("warming took [{}]", new TimeValue(took, TimeUnit.NANOSECONDS));
+ }
+ }
+ }
+
+ /** A handle on the execution of warm-up action. */
+ public interface TerminationHandle {
+
+ TerminationHandle NO_WAIT = () -> {};
+
+ /** Wait until execution of the warm-up action completes. */
+ void awaitTermination() throws InterruptedException;
+ }
+ public interface Listener {
+ /** Queue tasks to warm-up the given segments and return handles that allow to wait for termination of the
+ * execution of those tasks. */
+ TerminationHandle warmNewReaders(IndexShard indexShard, Engine.Searcher searcher);
+
+ TerminationHandle warmTopReader(IndexShard indexShard, Engine.Searcher searcher);
+ }
+
+ private static class NormsWarmer implements IndexWarmer.Listener {
+ private final Executor executor;
+ public NormsWarmer(Executor executor) {
+ this.executor = executor;
+ }
+ @Override
+ public TerminationHandle warmNewReaders(final IndexShard indexShard, final Engine.Searcher searcher) {
+ final MappedFieldType.Loading defaultLoading = indexShard.indexSettings().getValue(INDEX_NORMS_LOADING_SETTING);
+ final MapperService mapperService = indexShard.mapperService();
+ final ObjectSet warmUp = new ObjectHashSet<>();
+ for (DocumentMapper docMapper : mapperService.docMappers(false)) {
+ for (FieldMapper fieldMapper : docMapper.mappers()) {
+ final String indexName = fieldMapper.fieldType().name();
+ MappedFieldType.Loading normsLoading = fieldMapper.fieldType().normsLoading();
+ if (normsLoading == null) {
+ normsLoading = defaultLoading;
+ }
+ if (fieldMapper.fieldType().indexOptions() != IndexOptions.NONE && !fieldMapper.fieldType().omitNorms()
+ && normsLoading == MappedFieldType.Loading.EAGER) {
+ warmUp.add(indexName);
+ }
+ }
+ }
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ // Norms loading may be I/O intensive but is not CPU intensive, so we execute it in a single task
+ executor.execute(() -> {
+ try {
+ for (ObjectCursor stringObjectCursor : warmUp) {
+ final String indexName = stringObjectCursor.value;
+ final long start = System.nanoTime();
+ for (final LeafReaderContext ctx : searcher.reader().leaves()) {
+ final NumericDocValues values = ctx.reader().getNormValues(indexName);
+ if (values != null) {
+ values.get(0);
+ }
+ }
+ if (indexShard.warmerService().logger().isTraceEnabled()) {
+ indexShard.warmerService().logger().trace("warmed norms for [{}], took [{}]", indexName,
+ TimeValue.timeValueNanos(System.nanoTime() - start));
+ }
+ }
+ } catch (Throwable t) {
+ indexShard.warmerService().logger().warn("failed to warm-up norms", t);
+ } finally {
+ latch.countDown();
+ }
+ });
+
+ return () -> latch.await();
+ }
+
+ @Override
+ public TerminationHandle warmTopReader(IndexShard indexShard, final Engine.Searcher searcher) {
+ return TerminationHandle.NO_WAIT;
+ }
+ }
+
+ private static class FieldDataWarmer implements IndexWarmer.Listener {
+
+ private final Executor executor;
+ public FieldDataWarmer(Executor executor) {
+ this.executor = executor;
+ }
+
+ @Override
+ public TerminationHandle warmNewReaders(final IndexShard indexShard, final Engine.Searcher searcher) {
+ final MapperService mapperService = indexShard.mapperService();
+ final Map warmUp = new HashMap<>();
+ for (DocumentMapper docMapper : mapperService.docMappers(false)) {
+ for (FieldMapper fieldMapper : docMapper.mappers()) {
+ final FieldDataType fieldDataType = fieldMapper.fieldType().fieldDataType();
+ final String indexName = fieldMapper.fieldType().name();
+ if (fieldDataType == null) {
+ continue;
+ }
+ if (fieldDataType.getLoading() == MappedFieldType.Loading.LAZY) {
+ continue;
+ }
+
+ if (warmUp.containsKey(indexName)) {
+ continue;
+ }
+ warmUp.put(indexName, fieldMapper.fieldType());
+ }
+ }
+ final IndexFieldDataService indexFieldDataService = indexShard.indexFieldDataService();
+ final CountDownLatch latch = new CountDownLatch(searcher.reader().leaves().size() * warmUp.size());
+ for (final LeafReaderContext ctx : searcher.reader().leaves()) {
+ for (final MappedFieldType fieldType : warmUp.values()) {
+ executor.execute(() -> {
+ try {
+ final long start = System.nanoTime();
+ indexFieldDataService.getForField(fieldType).load(ctx);
+ if (indexShard.warmerService().logger().isTraceEnabled()) {
+ indexShard.warmerService().logger().trace("warmed fielddata for [{}], took [{}]", fieldType.name(),
+ TimeValue.timeValueNanos(System.nanoTime() - start));
+ }
+ } catch (Throwable t) {
+ indexShard.warmerService().logger().warn("failed to warm-up fielddata for [{}]", t, fieldType.name());
+ } finally {
+ latch.countDown();
+ }
+ });
+ }
+ }
+ return () -> latch.await();
+ }
+
+ @Override
+ public TerminationHandle warmTopReader(final IndexShard indexShard, final Engine.Searcher searcher) {
+ final MapperService mapperService = indexShard.mapperService();
+ final Map warmUpGlobalOrdinals = new HashMap<>();
+ for (DocumentMapper docMapper : mapperService.docMappers(false)) {
+ for (FieldMapper fieldMapper : docMapper.mappers()) {
+ final FieldDataType fieldDataType = fieldMapper.fieldType().fieldDataType();
+ final String indexName = fieldMapper.fieldType().name();
+ if (fieldDataType == null) {
+ continue;
+ }
+ if (fieldDataType.getLoading() != MappedFieldType.Loading.EAGER_GLOBAL_ORDINALS) {
+ continue;
+ }
+ if (warmUpGlobalOrdinals.containsKey(indexName)) {
+ continue;
+ }
+ warmUpGlobalOrdinals.put(indexName, fieldMapper.fieldType());
+ }
+ }
+ final IndexFieldDataService indexFieldDataService = indexShard.indexFieldDataService();
+ final CountDownLatch latch = new CountDownLatch(warmUpGlobalOrdinals.size());
+ for (final MappedFieldType fieldType : warmUpGlobalOrdinals.values()) {
+ executor.execute(() -> {
+ try {
+ final long start = System.nanoTime();
+ IndexFieldData.Global ifd = indexFieldDataService.getForField(fieldType);
+ ifd.loadGlobal(searcher.getDirectoryReader());
+ if (indexShard.warmerService().logger().isTraceEnabled()) {
+ indexShard.warmerService().logger().trace("warmed global ordinals for [{}], took [{}]", fieldType.name(),
+ TimeValue.timeValueNanos(System.nanoTime() - start));
+ }
+ } catch (Throwable t) {
+ indexShard.warmerService().logger().warn("failed to warm-up global ordinals for [{}]", t, fieldType.name());
+ } finally {
+ latch.countDown();
+ }
+ });
+ }
+ return () -> latch.await();
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/elasticsearch/index/NodeServicesProvider.java b/core/src/main/java/org/elasticsearch/index/NodeServicesProvider.java
index 36d3fdc5dce..4bb25214708 100644
--- a/core/src/main/java/org/elasticsearch/index/NodeServicesProvider.java
+++ b/core/src/main/java/org/elasticsearch/index/NodeServicesProvider.java
@@ -20,13 +20,10 @@
package org.elasticsearch.index;
import org.elasticsearch.client.Client;
-import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.util.BigArrays;
-import org.elasticsearch.indices.IndicesWarmer;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.indices.cache.query.IndicesQueryCache;
-import org.elasticsearch.indices.fielddata.cache.IndicesFieldDataCache;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.threadpool.ThreadPool;
@@ -40,24 +37,20 @@ public final class NodeServicesProvider {
private final ThreadPool threadPool;
private final IndicesQueryCache indicesQueryCache;
- private final IndicesWarmer warmer;
private final BigArrays bigArrays;
private final Client client;
private final IndicesQueriesRegistry indicesQueriesRegistry;
private final ScriptService scriptService;
- private final IndicesFieldDataCache indicesFieldDataCache;
private final CircuitBreakerService circuitBreakerService;
@Inject
- public NodeServicesProvider(ThreadPool threadPool, IndicesQueryCache indicesQueryCache, @Nullable IndicesWarmer warmer, BigArrays bigArrays, Client client, ScriptService scriptService, IndicesQueriesRegistry indicesQueriesRegistry, IndicesFieldDataCache indicesFieldDataCache, CircuitBreakerService circuitBreakerService) {
+ public NodeServicesProvider(ThreadPool threadPool, IndicesQueryCache indicesQueryCache, BigArrays bigArrays, Client client, ScriptService scriptService, IndicesQueriesRegistry indicesQueriesRegistry, CircuitBreakerService circuitBreakerService) {
this.threadPool = threadPool;
this.indicesQueryCache = indicesQueryCache;
- this.warmer = warmer;
this.bigArrays = bigArrays;
this.client = client;
this.indicesQueriesRegistry = indicesQueriesRegistry;
this.scriptService = scriptService;
- this.indicesFieldDataCache = indicesFieldDataCache;
this.circuitBreakerService = circuitBreakerService;
}
@@ -69,10 +62,6 @@ public final class NodeServicesProvider {
return indicesQueryCache;
}
- public IndicesWarmer getWarmer() {
- return warmer;
- }
-
public BigArrays getBigArrays() { return bigArrays; }
public Client getClient() {
@@ -87,10 +76,6 @@ public final class NodeServicesProvider {
return scriptService;
}
- public IndicesFieldDataCache getIndicesFieldDataCache() {
- return indicesFieldDataCache;
- }
-
public CircuitBreakerService getCircuitBreakerService() {
return circuitBreakerService;
}
diff --git a/core/src/main/java/org/elasticsearch/index/cache/bitset/BitsetFilterCache.java b/core/src/main/java/org/elasticsearch/index/cache/bitset/BitsetFilterCache.java
index 52858416ee0..7d5540b6224 100644
--- a/core/src/main/java/org/elasticsearch/index/cache/bitset/BitsetFilterCache.java
+++ b/core/src/main/java/org/elasticsearch/index/cache/bitset/BitsetFilterCache.java
@@ -48,8 +48,9 @@ import org.elasticsearch.index.mapper.object.ObjectMapper;
import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.shard.ShardUtils;
-import org.elasticsearch.indices.IndicesWarmer;
-import org.elasticsearch.indices.IndicesWarmer.TerminationHandle;
+import org.elasticsearch.index.IndexWarmer;
+import org.elasticsearch.index.IndexWarmer.TerminationHandle;
+import org.elasticsearch.threadpool.ThreadPool;
import java.io.Closeable;
import java.io.IOException;
@@ -74,22 +75,20 @@ public final class BitsetFilterCache extends AbstractIndexComponent implements L
private final boolean loadRandomAccessFiltersEagerly;
private final Cache