-
Notifications
You must be signed in to change notification settings - Fork 25.6k
TSDB: add index timestamp range check #78291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 23 commits
2bad389
8809757
6c42357
c3f1e66
79e96e0
b4a68c3
63e552c
0e67726
e3b7256
78c132d
fe8f654
120cdaf
563ca87
daccfb7
14a2378
2c45c57
d2a00ec
d631649
1dcebb0
79db882
69df14d
6c77b18
40f2e82
582588f
bfe25c5
6350ee1
a5a116f
7236ee1
20c21a8
9e92d09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |
| import org.elasticsearch.common.settings.Setting; | ||
| import org.elasticsearch.common.settings.Setting.Property; | ||
| import org.elasticsearch.common.settings.Settings; | ||
| import org.elasticsearch.common.time.DateUtils; | ||
| import org.elasticsearch.common.unit.ByteSizeUnit; | ||
| import org.elasticsearch.common.unit.ByteSizeValue; | ||
| import org.elasticsearch.core.Booleans; | ||
|
|
@@ -26,6 +27,7 @@ | |
| import org.elasticsearch.ingest.IngestService; | ||
| import org.elasticsearch.node.Node; | ||
|
|
||
| import java.time.Instant; | ||
| import java.util.Collections; | ||
| import java.util.Iterator; | ||
| import java.util.List; | ||
|
|
@@ -474,6 +476,46 @@ public static boolean isTimeSeriesModeEnabled() { | |
| return Build.CURRENT.isSnapshot() || (TIME_SERIES_MODE_FEATURE_FLAG_REGISTERED != null && TIME_SERIES_MODE_FEATURE_FLAG_REGISTERED); | ||
| } | ||
|
|
||
| /** | ||
| * in time series mode, the start time of the index, timestamp must larger than start_time | ||
| */ | ||
| public static final Setting<Instant> TIME_SERIES_START_TIME = Setting.dateSetting( | ||
| "index.time_series.start_time", | ||
| Instant.ofEpochMilli(0), | ||
| v -> {}, | ||
| Property.IndexScope, | ||
| Property.Final | ||
| ); | ||
|
|
||
| /** | ||
| * in time series mode, the end time of the index, timestamp must smaller than start_time | ||
| */ | ||
| public static final Setting<Instant> TIME_SERIES_END_TIME = Setting.dateSetting( | ||
| "index.time_series.end_time", | ||
| DateUtils.MAX_NANOSECOND_INSTANT, | ||
| new Setting.Validator<>() { | ||
| @Override | ||
| public void validate(Instant value) {} | ||
|
|
||
| @Override | ||
| public void validate(Instant value, Map<Setting<?>, Object> settings) { | ||
| @SuppressWarnings("unchecked") | ||
| Instant startTime = (Instant) settings.get(TIME_SERIES_START_TIME); | ||
| if (startTime.toEpochMilli() > value.toEpochMilli()) { | ||
| throw new IllegalArgumentException("index.time_series.end_time must be larger than index.time_series.start_time"); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Iterator<Setting<?>> settings() { | ||
| List<Setting<?>> settings = List.of(TIME_SERIES_START_TIME); | ||
| return settings.iterator(); | ||
| } | ||
| }, | ||
| Property.IndexScope, | ||
| Property.Dynamic | ||
| ); | ||
|
|
||
| /** | ||
| * The {@link IndexMode "mode"} of the index. | ||
| */ | ||
|
|
@@ -509,6 +551,14 @@ public Iterator<Setting<?>> settings() { | |
| * The {@link IndexMode "mode"} of the index. | ||
| */ | ||
| private final IndexMode mode; | ||
| /** | ||
| * Start time of the time_series index. | ||
| */ | ||
| private final Instant timeSeriesStartTime; | ||
| /** | ||
| * End time of the time_series index. | ||
| */ | ||
| private volatile Instant timeSeriesEndTime; | ||
|
||
|
|
||
| // volatile fields are updated via #updateIndexMetadata(IndexMetadata) under lock | ||
| private volatile Settings settings; | ||
|
|
@@ -651,7 +701,8 @@ public IndexSettings(final IndexMetadata indexMetadata, final Settings nodeSetti | |
| this.indexMetadata = indexMetadata; | ||
| numberOfShards = settings.getAsInt(IndexMetadata.SETTING_NUMBER_OF_SHARDS, null); | ||
| mode = isTimeSeriesModeEnabled() ? scopedSettings.get(MODE) : IndexMode.STANDARD; | ||
|
|
||
| timeSeriesStartTime = TIME_SERIES_START_TIME.get(settings); | ||
| timeSeriesEndTime = TIME_SERIES_END_TIME.get(settings); | ||
| this.searchThrottled = INDEX_SEARCH_THROTTLED.get(settings); | ||
| this.queryStringLenient = QUERY_STRING_LENIENT_SETTING.get(settings); | ||
| this.queryStringAnalyzeWildcard = QUERY_STRING_ANALYZE_WILDCARD.get(nodeSettings); | ||
|
|
@@ -765,6 +816,7 @@ public IndexSettings(final IndexMetadata indexMetadata, final Settings nodeSetti | |
| scopedSettings.addSettingsUpdateConsumer(INDEX_MAPPING_DEPTH_LIMIT_SETTING, this::setMappingDepthLimit); | ||
| scopedSettings.addSettingsUpdateConsumer(INDEX_MAPPING_FIELD_NAME_LENGTH_LIMIT_SETTING, this::setMappingFieldNameLengthLimit); | ||
| scopedSettings.addSettingsUpdateConsumer(INDEX_MAPPING_DIMENSION_FIELDS_LIMIT_SETTING, this::setMappingDimensionFieldsLimit); | ||
| scopedSettings.addSettingsUpdateConsumer(TIME_SERIES_END_TIME, this::updateTimeSeriesEndTime); | ||
| } | ||
|
|
||
| private void setSearchIdleAfter(TimeValue searchIdleAfter) { | ||
|
|
@@ -1278,4 +1330,21 @@ public long getMappingDimensionFieldsLimit() { | |
| private void setMappingDimensionFieldsLimit(long value) { | ||
| this.mappingDimensionFieldsLimit = value; | ||
| } | ||
|
|
||
| public Instant getTimeSeriesStartTime() { | ||
| return timeSeriesStartTime; | ||
| } | ||
|
|
||
| public Instant getTimeSeriesEndTime() { | ||
| return timeSeriesEndTime; | ||
| } | ||
|
|
||
| public void updateTimeSeriesEndTime(Instant endTime) { | ||
| if (this.timeSeriesEndTime.isAfter(endTime)) { | ||
| throw new IllegalArgumentException( | ||
| "index.time_series.end_time must be larger than current value [" + this.timeSeriesEndTime + "]" | ||
| ); | ||
| } | ||
| this.timeSeriesEndTime = endTime; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -201,6 +201,22 @@ NumericType numericType() { | |
| */ | ||
| public abstract long roundUpToMillis(long value); | ||
|
|
||
| public final void validateTimestamp(long value, DocumentParserContext context) { | ||
| if (context.indexSettings().getTimeSeriesStartTime() == null) { | ||
| assert context.indexSettings().getTimeSeriesEndTime() == null; | ||
| return; | ||
| } | ||
| long startTime = convert(context.indexSettings().getTimeSeriesStartTime()); | ||
| if (value < startTime) { | ||
| throw new IllegalArgumentException("time series index @timestamp value [" + value + "] must be larger than " + startTime); | ||
| } | ||
|
|
||
| long endTime = convert(context.indexSettings().getTimeSeriesEndTime()); | ||
| if (value >= endTime) { | ||
| throw new IllegalArgumentException("time series index @timestamp value [" + value + "] must be smaller than " + endTime); | ||
| } | ||
| } | ||
|
||
|
|
||
| public static Resolution ofOrdinal(int ord) { | ||
| for (Resolution resolution : values()) { | ||
| if (ord == resolution.ordinal()) { | ||
|
|
@@ -244,6 +260,7 @@ public static class Builder extends FieldMapper.Builder { | |
| private final Resolution resolution; | ||
| private final Version indexCreatedVersion; | ||
| private final ScriptCompiler scriptCompiler; | ||
| private final Validate validate; | ||
|
|
||
| public Builder( | ||
| String name, | ||
|
|
@@ -270,6 +287,7 @@ public Builder( | |
| this.format.setValue(dateFormatter.pattern()); | ||
| this.locale.setValue(dateFormatter.locale()); | ||
| } | ||
| this.validate = name.equals(DataStreamTimestampFieldMapper.DEFAULT_PATH) ? resolution::validateTimestamp : (v, c) -> {}; | ||
| } | ||
|
|
||
| private DateFormatter buildFormatter() { | ||
|
|
@@ -336,7 +354,16 @@ public DateFieldMapper build(MapperBuilderContext context) { | |
| ); | ||
|
|
||
| Long nullTimestamp = parseNullValue(ft); | ||
| return new DateFieldMapper(name, ft, multiFieldsBuilder.build(this, context), copyTo.build(), nullTimestamp, resolution, this); | ||
| return new DateFieldMapper( | ||
| name, | ||
| ft, | ||
| multiFieldsBuilder.build(this, context), | ||
| copyTo.build(), | ||
| nullTimestamp, | ||
| resolution, | ||
| this, | ||
| validate | ||
| ); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -716,6 +743,7 @@ public DocValueFormat docValueFormat(@Nullable String format, ZoneId timeZone) { | |
| private final Script script; | ||
| private final ScriptCompiler scriptCompiler; | ||
| private final FieldValues<Long> scriptValues; | ||
| private final Validate validate; | ||
|
|
||
| private DateFieldMapper( | ||
| String simpleName, | ||
|
|
@@ -724,7 +752,8 @@ private DateFieldMapper( | |
| CopyTo copyTo, | ||
| Long nullValue, | ||
| Resolution resolution, | ||
| Builder builder | ||
| Builder builder, | ||
| Validate validate | ||
| ) { | ||
| super(simpleName, mappedFieldType, multiFields, copyTo, builder.script.get() != null, builder.onScriptError.get()); | ||
| this.store = builder.store.getValue(); | ||
|
|
@@ -741,6 +770,7 @@ private DateFieldMapper( | |
| this.script = builder.script.get(); | ||
| this.scriptCompiler = builder.scriptCompiler; | ||
| this.scriptValues = builder.scriptValues(); | ||
| this.validate = validate; | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -780,6 +810,7 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio | |
| } | ||
| } | ||
| } | ||
| validate.validate(timestamp, context); | ||
|
|
||
| indexValue(context, timestamp); | ||
| } | ||
|
|
@@ -815,4 +846,8 @@ public boolean getIgnoreMalformed() { | |
| public Long getNullValue() { | ||
| return nullValue; | ||
| } | ||
|
|
||
| private interface Validate { | ||
| void validate(long value, DocumentParserContext context); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.