Skip to content

Commit 8945551

Browse files
authored
Add index setting that disables the index.dimensions based routing and _tsid creation strategy (#135673)
1 parent 0682615 commit 8945551

File tree

8 files changed

+124
-35
lines changed

8 files changed

+124
-35
lines changed

docs/changelog/135673.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 135673
2+
summary: Add index setting that disables the `index.dimensions` based routing and
3+
`_tsid` creation strategy
4+
area: TSDB
5+
type: enhancement
6+
issues: []

docs/reference/elasticsearch/index-settings/serverless.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ This page lists the {{es}} index settings available in {{serverless-full}} proje
1313
### General settings
1414

1515
* [`index.codec`](./index-modules.md#index-codec)
16-
* [`index.default_pipeline`](./index-modules.md#index-default-pipeline)
16+
* [`index.default_pipeline`](./index-modules.md#index-default-pipeline)
1717
* [`index.dense_vector.hnsw_filter_heuristic`](./index-modules.md#index-dense-vector-hnsw-filter-heuristic)
18-
* [`index.final_pipeline`](./index-modules.md#index-final-pipeline)
18+
* [`index.final_pipeline`](./index-modules.md#index-final-pipeline)
1919
* [`index.hidden`](./index-modules.md#index-hidden)
2020
* [`index.mode`](./index-modules.md#index-mode-setting)
21-
* [`index.query.default_field`](./index-modules.md#index-query-default-field)
22-
* [`index.refresh_interval`](./index-modules.md#index-refresh-interval-setting)
21+
* [`index.query.default_field`](./index-modules.md#index-query-default-field)
22+
* [`index.refresh_interval`](./index-modules.md#index-refresh-interval-setting)
2323

2424
### Index sorting settings
2525

@@ -30,10 +30,10 @@ This page lists the {{es}} index settings available in {{serverless-full}} proje
3030

3131
### Index blocks settings
3232

33-
* [`index.blocks.read_only`](./index-block.md#index-blocks-read-only)
34-
* [`index.blocks.read`](./index-block.md#index-blocks-read)
35-
* [`index.blocks.write`](./index-block.md#index-blocks-write)
36-
* [`index.blocks.metadata`](./index-block.md#index-blocks-metadata)
33+
* [`index.blocks.read_only`](./index-block.md#index-blocks-read-only)
34+
* [`index.blocks.read`](./index-block.md#index-blocks-read)
35+
* [`index.blocks.write`](./index-block.md#index-blocks-write)
36+
* [`index.blocks.metadata`](./index-block.md#index-blocks-metadata)
3737

3838
### Field and mapping related settings
3939

@@ -56,8 +56,9 @@ This page lists the {{es}} index settings available in {{serverless-full}} proje
5656
* [`index.look_ahead_time`](./time-series.md#index-look-ahead-time)
5757
* [`index.look_back_time`](./time-series.md#index-look-back-time)
5858
* [`index.routing_path`](./time-series.md#index-routing-path)
59+
* [`index.dimensions_tsid_strategy_enabled`](./time-series.md#index-dimensions-tsid-strategy-enabled)
5960

6061
### Similarity and analyzers
6162

6263
* [`index.similarity.*`](../mapping-reference/similarity.md)
63-
* [`index.analysis.*`](../mapping-reference/analyzer.md)
64+
* [`index.analysis.*`](../mapping-reference/analyzer.md)

docs/reference/elasticsearch/index-settings/time-series.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,39 @@ $$$index-look-back-time$$$
4444
: (Static, [time units](/reference/elasticsearch/rest-apis/api-conventions.md#time-units)) Interval used to calculate the `index.time_series.start_time` for a TSDS’s first backing index when a tsdb data stream is created. Defaults to `2h` (2 hours). Accepts `1m` (one minute) to `7d` (seven days). Only indices with an `index.mode` of `time_series` support this setting. For more information, refer to [Look-back time](docs-content://manage-data/data-store/data-streams/time-series-data-stream-tsds.md#tsds-look-back-time).
4545

4646
$$$index-routing-path$$$ `index.routing_path` {applies_to}`serverless: all`
47-
: (Static, string or array of strings) Plain `keyword` fields used to route documents in a TSDS to index shards. Supports wildcards (`*`). Only indices with an `index.mode` of `time_series` support this setting. Defaults to an empty list, except for data streams then defaults to the list of [dimension fields](docs-content://manage-data/data-store/data-streams/time-series-data-stream-tsds.md#time-series-dimension) with a `time_series_dimension` value of `true` defined in your component and index templates. For more information, refer to [Dimension-based routing](docs-content://manage-data/data-store/data-streams/time-series-data-stream-tsds.md#dimension-based-routing).
47+
: (Static, string or array of strings) Time series dimension fields used to route documents in a TSDS to index shards.
48+
Supports wildcards (`*`).
49+
Only indices with an `index.mode` of `time_series` support this setting.
50+
51+
: Defaults value:
52+
: Indices that are not part of a time series data stream have no default value and require the routing path to be defined explicitly.
53+
If a time series data stream is used that is eligible for the `index.dimensions`-based routing (see [`index.dimensions_tsid_strategy_enabled`](#index-dimensions-tsid-strategy-enabled)),
54+
the `index.routing_path` will be empty.
55+
For time series data streams where the `index.dimensions`-based routing does not apply,
56+
this defaults to the list of [dimension fields](docs-content://manage-data/data-store/data-streams/time-series-data-stream-tsds.md#time-series-dimension) with a `time_series_dimension` value of `true` as defined in your component and index templates.
57+
58+
: Manually setting a value disables the `index.dimensions`-based routing strategy (see [`index.dimensions_tsid_strategy_enabled`](#index-dimensions-tsid-strategy-enabled)).
59+
For more information, refer to [Dimension-based routing](docs-content://manage-data/data-store/data-streams/time-series-data-stream-tsds.md#dimension-based-routing).
60+
61+
62+
$$$index-dimensions-tsid-strategy-enabled$$$
63+
64+
`index.dimensions_tsid_strategy_enabled` {applies_to}`stack: ga 9.2` {applies_to}`serverless: all`
65+
: (Static, boolean) Controls if the `_tsid` can be created using the `index.dimensions` index setting.
66+
This is an internal setting that will be automatically populated and updated for eligible time series data streams and is not user-configurable.
67+
This strategy offers an improved ingestion performance that avoids processing dimensions multiple times for the purposes of shard routing and creating the `_tsid`.
68+
When used, `index.routing_path` will not be set and shard routing uses the full `_tsid`,
69+
which can help to avoid shard hot-spotting.
70+
71+
: If set to `false`,
72+
or `index.routing_path` is configured manually,
73+
or in case the index isn't eligible (see below),
74+
shard routing will be based on the `index.routing_path` instead.
75+
76+
: Defaults to `true`.
77+
78+
: This optimized `_tsid` creation strategy is only available for data streams and if there are no dynamic templates that set `time_series_dimension: true`.
79+
Trying to add such a dynamic template to existing backing indices after the fact will fail the update mapping request and you will need to roll over the data stream instead.
4880

4981
$$$index-mapping-dimension-fields-limit$$$
5082

modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/TSDBIndexingIT.java

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ public void testTsdbTemplatesNoKeywordFieldType() throws Exception {
333333
Settings.builder()
334334
.put("index.mode", "time_series")
335335
.put("index.routing_path", randomBoolean() ? null : "metricset")
336+
.put("index.dimensions_tsid_strategy_enabled", randomDouble() < 0.8)
336337
.build(),
337338
new CompressedXContent(mappingTemplate),
338339
null
@@ -640,12 +641,16 @@ public void testReindexing() throws Exception {
640641
public void testAddDimensionToMapping() throws Exception {
641642
String dataStreamName = "my-ds";
642643
var putTemplateRequest = new TransportPutComposableIndexTemplateAction.Request("id");
644+
boolean indexDimensionsTsidStrategyEnabled = randomBoolean();
643645
putTemplateRequest.indexTemplate(
644646
ComposableIndexTemplate.builder()
645647
.indexPatterns(List.of(dataStreamName))
646648
.template(
647649
new Template(
648-
Settings.builder().put("index.mode", "time_series").build(),
650+
Settings.builder()
651+
.put("index.mode", "time_series")
652+
.put("index.dimensions_tsid_strategy_enabled", indexDimensionsTsidStrategyEnabled)
653+
.build(),
649654
new CompressedXContent(MAPPING_TEMPLATE),
650655
null
651656
)
@@ -662,8 +667,13 @@ public void testAddDimensionToMapping() throws Exception {
662667
"my-ds"
663668
);
664669
assertAcked(client().execute(CreateDataStreamAction.INSTANCE, createDsRequest));
665-
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_DIMENSIONS), equalTo(List.of("metricset")));
666-
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_ROUTING_PATH), empty());
670+
if (indexDimensionsTsidStrategyEnabled) {
671+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_DIMENSIONS), equalTo(List.of("metricset")));
672+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_ROUTING_PATH), empty());
673+
} else {
674+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_DIMENSIONS), empty());
675+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_ROUTING_PATH), equalTo(List.of("metricset")));
676+
}
667677

668678
// put mapping with k8s.pod.uid as another time series dimension
669679
var putMappingRequest = new PutMappingRequest(dataStreamName).source("""
@@ -677,8 +687,13 @@ public void testAddDimensionToMapping() throws Exception {
677687
}
678688
""", XContentType.JSON);
679689
assertAcked(client().execute(TransportPutMappingAction.TYPE, putMappingRequest).actionGet());
680-
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_DIMENSIONS), containsInAnyOrder("metricset", "k8s.pod.name"));
681-
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_ROUTING_PATH), empty());
690+
if (indexDimensionsTsidStrategyEnabled) {
691+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_DIMENSIONS), containsInAnyOrder("metricset", "k8s.pod.name"));
692+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_ROUTING_PATH), empty());
693+
} else {
694+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_DIMENSIONS), empty());
695+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_ROUTING_PATH), equalTo(List.of("metricset")));
696+
}
682697

683698
// put dynamic template defining time series dimensions
684699
// we don't support index.dimensions in that case
@@ -698,13 +713,19 @@ public void testAddDimensionToMapping() throws Exception {
698713
}
699714
""", XContentType.JSON);
700715
ActionFuture<AcknowledgedResponse> putMappingFuture = client().execute(TransportPutMappingAction.TYPE, putMappingRequest);
701-
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, putMappingFuture::actionGet);
702-
assertThat(
703-
exception.getMessage(),
704-
containsString("Cannot add dynamic templates that define dimension fields on an existing index with index.dimensions")
705-
);
706-
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_DIMENSIONS), containsInAnyOrder("metricset", "k8s.pod.name"));
707-
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_ROUTING_PATH), empty());
716+
if (indexDimensionsTsidStrategyEnabled) {
717+
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, putMappingFuture::actionGet);
718+
assertThat(
719+
exception.getMessage(),
720+
containsString("Cannot add dynamic templates that define dimension fields on an existing index with index.dimensions")
721+
);
722+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_DIMENSIONS), containsInAnyOrder("metricset", "k8s.pod.name"));
723+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_ROUTING_PATH), empty());
724+
} else {
725+
assertAcked(putMappingFuture.actionGet());
726+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_DIMENSIONS), empty());
727+
assertThat(getSetting(dataStreamName, IndexMetadata.INDEX_ROUTING_PATH), equalTo(List.of("metricset")));
728+
}
708729
indexWithPodNames(dataStreamName, Instant.now(), Map.of(), "dog", "cat");
709730
}
710731

modules/data-streams/src/main/java/org/elasticsearch/datastreams/DataStreamIndexSettingsProvider.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ public void provideAdditionalSettings(
130130
dimensions
131131
);
132132
if (dimensions.isEmpty() == false) {
133-
if (matchesAllDimensions && indexVersion.onOrAfter(IndexVersions.TSID_CREATED_DURING_ROUTING)) {
133+
if (matchesAllDimensions
134+
&& IndexMetadata.INDEX_DIMENSIONS_TSID_STRATEGY_ENABLED.get(indexTemplateAndCreateRequestSettings)
135+
&& indexVersion.onOrAfter(IndexVersions.TSID_CREATED_DURING_ROUTING)) {
134136
// Only set index.dimensions if the paths in the dimensions list match all potential dimension fields.
135137
// This is not the case e.g. if a dynamic template matches by match_mapping_type instead of path_match
136138
additionalSettings.putList(INDEX_DIMENSIONS.getKey(), dimensions);

modules/data-streams/src/test/java/org/elasticsearch/datastreams/DataStreamIndexSettingsProviderTests.java

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ public class DataStreamIndexSettingsProviderTests extends ESTestCase {
4949
private static final TimeValue DEFAULT_LOOK_AHEAD_TIME = TimeValue.timeValueMinutes(30); // default
5050

5151
DataStreamIndexSettingsProvider provider;
52-
private boolean indexDimensionsTsidOptimizationEnabled;
52+
private boolean indexDimensionsTsidStrategyEnabledSetting;
53+
private boolean expectedIndexDimensionsTsidOptimizationEnabled;
5354
private IndexVersion indexVersion;
5455

5556
@Before
@@ -60,7 +61,9 @@ public void setup() {
6061
indexVersion = randomBoolean()
6162
? IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.TSID_CREATED_DURING_ROUTING)
6263
: IndexVersionUtils.randomVersionBetween(random(), IndexVersions.TSID_CREATED_DURING_ROUTING, IndexVersion.current());
63-
indexDimensionsTsidOptimizationEnabled = indexVersion.onOrAfter(IndexVersions.TSID_CREATED_DURING_ROUTING);
64+
indexDimensionsTsidStrategyEnabledSetting = usually();
65+
expectedIndexDimensionsTsidOptimizationEnabled = indexDimensionsTsidStrategyEnabledSetting
66+
&& indexVersion.onOrAfter(IndexVersions.TSID_CREATED_DURING_ROUTING);
6467
}
6568

6669
public void testGetAdditionalIndexSettings() throws Exception {
@@ -114,12 +117,15 @@ public void testGetAdditionalIndexSettings() throws Exception {
114117
Settings result = additionalSettings.build();
115118
// The index.time_series.end_time setting requires index.mode to be set to time_series adding it here so that we read this setting:
116119
// (in production the index.mode setting is usually provided in an index or component template)
117-
result = builder().put(result).put("index.mode", "time_series").build();
118-
assertThat(result.size(), equalTo(4));
120+
result = builder().put(result)
121+
.put("index.mode", "time_series")
122+
.put("index.dimensions_tsid_strategy_enabled", indexDimensionsTsidStrategyEnabledSetting)
123+
.build();
124+
assertThat(result.size(), equalTo(5));
119125
assertThat(IndexSettings.MODE.get(result), equalTo(IndexMode.TIME_SERIES));
120126
assertThat(IndexSettings.TIME_SERIES_START_TIME.get(result), equalTo(now.minusMillis(DEFAULT_LOOK_BACK_TIME.getMillis())));
121127
assertThat(IndexSettings.TIME_SERIES_END_TIME.get(result), equalTo(now.plusMillis(DEFAULT_LOOK_AHEAD_TIME.getMillis())));
122-
if (indexDimensionsTsidOptimizationEnabled) {
128+
if (expectedIndexDimensionsTsidOptimizationEnabled) {
123129
assertThat(IndexMetadata.INDEX_DIMENSIONS.get(result), containsInAnyOrder("field3", "field4", "field5", "field6"));
124130
assertThat(IndexMetadata.INDEX_ROUTING_PATH.get(result), empty());
125131
} else {
@@ -243,12 +249,15 @@ public void testGetAdditionalIndexSettingsMappingsMerging() throws Exception {
243249
Settings result = additionalSettings.build();
244250
// The index.time_series.end_time setting requires index.mode to be set to time_series adding it here so that we read this setting:
245251
// (in production the index.mode setting is usually provided in an index or component template)
246-
result = builder().put(result).put("index.mode", "time_series").build();
247-
assertThat(result.size(), equalTo(4));
252+
result = builder().put(result)
253+
.put("index.mode", "time_series")
254+
.put("index.dimensions_tsid_strategy_enabled", indexDimensionsTsidStrategyEnabledSetting)
255+
.build();
256+
assertThat(result.size(), equalTo(5));
248257
assertThat(IndexSettings.MODE.get(result), equalTo(IndexMode.TIME_SERIES));
249258
assertThat(IndexSettings.TIME_SERIES_START_TIME.get(result), equalTo(now.minusMillis(DEFAULT_LOOK_BACK_TIME.getMillis())));
250259
assertThat(IndexSettings.TIME_SERIES_END_TIME.get(result), equalTo(now.plusMillis(DEFAULT_LOOK_AHEAD_TIME.getMillis())));
251-
if (indexDimensionsTsidOptimizationEnabled) {
260+
if (expectedIndexDimensionsTsidOptimizationEnabled) {
252261
assertThat(IndexMetadata.INDEX_DIMENSIONS.get(result), containsInAnyOrder("field1", "field3"));
253262
assertThat(IndexMetadata.INDEX_ROUTING_PATH.get(result), empty());
254263
} else {
@@ -719,7 +728,7 @@ public void testGenerateNonDimensionDynamicTemplate() throws Exception {
719728
assertThat(IndexSettings.MODE.get(result), equalTo(IndexMode.TIME_SERIES));
720729
assertThat(IndexSettings.TIME_SERIES_START_TIME.get(result), equalTo(now.minusMillis(DEFAULT_LOOK_BACK_TIME.getMillis())));
721730
assertThat(IndexSettings.TIME_SERIES_END_TIME.get(result), equalTo(now.plusMillis(DEFAULT_LOOK_AHEAD_TIME.getMillis())));
722-
if (indexDimensionsTsidOptimizationEnabled) {
731+
if (expectedIndexDimensionsTsidOptimizationEnabled) {
723732
assertThat(IndexMetadata.INDEX_DIMENSIONS.get(result), containsInAnyOrder("host.id"));
724733
assertThat(IndexMetadata.INDEX_ROUTING_PATH.get(result), empty());
725734
} else {
@@ -807,7 +816,7 @@ public void testGenerateRoutingPathFromPassThroughObject() throws Exception {
807816
assertThat(IndexSettings.MODE.get(result), equalTo(IndexMode.TIME_SERIES));
808817
assertThat(IndexSettings.TIME_SERIES_START_TIME.get(result), equalTo(now.minusMillis(DEFAULT_LOOK_BACK_TIME.getMillis())));
809818
assertThat(IndexSettings.TIME_SERIES_END_TIME.get(result), equalTo(now.plusMillis(DEFAULT_LOOK_AHEAD_TIME.getMillis())));
810-
if (indexDimensionsTsidOptimizationEnabled) {
819+
if (expectedIndexDimensionsTsidOptimizationEnabled) {
811820
assertThat(IndexMetadata.INDEX_DIMENSIONS.get(result), containsInAnyOrder("labels.*"));
812821
assertThat(IndexMetadata.INDEX_ROUTING_PATH.get(result), empty());
813822
} else {
@@ -971,7 +980,9 @@ public void testAddDynamicTemplate() throws Exception {
971980
private Settings generateTsdbSettings(String mapping, Instant now) throws IOException {
972981
ProjectMetadata projectMetadata = emptyProject();
973982
String dataStreamName = "logs-app1";
974-
Settings settings = Settings.EMPTY;
983+
Settings settings = Settings.builder()
984+
.put("index.dimensions_tsid_strategy_enabled", indexDimensionsTsidStrategyEnabledSetting)
985+
.build();
975986

976987
Settings.Builder additionalSettings = builder();
977988
provider.provideAdditionalSettings(

server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,22 @@ public Iterator<Setting<?>> settings() {
534534
"index.dimensions",
535535
Setting.Property.IndexScope,
536536
Property.Dynamic,
537-
Property.PrivateIndex
537+
Property.PrivateIndex,
538+
Property.ServerlessPublic
539+
);
540+
541+
/**
542+
* Allows to disable the {@link #INDEX_DIMENSIONS}-based tsid creation strategy on a per-index basis.
543+
* This can help to mitigate potential issues with that strategy.
544+
* For example, when using this strategy,
545+
* it's not allowed to add a dynamic template that defines dimension fields to existing backing indices of a time series data stream.
546+
*/
547+
public static final Setting<Boolean> INDEX_DIMENSIONS_TSID_STRATEGY_ENABLED = Setting.boolSetting(
548+
"index.dimensions_tsid_strategy_enabled",
549+
true,
550+
Setting.Property.IndexScope,
551+
Property.Final,
552+
Property.ServerlessPublic
538553
);
539554

540555
/**

0 commit comments

Comments
 (0)