org.elasticsearch.cluster.metadata.IndexMetadata 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.cluster.metadata;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.TransportVersion;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.indices.rollover.RolloverInfo;
import org.elasticsearch.action.support.ActiveShardCount;
import org.elasticsearch.cluster.Diff;
import org.elasticsearch.cluster.Diffable;
import org.elasticsearch.cluster.DiffableUtils;
import org.elasticsearch.cluster.block.ClusterBlock;
import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.node.DiscoveryNodeFilters;
import org.elasticsearch.cluster.routing.IndexRouting;
import org.elasticsearch.cluster.routing.allocation.DataTier;
import org.elasticsearch.cluster.routing.allocation.IndexMetadataUpdater;
import org.elasticsearch.cluster.routing.allocation.decider.DiskThresholdDecider;
import org.elasticsearch.cluster.routing.allocation.decider.ShardsLimitAllocationDecider;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.Maps;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParserUtils;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.gateway.MetadataStateFormat;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.mapper.DateFieldMapper;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.seqno.SequenceNumbers;
import org.elasticsearch.index.shard.IndexLongFieldRange;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.shard.ShardLongFieldRange;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.snapshots.SearchableSnapshotsSettings;
import org.elasticsearch.xcontent.ToXContent;
import org.elasticsearch.xcontent.ToXContentFragment;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;
import org.elasticsearch.xcontent.XContentParser;
import java.io.IOException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.OptionalDouble;
import java.util.OptionalLong;
import java.util.Set;
import java.util.function.Function;
import static org.elasticsearch.cluster.metadata.Metadata.CONTEXT_MODE_PARAM;
import static org.elasticsearch.cluster.metadata.Metadata.DEDUPLICATED_MAPPINGS_PARAM;
import static org.elasticsearch.cluster.node.DiscoveryNodeFilters.OpType.AND;
import static org.elasticsearch.cluster.node.DiscoveryNodeFilters.OpType.OR;
import static org.elasticsearch.cluster.node.DiscoveryNodeFilters.validateIpValue;
import static org.elasticsearch.common.settings.Settings.readSettingsFromStream;
import static org.elasticsearch.snapshots.SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_PARTIAL_SETTING_KEY;
public class IndexMetadata implements Diffable, ToXContentFragment {
private static final Logger logger = LogManager.getLogger(IndexMetadata.class);
public static final ClusterBlock INDEX_READ_ONLY_BLOCK = new ClusterBlock(
5,
"index read-only (api)",
false,
false,
false,
RestStatus.FORBIDDEN,
EnumSet.of(ClusterBlockLevel.WRITE, ClusterBlockLevel.METADATA_WRITE)
);
public static final ClusterBlock INDEX_READ_BLOCK = new ClusterBlock(
7,
"index read (api)",
false,
false,
false,
RestStatus.FORBIDDEN,
EnumSet.of(ClusterBlockLevel.READ)
);
public static final ClusterBlock INDEX_WRITE_BLOCK = new ClusterBlock(
8,
"index write (api)",
false,
false,
false,
RestStatus.FORBIDDEN,
EnumSet.of(ClusterBlockLevel.WRITE)
);
public static final ClusterBlock INDEX_METADATA_BLOCK = new ClusterBlock(
9,
"index metadata (api)",
false,
false,
false,
RestStatus.FORBIDDEN,
EnumSet.of(ClusterBlockLevel.METADATA_WRITE, ClusterBlockLevel.METADATA_READ)
);
public static final ClusterBlock INDEX_READ_ONLY_ALLOW_DELETE_BLOCK = new ClusterBlock(
12,
"disk usage exceeded flood-stage watermark, index has read-only-allow-delete block",
false,
false,
true,
RestStatus.TOO_MANY_REQUESTS,
EnumSet.of(ClusterBlockLevel.WRITE)
);
// TODO: refactor this method after adding more downsampling metadata
public boolean isDownsampledIndex() {
final String sourceIndex = settings.get(IndexMetadata.INDEX_DOWNSAMPLE_SOURCE_NAME_KEY);
final String indexDownsamplingStatus = settings.get(IndexMetadata.INDEX_DOWNSAMPLE_STATUS_KEY);
final boolean downsamplingSuccess = DownsampleTaskStatus.SUCCESS.name()
.toLowerCase(Locale.ROOT)
.equals(indexDownsamplingStatus != null ? indexDownsamplingStatus.toLowerCase(Locale.ROOT) : DownsampleTaskStatus.UNKNOWN);
return Strings.isNullOrEmpty(sourceIndex) == false && downsamplingSuccess;
}
public enum State implements Writeable {
OPEN((byte) 0),
CLOSE((byte) 1);
private final byte id;
State(byte id) {
this.id = id;
}
public byte id() {
return this.id;
}
public static State fromId(byte id) {
if (id == 0) {
return OPEN;
} else if (id == 1) {
return CLOSE;
}
throw new IllegalStateException("No state match for id [" + id + "]");
}
public static State readFrom(StreamInput in) throws IOException {
byte id = in.readByte();
return switch (id) {
case 0 -> OPEN;
case 1 -> CLOSE;
default -> throw new IllegalStateException("No state match for id [" + id + "]");
};
}
public static State fromString(String state) {
if ("open".equals(state)) {
return OPEN;
} else if ("close".equals(state)) {
return CLOSE;
}
throw new IllegalStateException("No state match for [" + state + "]");
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeByte(id);
}
}
static Setting buildNumberOfShardsSetting() {
/* This is a safety limit that should only be exceeded in very rare and special cases. The assumption is that
* 99% of the users have less than 1024 shards per index. We also make it a hard check that requires restart of nodes
* if a cluster should allow to create more than 1024 shards per index. NOTE: this does not limit the number of shards
* per cluster. this also prevents creating stuff like a new index with millions of shards by accident which essentially
* kills the entire cluster with OOM on the spot.*/
final int maxNumShards = Integer.parseInt(System.getProperty("es.index.max_number_of_shards", "1024"));
if (maxNumShards < 1) {
throw new IllegalArgumentException("es.index.max_number_of_shards must be > 0");
}
return Setting.intSetting(SETTING_NUMBER_OF_SHARDS, 1, 1, maxNumShards, Property.IndexScope, Property.Final);
}
public static final String INDEX_SETTING_PREFIX = "index.";
public static final String SETTING_NUMBER_OF_SHARDS = "index.number_of_shards";
public static final Setting INDEX_NUMBER_OF_SHARDS_SETTING = buildNumberOfShardsSetting();
public static final String SETTING_NUMBER_OF_REPLICAS = "index.number_of_replicas";
public static final Setting INDEX_NUMBER_OF_REPLICAS_SETTING = Setting.intSetting(
SETTING_NUMBER_OF_REPLICAS,
1,
0,
Property.Dynamic,
Property.IndexScope
);
public static final String SETTING_ROUTING_PARTITION_SIZE = "index.routing_partition_size";
public static final Setting INDEX_ROUTING_PARTITION_SIZE_SETTING = Setting.intSetting(
SETTING_ROUTING_PARTITION_SIZE,
1,
1,
Property.Final,
Property.IndexScope
);
@SuppressWarnings("Convert2Diamond") // since some IntelliJs mysteriously report an error if an is replaced with <> here:
public static final Setting INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING = Setting.intSetting(
"index.number_of_routing_shards",
INDEX_NUMBER_OF_SHARDS_SETTING,
1,
new Setting.Validator() {
@Override
public void validate(final Integer value) {
}
@Override
public void validate(final Integer numRoutingShards, final Map, Object> settings) {
int numShards = (int) settings.get(INDEX_NUMBER_OF_SHARDS_SETTING);
if (numRoutingShards < numShards) {
throw new IllegalArgumentException(
"index.number_of_routing_shards [" + numRoutingShards + "] must be >= index.number_of_shards [" + numShards + "]"
);
}
getRoutingFactor(numShards, numRoutingShards);
}
@Override
public Iterator> settings() {
final List> settings = List.of(INDEX_NUMBER_OF_SHARDS_SETTING);
return settings.iterator();
}
},
Property.IndexScope
);
public static final String SETTING_AUTO_EXPAND_REPLICAS = "index.auto_expand_replicas";
public static final Setting INDEX_AUTO_EXPAND_REPLICAS_SETTING = AutoExpandReplicas.SETTING;
public enum APIBlock implements Writeable {
READ_ONLY("read_only", INDEX_READ_ONLY_BLOCK),
READ("read", INDEX_READ_BLOCK),
WRITE("write", INDEX_WRITE_BLOCK),
METADATA("metadata", INDEX_METADATA_BLOCK),
READ_ONLY_ALLOW_DELETE("read_only_allow_delete", INDEX_READ_ONLY_ALLOW_DELETE_BLOCK);
final String name;
final String settingName;
final Setting setting;
final ClusterBlock block;
APIBlock(String name, ClusterBlock block) {
this.name = name;
this.settingName = "index.blocks." + name;
this.setting = Setting.boolSetting(settingName, false, Property.Dynamic, Property.IndexScope);
this.block = block;
}
public String settingName() {
return settingName;
}
public Setting setting() {
return setting;
}
public ClusterBlock getBlock() {
return block;
}
public static APIBlock fromName(String name) {
for (APIBlock block : APIBlock.values()) {
if (block.name.equals(name)) {
return block;
}
}
throw new IllegalArgumentException("No block found with name " + name);
}
public static APIBlock fromSetting(String settingName) {
for (APIBlock block : APIBlock.values()) {
if (block.settingName.equals(settingName)) {
return block;
}
}
throw new IllegalArgumentException("No block found with setting name " + settingName);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(ordinal());
}
public static APIBlock readFrom(StreamInput input) throws IOException {
return APIBlock.values()[input.readVInt()];
}
}
public static final String SETTING_READ_ONLY = APIBlock.READ_ONLY.settingName();
public static final Setting INDEX_READ_ONLY_SETTING = APIBlock.READ_ONLY.setting();
public static final String SETTING_BLOCKS_READ = APIBlock.READ.settingName();
public static final Setting INDEX_BLOCKS_READ_SETTING = APIBlock.READ.setting();
public static final String SETTING_BLOCKS_WRITE = APIBlock.WRITE.settingName();
public static final Setting INDEX_BLOCKS_WRITE_SETTING = APIBlock.WRITE.setting();
public static final String SETTING_BLOCKS_METADATA = APIBlock.METADATA.settingName();
public static final Setting INDEX_BLOCKS_METADATA_SETTING = APIBlock.METADATA.setting();
public static final String SETTING_READ_ONLY_ALLOW_DELETE = APIBlock.READ_ONLY_ALLOW_DELETE.settingName();
public static final Setting INDEX_BLOCKS_READ_ONLY_ALLOW_DELETE_SETTING = APIBlock.READ_ONLY_ALLOW_DELETE.setting();
public static final String SETTING_VERSION_CREATED = "index.version.created";
public static final Setting SETTING_INDEX_VERSION_CREATED = new Setting<>(
SETTING_VERSION_CREATED,
Integer.toString(IndexVersion.ZERO.id()),
s -> IndexVersion.fromId(Integer.parseInt(s)),
Property.IndexScope,
Property.PrivateIndex
);
public static final String SETTING_VERSION_CREATED_STRING = "index.version.created_string";
public static final String SETTING_CREATION_DATE = "index.creation_date";
/**
* These internal settings are no longer added to new indices. They are deprecated but still defined
* to retain compatibility with old indexes. TODO: remove in 9.0.
*/
@Deprecated
public static final String SETTING_VERSION_UPGRADED = "index.version.upgraded";
@Deprecated
public static final String SETTING_VERSION_UPGRADED_STRING = "index.version.upgraded_string";
public static final String SETTING_VERSION_COMPATIBILITY = "index.version.compatibility";
/**
* See {@link #getCompatibilityVersion()}
*/
public static final Setting SETTING_INDEX_VERSION_COMPATIBILITY = new Setting<>(
SETTING_VERSION_COMPATIBILITY,
SETTING_INDEX_VERSION_CREATED, // fall back to index.version.created
s -> IndexVersion.fromId(Integer.parseInt(s)),
new Setting.Validator<>() {
@Override
public void validate(final IndexVersion compatibilityVersion) {
}
@Override
public void validate(final IndexVersion compatibilityVersion, final Map, Object> settings) {
IndexVersion createdVersion = (IndexVersion) settings.get(SETTING_INDEX_VERSION_CREATED);
if (compatibilityVersion.before(createdVersion)) {
throw new IllegalArgumentException(
SETTING_VERSION_COMPATIBILITY
+ " ["
+ compatibilityVersion
+ "] must be >= "
+ SETTING_VERSION_CREATED
+ " ["
+ createdVersion
+ "]"
);
}
}
@Override
public Iterator> settings() {
final List> settings = List.of(SETTING_INDEX_VERSION_CREATED);
return settings.iterator();
}
},
Property.IndexScope,
Property.PrivateIndex
);
/**
* The user provided name for an index. This is the plain string provided by the user when the index was created.
* It might still contain date math expressions etc. (added in 5.0)
*/
public static final String SETTING_INDEX_PROVIDED_NAME = "index.provided_name";
public static final String SETTING_PRIORITY = "index.priority";
public static final Setting INDEX_PRIORITY_SETTING = Setting.intSetting(
"index.priority",
1,
0,
Property.Dynamic,
Property.IndexScope
);
public static final String SETTING_CREATION_DATE_STRING = "index.creation_date_string";
public static final String SETTING_INDEX_UUID = "index.uuid";
public static final String SETTING_HISTORY_UUID = "index.history.uuid";
public static final String SETTING_DATA_PATH = "index.data_path";
public static final Setting INDEX_DATA_PATH_SETTING = new Setting<>(
SETTING_DATA_PATH,
"",
Function.identity(),
Property.IndexScope,
Property.DeprecatedWarning
);
public static final String INDEX_UUID_NA_VALUE = "_na_";
public static final String INDEX_ROUTING_REQUIRE_GROUP_PREFIX = "index.routing.allocation.require";
public static final String INDEX_ROUTING_INCLUDE_GROUP_PREFIX = "index.routing.allocation.include";
public static final String INDEX_ROUTING_EXCLUDE_GROUP_PREFIX = "index.routing.allocation.exclude";
public static final Setting.AffixSetting> INDEX_ROUTING_REQUIRE_GROUP_SETTING = Setting.prefixKeySetting(
INDEX_ROUTING_REQUIRE_GROUP_PREFIX + ".",
key -> Setting.stringListSetting(key, value -> validateIpValue(key, value), Property.Dynamic, Property.IndexScope)
);
public static final Setting.AffixSetting> INDEX_ROUTING_INCLUDE_GROUP_SETTING = Setting.prefixKeySetting(
INDEX_ROUTING_INCLUDE_GROUP_PREFIX + ".",
key -> Setting.stringListSetting(key, value -> validateIpValue(key, value), Property.Dynamic, Property.IndexScope)
);
public static final Setting.AffixSetting> INDEX_ROUTING_EXCLUDE_GROUP_SETTING = Setting.prefixKeySetting(
INDEX_ROUTING_EXCLUDE_GROUP_PREFIX + ".",
key -> Setting.stringListSetting(key, value -> validateIpValue(key, value), Property.Dynamic, Property.IndexScope)
);
public static final Setting.AffixSetting> INDEX_ROUTING_INITIAL_RECOVERY_GROUP_SETTING = Setting.prefixKeySetting(
"index.routing.allocation.initial_recovery.",
key -> Setting.stringListSetting(key)
);
/**
* The number of active shard copies to check for before proceeding with a write operation.
*/
public static final Setting SETTING_WAIT_FOR_ACTIVE_SHARDS = new Setting<>(
"index.write.wait_for_active_shards",
"1",
ActiveShardCount::parseString,
Setting.Property.Dynamic,
Setting.Property.IndexScope
);
public static final String SETTING_INDEX_HIDDEN = "index.hidden";
/**
* Whether the index is considered hidden or not. A hidden index will not be resolved in
* normal wildcard searches unless explicitly allowed
*/
public static final Setting INDEX_HIDDEN_SETTING = Setting.boolSetting(
SETTING_INDEX_HIDDEN,
false,
Property.Dynamic,
Property.IndexScope
);
/**
* an internal index format description, allowing us to find out if this index is upgraded or needs upgrading
*/
private static final String INDEX_FORMAT = "index.format";
public static final Setting INDEX_FORMAT_SETTING = Setting.intSetting(
INDEX_FORMAT,
0,
Setting.Property.IndexScope,
Setting.Property.Final
);
public static final Setting> INDEX_ROUTING_PATH = Setting.stringListSetting(
"index.routing_path",
Setting.Property.IndexScope,
Setting.Property.Final
);
/**
* Legacy index setting, kept for 7.x BWC compatibility. This setting has no effect in 8.x. Do not use.
* TODO: Remove in 9.0
*/
@Deprecated
public static final Setting INDEX_ROLLUP_SOURCE_UUID = Setting.simpleString(
"index.rollup.source.uuid",
Property.IndexScope,
Property.PrivateIndex,
Property.IndexSettingDeprecatedInV7AndRemovedInV8
);
/**
* Legacy index setting, kept for 7.x BWC compatibility. This setting has no effect in 8.x. Do not use.
* TODO: Remove in 9.0
*/
@Deprecated
public static final Setting INDEX_ROLLUP_SOURCE_NAME = Setting.simpleString(
"index.rollup.source.name",
Property.IndexScope,
Property.PrivateIndex,
Property.IndexSettingDeprecatedInV7AndRemovedInV8
);
public static final String KEY_IN_SYNC_ALLOCATIONS = "in_sync_allocations";
static final String KEY_VERSION = "version";
static final String KEY_MAPPING_VERSION = "mapping_version";
static final String KEY_SETTINGS_VERSION = "settings_version";
static final String KEY_ALIASES_VERSION = "aliases_version";
static final String KEY_ROUTING_NUM_SHARDS = "routing_num_shards";
static final String KEY_SETTINGS = "settings";
static final String KEY_STATE = "state";
static final String KEY_MAPPINGS = "mappings";
static final String KEY_MAPPINGS_HASH = "mappings_hash";
static final String KEY_ALIASES = "aliases";
static final String KEY_ROLLOVER_INFOS = "rollover_info";
static final String KEY_SYSTEM = "system";
static final String KEY_TIMESTAMP_RANGE = "timestamp_range";
public static final String KEY_PRIMARY_TERMS = "primary_terms";
public static final String KEY_STATS = "stats";
public static final String KEY_WRITE_LOAD_FORECAST = "write_load_forecast";
public static final String KEY_SHARD_SIZE_FORECAST = "shard_size_forecast";
public static final String INDEX_STATE_FILE_PREFIX = "state-";
static final TransportVersion SYSTEM_INDEX_FLAG_ADDED = TransportVersion.V_7_10_0;
static final TransportVersion STATS_AND_FORECAST_ADDED = TransportVersion.V_8_6_0;
private final int routingNumShards;
private final int routingFactor;
private final int routingPartitionSize;
private final List routingPaths;
private final int numberOfShards;
private final int numberOfReplicas;
private final Index index;
private final long version;
private final long mappingVersion;
private final long settingsVersion;
private final long aliasesVersion;
private final long[] primaryTerms;
private final State state;
private final ImmutableOpenMap aliases;
private final Settings settings;
@Nullable
private final MappingMetadata mapping;
private final ImmutableOpenMap customData;
private final Map> inSyncAllocationIds;
private final transient int totalNumberOfShards;
private final DiscoveryNodeFilters requireFilters;
private final DiscoveryNodeFilters includeFilters;
private final DiscoveryNodeFilters excludeFilters;
private final DiscoveryNodeFilters initialRecoveryFilters;
private final IndexVersion indexCreatedVersion;
private final IndexVersion indexCompatibilityVersion;
private final ActiveShardCount waitForActiveShards;
private final ImmutableOpenMap rolloverInfos;
private final boolean isSystem;
private final boolean isHidden;
private final IndexLongFieldRange timestampRange;
private final int priority;
private final long creationDate;
private final boolean ignoreDiskWatermarks;
@Nullable // since we store null if DataTier.TIER_PREFERENCE_SETTING failed validation
private final List tierPreference;
private final int shardsPerNodeLimit;
@Nullable // if an index isn't managed by ilm, it won't have a policy
private final String lifecyclePolicyName;
private final LifecycleExecutionState lifecycleExecutionState;
private final AutoExpandReplicas autoExpandReplicas;
private final boolean isSearchableSnapshot;
private final boolean isPartialSearchableSnapshot;
@Nullable
private final IndexMode indexMode;
@Nullable
private final Instant timeSeriesStart;
@Nullable
private final Instant timeSeriesEnd;
@Nullable
private final IndexMetadataStats stats;
@Nullable
private final Double writeLoadForecast;
@Nullable
private final Long shardSizeInBytesForecast;
private IndexMetadata(
final Index index,
final long version,
final long mappingVersion,
final long settingsVersion,
final long aliasesVersion,
final long[] primaryTerms,
final State state,
final int numberOfShards,
final int numberOfReplicas,
final Settings settings,
final MappingMetadata mapping,
final ImmutableOpenMap aliases,
final ImmutableOpenMap customData,
final Map> inSyncAllocationIds,
final DiscoveryNodeFilters requireFilters,
final DiscoveryNodeFilters initialRecoveryFilters,
final DiscoveryNodeFilters includeFilters,
final DiscoveryNodeFilters excludeFilters,
final IndexVersion indexCreatedVersion,
final int routingNumShards,
final int routingPartitionSize,
final List routingPaths,
final ActiveShardCount waitForActiveShards,
final ImmutableOpenMap rolloverInfos,
final boolean isSystem,
final boolean isHidden,
final IndexLongFieldRange timestampRange,
final int priority,
final long creationDate,
final boolean ignoreDiskWatermarks,
@Nullable final List tierPreference,
final int shardsPerNodeLimit,
final String lifecyclePolicyName,
final LifecycleExecutionState lifecycleExecutionState,
final AutoExpandReplicas autoExpandReplicas,
final boolean isSearchableSnapshot,
final boolean isPartialSearchableSnapshot,
@Nullable final IndexMode indexMode,
@Nullable final Instant timeSeriesStart,
@Nullable final Instant timeSeriesEnd,
final IndexVersion indexCompatibilityVersion,
@Nullable final IndexMetadataStats stats,
@Nullable final Double writeLoadForecast,
@Nullable Long shardSizeInBytesForecast
) {
this.index = index;
this.version = version;
assert mappingVersion >= 0 : mappingVersion;
this.mappingVersion = mappingVersion;
assert settingsVersion >= 0 : settingsVersion;
this.settingsVersion = settingsVersion;
assert aliasesVersion >= 0 : aliasesVersion;
this.aliasesVersion = aliasesVersion;
this.primaryTerms = primaryTerms;
assert primaryTerms.length == numberOfShards;
this.state = state;
this.numberOfShards = numberOfShards;
this.numberOfReplicas = numberOfReplicas;
this.totalNumberOfShards = numberOfShards * (numberOfReplicas + 1);
this.settings = settings;
this.mapping = mapping;
this.customData = customData;
this.aliases = aliases;
this.inSyncAllocationIds = inSyncAllocationIds;
this.requireFilters = requireFilters;
this.includeFilters = includeFilters;
this.excludeFilters = excludeFilters;
this.initialRecoveryFilters = initialRecoveryFilters;
this.indexCreatedVersion = indexCreatedVersion;
this.routingNumShards = routingNumShards;
this.routingFactor = routingNumShards / numberOfShards;
this.routingPartitionSize = routingPartitionSize;
this.routingPaths = routingPaths;
this.waitForActiveShards = waitForActiveShards;
this.rolloverInfos = rolloverInfos;
this.isSystem = isSystem;
assert isHidden == INDEX_HIDDEN_SETTING.get(settings);
this.isHidden = isHidden;
this.timestampRange = timestampRange;
this.priority = priority;
this.creationDate = creationDate;
this.ignoreDiskWatermarks = ignoreDiskWatermarks;
this.tierPreference = tierPreference;
this.shardsPerNodeLimit = shardsPerNodeLimit;
this.lifecyclePolicyName = lifecyclePolicyName;
this.lifecycleExecutionState = lifecycleExecutionState;
this.autoExpandReplicas = autoExpandReplicas;
this.isSearchableSnapshot = isSearchableSnapshot;
this.isPartialSearchableSnapshot = isPartialSearchableSnapshot;
this.indexCompatibilityVersion = indexCompatibilityVersion;
assert indexCompatibilityVersion.equals(SETTING_INDEX_VERSION_COMPATIBILITY.get(settings));
this.indexMode = indexMode;
this.timeSeriesStart = timeSeriesStart;
this.timeSeriesEnd = timeSeriesEnd;
this.stats = stats;
this.writeLoadForecast = writeLoadForecast;
this.shardSizeInBytesForecast = shardSizeInBytesForecast;
assert numberOfShards * routingFactor == routingNumShards : routingNumShards + " must be a multiple of " + numberOfShards;
}
IndexMetadata withMappingMetadata(MappingMetadata mapping) {
if (mapping() == mapping) {
return this;
}
return new IndexMetadata(
this.index,
this.version,
this.mappingVersion,
this.settingsVersion,
this.aliasesVersion,
this.primaryTerms,
this.state,
this.numberOfShards,
this.numberOfReplicas,
this.settings,
mapping,
this.aliases,
this.customData,
this.inSyncAllocationIds,
this.requireFilters,
this.initialRecoveryFilters,
this.includeFilters,
this.excludeFilters,
this.indexCreatedVersion,
this.routingNumShards,
this.routingPartitionSize,
this.routingPaths,
this.waitForActiveShards,
this.rolloverInfos,
this.isSystem,
this.isHidden,
this.timestampRange,
this.priority,
this.creationDate,
this.ignoreDiskWatermarks,
this.tierPreference,
this.shardsPerNodeLimit,
this.lifecyclePolicyName,
this.lifecycleExecutionState,
this.autoExpandReplicas,
this.isSearchableSnapshot,
this.isPartialSearchableSnapshot,
this.indexMode,
this.timeSeriesStart,
this.timeSeriesEnd,
this.indexCompatibilityVersion,
this.stats,
this.writeLoadForecast,
this.shardSizeInBytesForecast
);
}
/**
* Copy constructor that sets the in-sync allocation ids for the specified shard.
* @param shardId shard id to set in-sync allocation ids for
* @param inSyncSet new in-sync allocation ids
* @return updated instance
*/
public IndexMetadata withInSyncAllocationIds(int shardId, Set inSyncSet) {
if (inSyncSet.equals(inSyncAllocationIds.get(shardId))) {
return this;
}
return new IndexMetadata(
this.index,
this.version,
this.mappingVersion,
this.settingsVersion,
this.aliasesVersion,
this.primaryTerms,
this.state,
this.numberOfShards,
this.numberOfReplicas,
this.settings,
this.mapping,
this.aliases,
this.customData,
Maps.copyMapWithAddedOrReplacedEntry(this.inSyncAllocationIds, shardId, Set.copyOf(inSyncSet)),
this.requireFilters,
this.initialRecoveryFilters,
this.includeFilters,
this.excludeFilters,
this.indexCreatedVersion,
this.routingNumShards,
this.routingPartitionSize,
this.routingPaths,
this.waitForActiveShards,
this.rolloverInfos,
this.isSystem,
this.isHidden,
this.timestampRange,
this.priority,
this.creationDate,
this.ignoreDiskWatermarks,
this.tierPreference,
this.shardsPerNodeLimit,
this.lifecyclePolicyName,
this.lifecycleExecutionState,
this.autoExpandReplicas,
this.isSearchableSnapshot,
this.isPartialSearchableSnapshot,
this.indexMode,
this.timeSeriesStart,
this.timeSeriesEnd,
this.indexCompatibilityVersion,
this.stats,
this.writeLoadForecast,
this.shardSizeInBytesForecast
);
}
/**
* Creates a copy of this instance that has the primary term for the given shard id incremented.
* @param shardId shard id to increment primary term for
* @return updated instance with incremented primary term
*/
public IndexMetadata withIncrementedPrimaryTerm(int shardId) {
final long[] incremented = this.primaryTerms.clone();
incremented[shardId]++;
return new IndexMetadata(
this.index,
this.version,
this.mappingVersion,
this.settingsVersion,
this.aliasesVersion,
incremented,
this.state,
this.numberOfShards,
this.numberOfReplicas,
this.settings,
this.mapping,
this.aliases,
this.customData,
this.inSyncAllocationIds,
this.requireFilters,
this.initialRecoveryFilters,
this.includeFilters,
this.excludeFilters,
this.indexCreatedVersion,
this.routingNumShards,
this.routingPartitionSize,
this.routingPaths,
this.waitForActiveShards,
this.rolloverInfos,
this.isSystem,
this.isHidden,
this.timestampRange,
this.priority,
this.creationDate,
this.ignoreDiskWatermarks,
this.tierPreference,
this.shardsPerNodeLimit,
this.lifecyclePolicyName,
this.lifecycleExecutionState,
this.autoExpandReplicas,
this.isSearchableSnapshot,
this.isPartialSearchableSnapshot,
this.indexMode,
this.timeSeriesStart,
this.timeSeriesEnd,
this.indexCompatibilityVersion,
this.stats,
this.writeLoadForecast,
this.shardSizeInBytesForecast
);
}
/**
* @param timestampRange new timestamp range
* @return copy of this instance with updated timestamp range
*/
public IndexMetadata withTimestampRange(IndexLongFieldRange timestampRange) {
if (timestampRange.equals(this.timestampRange)) {
return this;
}
return new IndexMetadata(
this.index,
this.version,
this.mappingVersion,
this.settingsVersion,
this.aliasesVersion,
this.primaryTerms,
this.state,
this.numberOfShards,
this.numberOfReplicas,
this.settings,
this.mapping,
this.aliases,
this.customData,
this.inSyncAllocationIds,
this.requireFilters,
this.initialRecoveryFilters,
this.includeFilters,
this.excludeFilters,
this.indexCreatedVersion,
this.routingNumShards,
this.routingPartitionSize,
this.routingPaths,
this.waitForActiveShards,
this.rolloverInfos,
this.isSystem,
this.isHidden,
timestampRange,
this.priority,
this.creationDate,
this.ignoreDiskWatermarks,
this.tierPreference,
this.shardsPerNodeLimit,
this.lifecyclePolicyName,
this.lifecycleExecutionState,
this.autoExpandReplicas,
this.isSearchableSnapshot,
this.isPartialSearchableSnapshot,
this.indexMode,
this.timeSeriesStart,
this.timeSeriesEnd,
this.indexCompatibilityVersion,
this.stats,
this.writeLoadForecast,
this.shardSizeInBytesForecast
);
}
/**
* @return a copy of this instance that has its version incremented by one
*/
public IndexMetadata withIncrementedVersion() {
return new IndexMetadata(
this.index,
this.version + 1,
this.mappingVersion,
this.settingsVersion,
this.aliasesVersion,
this.primaryTerms,
this.state,
this.numberOfShards,
this.numberOfReplicas,
this.settings,
this.mapping,
this.aliases,
this.customData,
this.inSyncAllocationIds,
this.requireFilters,
this.initialRecoveryFilters,
this.includeFilters,
this.excludeFilters,
this.indexCreatedVersion,
this.routingNumShards,
this.routingPartitionSize,
this.routingPaths,
this.waitForActiveShards,
this.rolloverInfos,
this.isSystem,
this.isHidden,
this.timestampRange,
this.priority,
this.creationDate,
this.ignoreDiskWatermarks,
this.tierPreference,
this.shardsPerNodeLimit,
this.lifecyclePolicyName,
this.lifecycleExecutionState,
this.autoExpandReplicas,
this.isSearchableSnapshot,
this.isPartialSearchableSnapshot,
this.indexMode,
this.timeSeriesStart,
this.timeSeriesEnd,
this.indexCompatibilityVersion,
this.stats,
this.writeLoadForecast,
this.shardSizeInBytesForecast
);
}
public Index getIndex() {
return index;
}
public String getIndexUUID() {
return index.getUUID();
}
public long getVersion() {
return this.version;
}
public long getMappingVersion() {
return mappingVersion;
}
public long getSettingsVersion() {
return settingsVersion;
}
public long getAliasesVersion() {
return aliasesVersion;
}
/**
* The term of the current selected primary. This is a non-negative number incremented when
* a primary shard is assigned after a full cluster restart or a replica shard is promoted to a primary.
*
* Note: since we increment the term every time a shard is assigned, the term for any operational shard (i.e., a shard
* that can be indexed into) is larger than 0. See {@link IndexMetadataUpdater#applyChanges}.
**/
public long primaryTerm(int shardId) {
return this.primaryTerms[shardId];
}
/**
* Return the {@link Version} on which this index has been created. This
* information is typically useful for backward compatibility.
* To check index compatibility (e.g. N-1 checks), use {@link #getCompatibilityVersion()} instead.
*/
public Version getCreationVersion() {
return indexCreatedVersion.toVersion();
}
/**
* Return the {@link Version} that this index provides compatibility for.
* This is typically compared to the {@link Version#minimumIndexCompatibilityVersion()} to figure out whether the index can be handled
* by the cluster.
* By default, this is equal to the {@link #getCreationVersion()}, but can also be a newer version if the index has been imported as
* a legacy index from an older snapshot, and its metadata has been converted to be handled by newer version nodes.
*/
public Version getCompatibilityVersion() {
return indexCompatibilityVersion.toVersion();
}
public long getCreationDate() {
return creationDate;
}
public State getState() {
return this.state;
}
public int getNumberOfShards() {
return numberOfShards;
}
public int getNumberOfReplicas() {
return numberOfReplicas;
}
public int getRoutingPartitionSize() {
return routingPartitionSize;
}
public boolean isRoutingPartitionedIndex() {
return routingPartitionSize != 1;
}
public List getRoutingPaths() {
return routingPaths;
}
public int getTotalNumberOfShards() {
return totalNumberOfShards;
}
/**
* Returns the configured {@link #SETTING_WAIT_FOR_ACTIVE_SHARDS}, which defaults
* to an active shard count of 1 if not specified.
*/
public ActiveShardCount getWaitForActiveShards() {
return waitForActiveShards;
}
public boolean ignoreDiskWatermarks() {
return ignoreDiskWatermarks;
}
public Settings getSettings() {
return settings;
}
public Map getAliases() {
return this.aliases;
}
public int getShardsPerNodeLimit() {
return shardsPerNodeLimit;
}
public List getTierPreference() {
if (tierPreference == null) {
final List parsed = DataTier.parseTierList(DataTier.TIER_PREFERENCE_SETTING.get(settings));
assert false : "the setting parsing should always throw if we didn't store a tier preference when building this instance";
return parsed;
}
return tierPreference;
}
/**
* Return the name of the Index Lifecycle Policy associated with this index, or null if it is not managed by ILM.
*/
@Nullable
public String getLifecyclePolicyName() {
return lifecyclePolicyName;
}
public LifecycleExecutionState getLifecycleExecutionState() {
return lifecycleExecutionState;
}
public AutoExpandReplicas getAutoExpandReplicas() {
return autoExpandReplicas;
}
public boolean isSearchableSnapshot() {
return isSearchableSnapshot;
}
public boolean isPartialSearchableSnapshot() {
return isPartialSearchableSnapshot;
}
/**
* @return the mode this index is in. This determines the behaviour and features it supports.
* If null
is returned then this in index is in standard mode.
*/
@Nullable
public IndexMode getIndexMode() {
return indexMode;
}
/**
* If this index is in {@link IndexMode#TIME_SERIES} then this returns the lower boundary of the time series time range.
* Together with {@link #getTimeSeriesEnd()} this defines the time series time range this index has and the range of
* timestamps all documents in this index have.
*
* @return If this index is in {@link IndexMode#TIME_SERIES} then this returns the lower boundary of the time series time range.
* If this index isn't in {@link IndexMode#TIME_SERIES} then null
is returned.
*/
@Nullable
public Instant getTimeSeriesStart() {
return timeSeriesStart;
}
/**
* If this index is in {@link IndexMode#TIME_SERIES} then this returns the upper boundary of the time series time range.
* Together with {@link #getTimeSeriesStart()} this defines the time series time range this index has and the range of
* timestamps all documents in this index have.
*
* @return If this index is in {@link IndexMode#TIME_SERIES} then this returns the upper boundary of the time series time range.
* If this index isn't in {@link IndexMode#TIME_SERIES} then null
is returned.
*/
@Nullable
public Instant getTimeSeriesEnd() {
return timeSeriesEnd;
}
/**
* Return the concrete mapping for this index or {@code null} if this index has no mappings at all.
*/
@Nullable
public MappingMetadata mapping() {
return mapping;
}
@Nullable
public IndexMetadataStats getStats() {
return stats;
}
public OptionalDouble getForecastedWriteLoad() {
return writeLoadForecast == null ? OptionalDouble.empty() : OptionalDouble.of(writeLoadForecast);
}
public OptionalLong getForecastedShardSizeInBytes() {
return shardSizeInBytesForecast == null ? OptionalLong.empty() : OptionalLong.of(shardSizeInBytesForecast);
}
public static final String INDEX_RESIZE_SOURCE_UUID_KEY = "index.resize.source.uuid";
public static final String INDEX_RESIZE_SOURCE_NAME_KEY = "index.resize.source.name";
public static final Setting INDEX_RESIZE_SOURCE_UUID = Setting.simpleString(INDEX_RESIZE_SOURCE_UUID_KEY);
public static final Setting INDEX_RESIZE_SOURCE_NAME = Setting.simpleString(INDEX_RESIZE_SOURCE_NAME_KEY);
/**
* we use "i.r.a.initial_recovery" rather than "i.r.a.require|include" since we want the replica to allocate right away
* once we are allocated.
*/
public static final String INDEX_SHRINK_INITIAL_RECOVERY_KEY = INDEX_ROUTING_INITIAL_RECOVERY_GROUP_SETTING.getKey() + "_id";
public Index getResizeSourceIndex() {
return INDEX_RESIZE_SOURCE_UUID.exists(settings)
? new Index(INDEX_RESIZE_SOURCE_NAME.get(settings), INDEX_RESIZE_SOURCE_UUID.get(settings))
: null;
}
public static final String INDEX_DOWNSAMPLE_SOURCE_UUID_KEY = "index.downsample.source.uuid";
public static final String INDEX_DOWNSAMPLE_SOURCE_NAME_KEY = "index.downsample.source.name";
public static final String INDEX_DOWNSAMPLE_STATUS_KEY = "index.downsample.status";
public static final Setting INDEX_DOWNSAMPLE_SOURCE_UUID = Setting.simpleString(
INDEX_DOWNSAMPLE_SOURCE_UUID_KEY,
Property.IndexScope,
Property.PrivateIndex
);
public static final Setting INDEX_DOWNSAMPLE_SOURCE_NAME = Setting.simpleString(
INDEX_DOWNSAMPLE_SOURCE_NAME_KEY,
Property.IndexScope,
Property.PrivateIndex
);
public enum DownsampleTaskStatus {
UNKNOWN,
STARTED,
SUCCESS;
@Override
public String toString() {
return name().toLowerCase(Locale.ROOT);
}
}
public static final Setting INDEX_DOWNSAMPLE_STATUS = Setting.enumSetting(
DownsampleTaskStatus.class,
INDEX_DOWNSAMPLE_STATUS_KEY,
DownsampleTaskStatus.UNKNOWN,
Property.IndexScope,
Property.InternalIndex
);
// LIFECYCLE_NAME is here an as optimization, see LifecycleSettings.LIFECYCLE_NAME and
// LifecycleSettings.LIFECYCLE_NAME_SETTING for the 'real' version
public static final String LIFECYCLE_NAME = "index.lifecycle.name";
Map getCustomData() {
return this.customData;
}
public Map getCustomData(final String key) {
return this.customData.get(key);
}
public Map> getInSyncAllocationIds() {
return inSyncAllocationIds;
}
public Map getRolloverInfos() {
return rolloverInfos;
}
public Set inSyncAllocationIds(int shardId) {
assert shardId >= 0 && shardId < numberOfShards;
return inSyncAllocationIds.get(shardId);
}
@Nullable
public DiscoveryNodeFilters requireFilters() {
return requireFilters;
}
@Nullable
public DiscoveryNodeFilters getInitialRecoveryFilters() {
return initialRecoveryFilters;
}
@Nullable
public DiscoveryNodeFilters includeFilters() {
return includeFilters;
}
@Nullable
public DiscoveryNodeFilters excludeFilters() {
return excludeFilters;
}
public IndexLongFieldRange getTimestampRange() {
return timestampRange;
}
/**
* @return whether this index has a time series timestamp range
*/
public boolean hasTimeSeriesTimestampRange() {
return indexMode != null && indexMode.getTimestampBound(this) != null;
}
/**
* @param dateFieldType the date field type of '@timestamp' field which is
* used to convert the start and end times recorded in index metadata
* to the right format that is being used by '@timestamp' field.
* For example, the '@timestamp' can be configured with nanosecond precision.
* @return the time range this index represents if this index is in time series mode.
* Otherwise null
is returned.
*/
@Nullable
public IndexLongFieldRange getTimeSeriesTimestampRange(DateFieldMapper.DateFieldType dateFieldType) {
var bounds = indexMode != null ? indexMode.getTimestampBound(this) : null;
if (bounds != null) {
long start = dateFieldType.resolution().convert(Instant.ofEpochMilli(bounds.startTime()));
long end = dateFieldType.resolution().convert(Instant.ofEpochMilli(bounds.endTime()));
return IndexLongFieldRange.NO_SHARDS.extendWithShardRange(0, 1, ShardLongFieldRange.of(start, end));
} else {
return null;
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
IndexMetadata that = (IndexMetadata) o;
if (version != that.version) {
return false;
}
if (aliases.equals(that.aliases) == false) {
return false;
}
if (index.equals(that.index) == false) {
return false;
}
if (Objects.equals(mapping, that.mapping) == false) {
return false;
}
if (settings.equals(that.settings) == false) {
return false;
}
if (state != that.state) {
return false;
}
if (customData.equals(that.customData) == false) {
return false;
}
if (routingNumShards != that.routingNumShards) {
return false;
}
if (routingFactor != that.routingFactor) {
return false;
}
if (Arrays.equals(primaryTerms, that.primaryTerms) == false) {
return false;
}
if (inSyncAllocationIds.equals(that.inSyncAllocationIds) == false) {
return false;
}
if (rolloverInfos.equals(that.rolloverInfos) == false) {
return false;
}
if (isSystem != that.isSystem) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = index.hashCode();
result = 31 * result + Long.hashCode(version);
result = 31 * result + state.hashCode();
result = 31 * result + aliases.hashCode();
result = 31 * result + settings.hashCode();
result = 31 * result + Objects.hash(mapping);
result = 31 * result + customData.hashCode();
result = 31 * result + Long.hashCode(routingFactor);
result = 31 * result + Long.hashCode(routingNumShards);
result = 31 * result + Arrays.hashCode(primaryTerms);
result = 31 * result + inSyncAllocationIds.hashCode();
result = 31 * result + rolloverInfos.hashCode();
result = 31 * result + Boolean.hashCode(isSystem);
return result;
}
@Override
public Diff diff(IndexMetadata previousState) {
return new IndexMetadataDiff(previousState, this);
}
public static Diff readDiffFrom(StreamInput in) throws IOException {
return new IndexMetadataDiff(in);
}
public static IndexMetadata fromXContent(XContentParser parser) throws IOException {
return Builder.fromXContent(parser);
}
public static IndexMetadata fromXContent(XContentParser parser, Map mappingsByHash) throws IOException {
return Builder.fromXContent(parser, mappingsByHash);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
Builder.toXContent(this, builder, params);
return builder;
}
private static final TransportVersion SETTING_DIFF_VERSION = TransportVersion.V_8_5_0;
private static class IndexMetadataDiff implements Diff {
private final String index;
private final int routingNumShards;
private final long version;
private final long mappingVersion;
private final long settingsVersion;
private final long aliasesVersion;
private final long[] primaryTerms;
private final State state;
// used for BwC when this instance was written by an older version node that does not diff settings yet
@Nullable
private final Settings settings;
@Nullable
private final Diff settingsDiff;
private final Diff> mappings;
private final Diff> aliases;
private final Diff> customData;
private final Diff
© 2015 - 2025 Weber Informatics LLC | Privacy Policy