
org.elasticsearch.indices.TimestampFieldMapperService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch - Open Source, Distributed, RESTful Search Engine
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.indices;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterStateApplier;
import org.elasticsearch.cluster.metadata.DataStream;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.mapper.DateFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.shard.IndexLongFieldRange;
import org.elasticsearch.node.Node;
import org.elasticsearch.threadpool.ThreadPool;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import static org.elasticsearch.common.util.concurrent.EsExecutors.daemonThreadFactory;
import static org.elasticsearch.core.Strings.format;
/**
* Tracks the mapping of the {@code @timestamp} field of immutable indices that expose their timestamp range in their index metadata.
* Coordinating nodes do not have (easy) access to mappings for all indices, so we extract the type of this one field from the mapping here.
*/
public class TimestampFieldMapperService extends AbstractLifecycleComponent implements ClusterStateApplier {
private static final Logger logger = LogManager.getLogger(TimestampFieldMapperService.class);
private final IndicesService indicesService;
private final ExecutorService executor; // single thread to construct mapper services async as needed
/**
* The type of the {@code @timestamp} field keyed by index. Futures may be completed with {@code null} to indicate that there is
* no usable {@code @timestamp} field.
*/
private final Map> fieldTypesByIndex = ConcurrentCollections.newConcurrentMap();
public TimestampFieldMapperService(Settings settings, ThreadPool threadPool, IndicesService indicesService) {
this.indicesService = indicesService;
final String nodeName = Objects.requireNonNull(Node.NODE_NAME_SETTING.get(settings));
final String threadName = "TimestampFieldMapperService#updateTask";
executor = EsExecutors.newScaling(
nodeName + "/" + threadName,
0,
1,
0,
TimeUnit.MILLISECONDS,
true,
daemonThreadFactory(nodeName, threadName),
threadPool.getThreadContext()
);
}
@Override
protected void doStart() {}
@Override
protected void doStop() {
ThreadPool.terminate(executor, 10, TimeUnit.SECONDS);
}
@Override
protected void doClose() {}
@Override
public void applyClusterState(ClusterChangedEvent event) {
final Metadata metadata = event.state().metadata();
final Map indices = metadata.indices();
if (indices == event.previousState().metadata().indices()) {
return;
}
// clear out mappers for indices that no longer exist or whose timestamp range is no longer known
fieldTypesByIndex.keySet().removeIf(index -> hasUsefulTimestampField(metadata.index(index)) == false);
// capture mappers for indices that do exist
for (IndexMetadata indexMetadata : indices.values()) {
final Index index = indexMetadata.getIndex();
if (hasUsefulTimestampField(indexMetadata) && fieldTypesByIndex.containsKey(index) == false) {
logger.trace("computing timestamp mapping for {}", index);
final PlainActionFuture future = new PlainActionFuture<>();
fieldTypesByIndex.put(index, future);
final IndexService indexService = indicesService.indexService(index);
if (indexService == null) {
logger.trace("computing timestamp mapping for {} async", index);
executor.execute(new AbstractRunnable() {
@Override
public void onFailure(Exception e) {
logger.debug(() -> format("failed to compute mapping for %s", index), e);
future.onResponse(null); // no need to propagate a failure to create the mapper service to searches
}
@Override
protected void doRun() throws Exception {
try (MapperService mapperService = indicesService.createIndexMapperServiceForValidation(indexMetadata)) {
mapperService.merge(indexMetadata, MapperService.MergeReason.MAPPING_RECOVERY);
logger.trace("computed timestamp field mapping for {}", index);
future.onResponse(fromMapperService(mapperService));
}
}
});
} else {
logger.trace("computing timestamp mapping for {} using existing index service", index);
try {
future.onResponse(fromMapperService(indexService.mapperService()));
} catch (Exception e) {
assert false : e;
future.onResponse(null);
}
}
}
}
}
private static boolean hasUsefulTimestampField(IndexMetadata indexMetadata) {
if (indexMetadata == null) {
return false;
}
if (indexMetadata.hasTimeSeriesTimestampRange()) {
// Tsdb indices have @timestamp field and index.time_series.start_time / index.time_series.end_time range
return true;
}
final IndexLongFieldRange timestampRange = indexMetadata.getTimestampRange();
return timestampRange.isComplete() && timestampRange != IndexLongFieldRange.UNKNOWN;
}
private static DateFieldMapper.DateFieldType fromMapperService(MapperService mapperService) {
final MappedFieldType mappedFieldType = mapperService.fieldType(DataStream.TimestampField.FIXED_TIMESTAMP_FIELD);
if (mappedFieldType instanceof DateFieldMapper.DateFieldType) {
return (DateFieldMapper.DateFieldType) mappedFieldType;
} else {
return null;
}
}
/**
* @return the field type of the {@code @timestamp} field of the given index, or {@code null} if:
* - the index is not found,
* - the field is not found,
* - the mapping is not known yet, or
* - the field is not a timestamp field.
*/
@Nullable
public DateFieldMapper.DateFieldType getTimestampFieldType(Index index) {
final PlainActionFuture future = fieldTypesByIndex.get(index);
if (future == null || future.isDone() == false) {
return null;
}
// call non-blocking actionResult() as we could be on a network or scheduler thread which we must not block
return future.actionResult();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy